2
* DO NOT ALTER OR REMOVE COPYRIGHT NOTICES OR THIS HEADER.
4
* Copyright 1997-2007 Sun Microsystems, Inc. All rights reserved.
6
* The contents of this file are subject to the terms of either the GNU
7
* General Public License Version 2 only ("GPL") or the Common
8
* Development and Distribution License("CDDL") (collectively, the
9
* "License"). You may not use this file except in compliance with the
10
* License. You can obtain a copy of the License at
11
* http://www.netbeans.org/cddl-gplv2.html
12
* or nbbuild/licenses/CDDL-GPL-2-CP. See the License for the
13
* specific language governing permissions and limitations under the
14
* License. When distributing the software, include this License Header
15
* Notice in each file and include the License file at
16
* nbbuild/licenses/CDDL-GPL-2-CP. Sun designates this
17
* particular file as subject to the "Classpath" exception as provided
18
* by Sun in the GPL Version 2 section of the License file that
19
* accompanied this code. If applicable, add the following below the
20
* License Header, with the fields enclosed by brackets [] replaced by
21
* your own identifying information:
22
* "Portions Copyrighted [year] [name of copyright owner]"
26
* The Original Software is NetBeans. The Initial Developer of the Original
27
* Software is Sun Microsystems, Inc. Portions Copyright 1997-2006 Sun
28
* Microsystems, Inc. All Rights Reserved.
30
* If you wish your version of this file to be governed by only the CDDL
31
* or only the GPL Version 2, indicate your decision by adding
32
* "[Contributor] elects to include this software in this distribution
33
* under the [CDDL or GPL Version 2] license." If you do not indicate a
34
* single choice of license, a recipient has the option to distribute
35
* your version of this file under either the CDDL, the GPL Version 2 or
36
* to extend the choice of license to its licensees as provided above.
37
* However, if you add GPL Version 2 code and therefore, elected the GPL
38
* Version 2 license, then the option applies only if the new code is
39
* made subject to such option by the copyright holder.
42
package org.netbeans.modules.java.j2seproject.ui.customizer;
44
import java.awt.Component;
45
import java.beans.PropertyChangeEvent;
46
import java.beans.PropertyChangeListener;
47
import java.text.MessageFormat;
48
import java.util.ArrayList;
49
import java.util.List;
51
import java.util.TreeSet;
52
import java.util.logging.Logger;
53
import javax.swing.AbstractListModel;
54
import javax.swing.ComboBoxModel;
55
import javax.swing.JButton;
56
import javax.swing.JList;
57
import javax.swing.ListCellRenderer;
58
import javax.swing.event.ListDataEvent;
59
import javax.swing.event.ListDataListener;
60
import org.netbeans.api.java.platform.JavaPlatform;
61
import org.netbeans.api.java.platform.JavaPlatformManager;
62
import org.netbeans.api.java.platform.Specification;
63
import org.netbeans.modules.java.j2seproject.J2SEProjectType;
64
import org.netbeans.modules.java.j2seproject.UpdateHelper;
65
import org.netbeans.spi.project.support.ant.EditableProperties;
66
import org.openide.DialogDisplayer;
67
import org.openide.NotifyDescriptor;
68
import org.openide.awt.HtmlRenderer;
69
import org.openide.modules.SpecificationVersion;
70
import org.openide.util.NbBundle;
71
import org.openide.util.WeakListeners;
72
import org.w3c.dom.Element;
73
import org.w3c.dom.NodeList;
76
* Support class for {@link JavaPlatform} manipulation in j2seproject customizer.
79
public class PlatformUiSupport {
81
private static final SpecificationVersion JDK_5 = new SpecificationVersion ("1.5"); //NOI18N
82
private static final SpecificationVersion JDK_6 = new SpecificationVersion ("1.6"); //NOI18N
83
private static final Logger LOGGER = Logger.getLogger(PlatformUiSupport.class.getName());
85
private PlatformUiSupport() {
89
* Creates {@link ComboBoxModel} of J2SE platforms.
90
* The model listens on the {@link JavaPlatformManager} and update its
91
* state according to changes
92
* @param activePlatform the active project's platform
93
* @return {@link ComboBoxModel}
95
public static ComboBoxModel createPlatformComboBoxModel (String activePlatform) {
96
return new PlatformComboBoxModel (activePlatform);
101
* Creates a {@link ListCellRenderer} for rendering items of the {@link ComboBoxModel}
102
* created by the {@link PlatformUiSupport#createPlatformComboBoxModel} method.
103
* @return {@link ListCellRenderer}
105
public static ListCellRenderer createPlatformListCellRenderer () {
106
return new PlatformListCellRenderer ();
110
* Stores active platform, javac.source and javac.target into the project's metadata
111
* @param props project's shared properties
112
* @param helper to read/update project.xml
113
* @param platformKey the PatformKey got from the platform model
114
* @param sourceLevel source level
116
public static void storePlatform (EditableProperties props, UpdateHelper helper, Object platformKey, Object sourceLevelKey) {
117
assert platformKey instanceof PlatformKey;
118
PlatformKey pk = (PlatformKey) platformKey;
119
JavaPlatform platform = getPlatform(pk);
120
//null means active broken (unresolved) platform, no need to do anything
121
if (platform != null) {
122
SpecificationVersion jdk13 = new SpecificationVersion ("1.3"); //NOI18N
123
String platformAntName = (String) platform.getProperties().get("platform.ant.name"); //NOI18N
124
assert platformAntName != null;
125
props.put(J2SEProjectProperties.JAVA_PLATFORM, platformAntName);
126
Element root = helper.getPrimaryConfigurationData(true);
127
boolean defaultPlatform = pk.isDefaultPlatform();
128
boolean changed = false;
129
NodeList explicitPlatformNodes = root.getElementsByTagNameNS (J2SEProjectType.PROJECT_CONFIGURATION_NAMESPACE,"explicit-platform"); //NOI18N
130
if (defaultPlatform) {
131
if (explicitPlatformNodes.getLength()==1) {
132
root.removeChild(explicitPlatformNodes.item(0));
137
Element explicitPlatform;
138
switch (explicitPlatformNodes.getLength()) {
140
explicitPlatform = root.getOwnerDocument().createElementNS(J2SEProjectType.PROJECT_CONFIGURATION_NAMESPACE, "explicit-platform"); //NOI18N
141
NodeList sourceRootNodes = root.getElementsByTagNameNS (J2SEProjectType.PROJECT_CONFIGURATION_NAMESPACE,"source-roots"); //NOI18N
142
assert sourceRootNodes.getLength() == 1 : "Broken project.xml file"; //NOI18N
143
root.insertBefore(explicitPlatform, sourceRootNodes.item(0));
147
explicitPlatform = (Element)explicitPlatformNodes.item(0);
150
throw new AssertionError("Broken project.xml file"); //NOI18N
152
String explicitSourceAttrValue = explicitPlatform.getAttribute("explicit-source-supported"); //NOI18N
153
if (jdk13.compareTo(platform.getSpecification().getVersion())>=0 &&
154
!"false".equals(explicitSourceAttrValue)) { //NOI18N
155
explicitPlatform.setAttribute("explicit-source-supported","false"); //NOI18N
158
else if (jdk13.compareTo(platform.getSpecification().getVersion())<0 &&
159
!"true".equals(explicitSourceAttrValue)) { //NOI18N
160
explicitPlatform.setAttribute("explicit-source-supported","true"); //NOI18N
165
SpecificationVersion sourceLevel;
166
if (sourceLevelKey == null) {
167
sourceLevel = platform.getSpecification().getVersion();
170
assert sourceLevelKey instanceof SourceLevelKey;
171
sourceLevel = ((SourceLevelKey)sourceLevelKey).getSourceLevel();
173
String javacSource = sourceLevel.toString();
174
String javacTarget = javacSource;
177
// Customizer value | -source | -targer
183
// JDK 7 1.7 1.7 - should bring a new language features
184
if (jdk13.compareTo(sourceLevel)>=0) {
185
javacTarget = "1.1"; //NOI18N
187
else if (JDK_6.equals(sourceLevel)) {
188
javacSource = JDK_5.toString();
191
if (!javacSource.equals(props.getProperty(J2SEProjectProperties.JAVAC_SOURCE))) {
192
props.setProperty (J2SEProjectProperties.JAVAC_SOURCE, javacSource);
194
if (!javacTarget.equals(props.getProperty(J2SEProjectProperties.JAVAC_TARGET))) {
195
props.setProperty (J2SEProjectProperties.JAVAC_TARGET, javacTarget);
199
helper.putPrimaryConfigurationData(root, true);
206
* Returns a {@link JavaPlatform} for an item obtained from the ComboBoxModel created by
207
* the {@link PlatformUiSupport#createComboBoxModel} method
208
* @param platformKey an item obtained from ComboBoxModel created by {@link PlatformUiSupport#createComboBoxModel}
209
* @return JavaPlatform or null in case when platform is broken
210
* @exception {@link IllegalArgumentException} is thrown in case when parameter in not an object created by
211
* platform combobox model.
213
public static JavaPlatform getPlatform (Object platformKey) {
214
if (platformKey instanceof PlatformKey) {
215
return getPlatform ((PlatformKey)platformKey);
218
throw new IllegalArgumentException ();
223
* Creates {@link ComboBoxModel} of source levels for active platform.
224
* The model listens on the platform's {@link ComboBoxModel} and update its
225
* state according to changes
226
* @param platformComboBoxModel the platform's model used for listenning
227
* @param initialValue initial source level value
228
* @return {@link ComboBoxModel} of {@link SpecificationVersion}
230
public static ComboBoxModel createSourceLevelComboBoxModel (ComboBoxModel platformComboBoxModel, String initialSourceLevel, String initinalTargetLevel) {
231
return new SourceLevelComboBoxModel (platformComboBoxModel, initialSourceLevel, initinalTargetLevel);
235
public static ListCellRenderer createSourceLevelListCellRenderer () {
236
return new SourceLevelListCellRenderer ();
240
private static JavaPlatform getPlatform (PlatformKey platformKey) {
241
return platformKey.platform;
246
* This class represents a JavaPlatform in the {@link ListModel}
247
* created by the {@link PlatformUiSupport#createPlatformComboBoxModel}
250
private static class PlatformKey implements Comparable {
253
private JavaPlatform platform;
256
* Creates a PlatformKey for a broken platform
257
* @param name the ant name of the broken platform
259
public PlatformKey (String name) {
265
* Creates a PlatformKey for a platform
266
* @param platform the {@link JavaPlatform}
268
public PlatformKey (JavaPlatform platform) {
269
assert platform != null;
270
this.platform = platform;
273
public int compareTo(Object o) {
274
return this.getDisplayName().compareTo(((PlatformKey)o).getDisplayName());
277
public boolean equals (Object other) {
278
if (other instanceof PlatformKey) {
279
PlatformKey otherKey = (PlatformKey)other;
280
return (this.platform == null ? otherKey.platform == null : this.platform.equals(otherKey.platform)) &&
281
otherKey.getDisplayName().equals (this.getDisplayName());
288
public int hashCode () {
289
return getDisplayName ().hashCode ();
292
public String toString () {
293
return getDisplayName ();
296
public synchronized String getDisplayName () {
297
if (this.name == null) {
298
this.name = this.platform.getDisplayName();
303
public boolean isDefaultPlatform () {
304
if (this.platform == null) {
307
return this.platform.equals(JavaPlatformManager.getDefault().getDefaultPlatform());
310
public boolean isBroken () {
311
return this.platform == null;
316
private static final class SourceLevelKey implements Comparable {
318
final SpecificationVersion sourceLevel;
319
final boolean broken;
321
public SourceLevelKey (final SpecificationVersion sourceLevel) {
322
this (sourceLevel, false);
325
public SourceLevelKey (final SpecificationVersion sourceLevel, final boolean broken) {
326
assert sourceLevel != null : "Source level cannot be null"; //NOI18N
327
this.sourceLevel = sourceLevel;
328
this.broken = broken;
331
public SpecificationVersion getSourceLevel () {
332
return this.sourceLevel;
335
public boolean isBroken () {
339
public int compareTo (final Object other) {
340
assert other instanceof SourceLevelKey : "Illegal argument of SourceLevelKey.compareTo()"; //NOI18N
341
SourceLevelKey otherKey = (SourceLevelKey) other;
342
return this.sourceLevel.compareTo(otherKey.sourceLevel);
345
public @Override boolean equals (final Object other) {
346
return (other instanceof SourceLevelKey) &&
347
((SourceLevelKey)other).sourceLevel.equals(this.sourceLevel);
350
public @Override int hashCode () {
351
return this.sourceLevel.hashCode();
354
public @Override String toString () {
355
StringBuffer buffer = new StringBuffer ();
357
buffer.append("Broken: "); //NOI18N
359
buffer.append(this.sourceLevel.toString());
360
return buffer.toString();
363
public String getDisplayName () {
364
String _tmp = sourceLevel.toString();
365
if (JDK_5.compareTo(sourceLevel)<=0) {
366
_tmp = _tmp.replaceFirst("^1\\.([5-9]|\\d\\d+)$", "$1"); //NOI18N
368
return NbBundle.getMessage(PlatformUiSupport.class, "LBL_JDK",_tmp);
373
private static class PlatformComboBoxModel extends AbstractListModel implements ComboBoxModel, PropertyChangeListener {
375
private JavaPlatformManager pm;
376
private PlatformKey[] platformNamesCache;
377
private String initialPlatform;
378
private PlatformKey selectedPlatform;
380
public PlatformComboBoxModel (String initialPlatform) {
381
this.pm = JavaPlatformManager.getDefault();
382
this.pm.addPropertyChangeListener(WeakListeners.propertyChange(this, this.pm));
383
this.initialPlatform = initialPlatform;
386
public int getSize () {
387
PlatformKey[] platformNames = getPlatformNames ();
388
return platformNames.length;
391
public Object getElementAt (int index) {
392
PlatformKey[] platformNames = getPlatformNames ();
393
assert index >=0 && index< platformNames.length;
394
return platformNames[index];
397
public Object getSelectedItem () {
398
this.getPlatformNames(); //Force setting of selectedPlatform if it is not alredy done
399
return this.selectedPlatform;
402
public void setSelectedItem (Object obj) {
403
this.selectedPlatform = (PlatformKey) obj;
404
this.fireContentsChanged(this, -1, -1);
407
public void propertyChange (PropertyChangeEvent event) {
408
if (JavaPlatformManager.PROP_INSTALLED_PLATFORMS.equals(event.getPropertyName())) {
409
synchronized (this) {
410
this.platformNamesCache = null;
412
this.fireContentsChanged(this, -1, -1);
416
private synchronized PlatformKey[] getPlatformNames () {
417
if (this.platformNamesCache == null) {
418
JavaPlatform[] platforms = pm.getPlatforms (null, new Specification("j2se",null)); //NOI18N
419
JavaPlatform defaultPlatform = pm.getDefaultPlatform ();
420
Set/*<PlatformKey>*/ orderedNames = new TreeSet ();
421
boolean activeFound = false;
422
for (int i=0; i< platforms.length; i++) {
423
if (platforms[i].getInstallFolders().size()>0) {
424
PlatformKey pk = new PlatformKey(platforms[i]);
425
orderedNames.add (pk);
426
if (!activeFound && initialPlatform != null) {
427
String antName = (String) platforms[i].getProperties().get("platform.ant.name"); //NOI18N
428
if (initialPlatform.equals(antName)) {
429
if (this.selectedPlatform == null) {
430
this.selectedPlatform = pk;
431
initialPlatform = null;
439
if (initialPlatform == null) {
440
if (this.selectedPlatform == null || !orderedNames.contains(this.selectedPlatform)) {
441
this.selectedPlatform = new PlatformKey (JavaPlatformManager.getDefault().getDefaultPlatform());
445
PlatformKey pk = new PlatformKey (this.initialPlatform);
446
orderedNames.add (pk);
447
if (this.selectedPlatform == null) {
448
this.selectedPlatform = pk;
452
this.platformNamesCache = (PlatformKey[]) orderedNames.toArray(new PlatformKey[orderedNames.size()]);
454
return this.platformNamesCache;
459
private static class PlatformListCellRenderer implements ListCellRenderer {
461
private ListCellRenderer delegate;
463
public PlatformListCellRenderer () {
464
this.delegate = HtmlRenderer.createRenderer ();
467
public Component getListCellRendererComponent(JList list, Object value, int index, boolean isSelected, boolean cellHasFocus) {
473
assert value instanceof PlatformKey : "Wrong model"; //NOI18N
474
PlatformKey key = (PlatformKey) value;
475
if (key.isBroken()) {
476
name = "<html><font color=\"#A40000\">" + //NOI18N
477
NbBundle.getMessage (PlatformUiSupport.class,"TXT_BrokenPlatformFmt", key.getDisplayName());
480
name = key.getDisplayName();
483
return this.delegate.getListCellRendererComponent(list, name, index, isSelected, cellHasFocus);
487
private static class SourceLevelComboBoxModel extends AbstractListModel implements ComboBoxModel, ListDataListener {
489
private static final String VERSION_PREFIX = "1."; //The version prefix
490
private static final int INITIAL_VERSION_MINOR = 2; //1.2
492
private SpecificationVersion selectedSourceLevel;
493
private SpecificationVersion originalSourceLevel;
494
private SourceLevelKey[] sourceLevelCache;
495
private final ComboBoxModel platformComboBoxModel;
496
private PlatformKey activePlatform;
498
public SourceLevelComboBoxModel (ComboBoxModel platformComboBoxModel, String initialSourceLevel, String initialTargetLevel) {
499
this.platformComboBoxModel = platformComboBoxModel;
500
this.activePlatform = (PlatformKey) this.platformComboBoxModel.getSelectedItem();
501
this.platformComboBoxModel.addListDataListener (this);
502
if (initialSourceLevel != null && initialSourceLevel.length() > 0) {
504
this.originalSourceLevel = new SpecificationVersion (initialSourceLevel);
505
} catch (NumberFormatException nfe) {
506
//If the javac.source has invalid value, do not preselect and log it
507
LOGGER.warning("Invalid javac.source: "+initialSourceLevel); //NO18N
510
if (initialTargetLevel != null && initialTargetLevel.length() > 0) {
512
SpecificationVersion originalTargetLevel = new SpecificationVersion (initialTargetLevel);
513
if (this.originalSourceLevel == null || this.originalSourceLevel.compareTo(originalTargetLevel)<0) {
514
this.originalSourceLevel = originalTargetLevel;
516
} catch (NumberFormatException nfe) {
517
//If the javac.target has invalid value, do not preselect and log it
518
LOGGER.warning("Invalid javac.target: "+initialTargetLevel); //NOI18N
521
this.selectedSourceLevel = this.originalSourceLevel;
524
public int getSize () {
525
SourceLevelKey[] sLevels = getSourceLevels ();
526
return sLevels.length;
529
public Object getElementAt (int index) {
530
SourceLevelKey[] sLevels = getSourceLevels ();
531
assert index >=0 && index< sLevels.length;
532
return sLevels[index];
535
public Object getSelectedItem () {
536
SourceLevelKey[] keys = getSourceLevels ();
537
for (int i=0; i<keys.length; i++) {
538
if (keys[i].getSourceLevel().equals(this.selectedSourceLevel)) {
545
public void setSelectedItem (Object obj) {
546
this.selectedSourceLevel = (obj == null ? null : ((SourceLevelKey)obj).getSourceLevel());
547
this.fireContentsChanged(this, -1, -1);
550
public void intervalAdded(ListDataEvent e) {
553
public void intervalRemoved(ListDataEvent e) {
556
public void contentsChanged(ListDataEvent e) {
557
PlatformKey selectedPlatform = (PlatformKey) this.platformComboBoxModel.getSelectedItem();
558
JavaPlatform platform = getPlatform(selectedPlatform);
559
if (platform != null) {
560
SpecificationVersion version = platform.getSpecification().getVersion();
561
if (this.selectedSourceLevel != null && this.selectedSourceLevel.compareTo(version)>0 &&
562
!shouldChangePlatform (selectedSourceLevel, version)) {
564
this.platformComboBoxModel.setSelectedItem(this.activePlatform);
568
this.originalSourceLevel = null;
571
this.activePlatform = selectedPlatform;
575
private void resetCache () {
576
synchronized (this) {
577
this.sourceLevelCache = null;
579
this.fireContentsChanged(this, -1, -1);
582
private SourceLevelKey[] getSourceLevels () {
583
if (this.sourceLevelCache == null) {
584
PlatformKey selectedPlatform = (PlatformKey) this.platformComboBoxModel.getSelectedItem();
585
JavaPlatform platform = getPlatform(selectedPlatform);
586
List/*<SpecificationVersion>*/ sLevels = new ArrayList ();
587
//If platform == null broken platform, the source level range is unknown
588
//The source level combo box should be empty and disabled
589
boolean selSourceLevelValid = false;
590
if (platform != null) {
591
SpecificationVersion version = platform.getSpecification().getVersion();
592
int index = INITIAL_VERSION_MINOR;
593
SpecificationVersion template = new SpecificationVersion (VERSION_PREFIX + Integer.toString (index++));
594
boolean origSourceLevelValid = false;
596
while (template.compareTo(version)<=0) {
597
if (template.equals(this.originalSourceLevel)) {
598
origSourceLevelValid = true;
600
if (template.equals(this.selectedSourceLevel)) {
601
selSourceLevelValid = true;
603
sLevels.add (new SourceLevelKey (template));
604
template = new SpecificationVersion (VERSION_PREFIX + Integer.toString (index++));
606
if (this.originalSourceLevel != null && !origSourceLevelValid) {
607
if (originalSourceLevel.equals(this.selectedSourceLevel)) {
608
selSourceLevelValid = true;
610
sLevels.add (new SourceLevelKey(this.originalSourceLevel,true));
613
this.sourceLevelCache = (SourceLevelKey[]) sLevels.toArray(new SourceLevelKey[sLevels.size()]);
614
if (!selSourceLevelValid) {
615
this.selectedSourceLevel = this.sourceLevelCache.length == 0 ?
616
null : this.sourceLevelCache[this.sourceLevelCache.length-1].getSourceLevel();
619
return this.sourceLevelCache;
622
private static boolean shouldChangePlatform (SpecificationVersion selectedSourceLevel, SpecificationVersion platformSourceLevel) {
623
JButton changeOption = new JButton (NbBundle.getMessage(PlatformUiSupport.class, "CTL_ChangePlatform"));
624
changeOption.getAccessibleContext().setAccessibleDescription(NbBundle.getMessage(PlatformUiSupport.class, "AD_ChangePlatform"));
625
String message = MessageFormat.format (NbBundle.getMessage(PlatformUiSupport.class,"TXT_ChangePlatform"),new Object[] {
626
selectedSourceLevel.toString(),
627
platformSourceLevel.toString(),
629
return DialogDisplayer.getDefault().notify(
630
new NotifyDescriptor (message,
631
NbBundle.getMessage(PlatformUiSupport.class,"TXT_ChangePlatformTitle"),
632
NotifyDescriptor.DEFAULT_OPTION,
633
NotifyDescriptor.WARNING_MESSAGE,
636
NotifyDescriptor.CANCEL_OPTION
638
changeOption)) == changeOption;
642
private static class SourceLevelListCellRenderer implements ListCellRenderer {
644
ListCellRenderer delegate;
646
public SourceLevelListCellRenderer () {
647
this.delegate = HtmlRenderer.createRenderer();
650
public Component getListCellRendererComponent(JList list, Object value, int index, boolean isSelected, boolean cellHasFocus) {
653
message = ""; //NOI18N
656
assert value instanceof SourceLevelKey;
657
SourceLevelKey key = (SourceLevelKey) value;
658
if (key.isBroken()) {
659
message = "<html><font color=\"#A40000\">" + //NOI18N
660
NbBundle.getMessage(PlatformUiSupport.class,"TXT_InvalidSourceLevel",key.getDisplayName());
663
message = key.getDisplayName();
666
return this.delegate.getListCellRendererComponent(list, message, index, isSelected, cellHasFocus);