001: /*
002: * DO NOT ALTER OR REMOVE COPYRIGHT NOTICES OR THIS HEADER.
003: *
004: * Copyright 1997-2007 Sun Microsystems, Inc. All rights reserved.
005: *
006: * The contents of this file are subject to the terms of either the GNU
007: * General Public License Version 2 only ("GPL") or the Common
008: * Development and Distribution License("CDDL") (collectively, the
009: * "License"). You may not use this file except in compliance with the
010: * License. You can obtain a copy of the License at
011: * http://www.netbeans.org/cddl-gplv2.html
012: * or nbbuild/licenses/CDDL-GPL-2-CP. See the License for the
013: * specific language governing permissions and limitations under the
014: * License. When distributing the software, include this License Header
015: * Notice in each file and include the License file at
016: * nbbuild/licenses/CDDL-GPL-2-CP. Sun designates this
017: * particular file as subject to the "Classpath" exception as provided
018: * by Sun in the GPL Version 2 section of the License file that
019: * accompanied this code. If applicable, add the following below the
020: * License Header, with the fields enclosed by brackets [] replaced by
021: * your own identifying information:
022: * "Portions Copyrighted [year] [name of copyright owner]"
023: *
024: * Contributor(s):
025: *
026: * The Original Software is NetBeans. The Initial Developer of the Original
027: * Software is Sun Microsystems, Inc. Portions Copyright 1997-2006 Sun
028: * Microsystems, Inc. All Rights Reserved.
029: *
030: * If you wish your version of this file to be governed by only the CDDL
031: * or only the GPL Version 2, indicate your decision by adding
032: * "[Contributor] elects to include this software in this distribution
033: * under the [CDDL or GPL Version 2] license." If you do not indicate a
034: * single choice of license, a recipient has the option to distribute
035: * your version of this file under either the CDDL, the GPL Version 2 or
036: * to extend the choice of license to its licensees as provided above.
037: * However, if you add GPL Version 2 code and therefore, elected the GPL
038: * Version 2 license, then the option applies only if the new code is
039: * made subject to such option by the copyright holder.
040: */
041:
042: package org.netbeans.modules.mobility.project;
043:
044: import java.beans.PropertyChangeListener;
045: import java.beans.PropertyChangeSupport;
046: import java.io.IOException;
047: import java.util.Arrays;
048: import java.util.Collection;
049: import java.util.Collections;
050: import java.util.Comparator;
051: import java.util.HashMap;
052: import java.util.Map;
053: import java.util.Set;
054: import java.util.TreeMap;
055: import java.util.TreeSet;
056: import javax.swing.SwingUtilities;
057: import org.netbeans.api.project.ProjectManager;
058: import org.netbeans.modules.mobility.project.ui.J2MECustomizerProvider;
059: import org.netbeans.spi.project.ProjectConfiguration;
060: import org.netbeans.spi.project.ProjectConfigurationProvider;
061: import org.netbeans.mobility.antext.preprocessor.CommentingPreProcessor;
062: import org.netbeans.modules.mobility.project.ui.customizer.J2MEProjectProperties;
063: import org.netbeans.spi.project.support.ant.AntProjectEvent;
064: import org.netbeans.spi.project.support.ant.AntProjectHelper;
065: import org.netbeans.spi.project.support.ant.AntProjectListener;
066: import org.netbeans.spi.project.support.ant.EditableProperties;
067: import org.openide.ErrorManager;
068: import org.openide.util.Mutex;
069: import org.openide.util.MutexException;
070:
071: /**
072: * Helper class implementing ProjectConfigurationProvider for Ant based projects.
073: * @author Adam Sotona, David Kaspar
074: */
075: public final class ProjectConfigurationsHelper implements
076: ProjectConfigurationProvider<ProjectConfiguration>,
077: AntProjectListener {
078:
079: /**
080: * name of the Ant property storing the active configuration
081: */
082: public static final String PROJ_PROP_CONFIGURATION_ACTIVE = "config.active"; // NOI18N
083:
084: /**
085: * Default configuration name.
086: */
087: static public final String DEFAULT_CONFIGURATION_NAME = "DefaultConfiguration"; // NOI18N
088:
089: /**
090: * Default configuration name.
091: */
092: public static final String PROJECT_PROPERTIES = "ProjectProperties"; // NOI18N
093:
094: protected final AntProjectHelper h;
095: private TreeMap<String, ProjectConfiguration> configurations;
096: private PropertyChangeSupport psp;
097: private ProjectConfiguration activeConfiguration;
098: //private ProjectConfiguration[] configurations;
099: private ProjectConfiguration defaultConfiguration;
100: private J2MEProject p;
101: private boolean preprocessorOn, invalid = true;
102:
103: /**
104: * Creates new instance of the helper.
105: * @param helper AntProjectHelper for accessing Ant project properties.
106: * @param emp ExtensibleMetadataProvider to access project XML.
107: */
108: public ProjectConfigurationsHelper(AntProjectHelper helper,
109: J2MEProject p) {
110: this .h = helper;
111: this .p = p;
112: }
113:
114: public boolean isPreprocessorOn() {
115: if (invalid) {
116: String prop = h.getStandardPropertyEvaluator().getProperty(
117: DefaultPropertiesDescriptor.USE_PREPROCESSOR);
118: preprocessorOn = prop == null || Boolean.parseBoolean(prop);
119: invalid = false;
120: }
121: return preprocessorOn;
122: }
123:
124: public synchronized ProjectConfiguration getDefaultConfiguration() {
125: if (defaultConfiguration == null) {
126: defaultConfiguration = createConfiguration(DEFAULT_CONFIGURATION_NAME); //NOI18N
127: }
128: return defaultConfiguration;
129: }
130:
131: /**
132: * Append new configuration to the project.
133: * @param configName String new configuration name
134: * @return boolean success
135: */
136: public final boolean addConfiguration(final String configName) {
137: if (configName == null
138: || configName.equals(getDefaultConfiguration()
139: .getDisplayName()))
140: return false;
141: boolean ret = (ProjectManager.mutex()
142: .writeAccess(new Mutex.Action<Boolean>() {
143: public Boolean run() {
144: EditableProperties props = h
145: .getProperties(AntProjectHelper.PROJECT_PROPERTIES_PATH);
146: String cfgs = props
147: .getProperty(DefaultPropertiesDescriptor.ALL_CONFIGURATIONS);
148: if (cfgs == null)
149: cfgs = ""; //NOI18N
150: boolean add = true;
151: StringBuffer sb = new StringBuffer(" ");
152: for (String s : cfgs.split(",")) { //NOI18N
153: if (s.trim().length() > 0) {
154: int i = s.compareTo(configName);
155: if (i == 0)
156: return Boolean.FALSE;
157: else if (add && i > 0) {
158: add = false;
159: sb.append(',').append(configName);
160: }
161: sb.append(',').append(s);
162: }
163: }
164: if (add) {
165: sb.append(',').append(configName);
166: }
167: props
168: .put(
169: DefaultPropertiesDescriptor.ALL_CONFIGURATIONS,
170: sb.toString());
171: h
172: .putProperties(
173: AntProjectHelper.PROJECT_PROPERTIES_PATH,
174: props);
175: return Boolean.TRUE;
176: }
177: })).booleanValue();
178: try {
179: ProjectManager.getDefault().saveProject(p);
180: return ret;
181: } catch (IOException ioe) {
182: ErrorManager.getDefault().notify(ioe);
183: }
184: return false;
185: }
186:
187: public Map<String, String> getAbilitiesFor(
188: final ProjectConfiguration cfg) {
189: final String abilities = J2MEProjectUtils.evaluateProperty(h,
190: DefaultPropertiesDescriptor.ABILITIES, cfg
191: .getDisplayName());
192: final Map<String, String> m = abilities == null ? new HashMap<String, String>()
193: : CommentingPreProcessor.decodeAbilitiesMap(abilities);
194: m.put("DebugLevel", J2MEProjectUtils.evaluateProperty(h,
195: DefaultPropertiesDescriptor.DEBUG_LEVEL, cfg
196: .getDisplayName())); //NOI18N
197: return m;
198: }
199:
200: public Map<String, String> getActiveAbilities() {
201: return getAbilitiesFor(getActiveConfiguration());
202: }
203:
204: public Set<String> getAllIdentifiers(
205: final boolean includeConfigNames) {
206: final TreeSet<String> s = new TreeSet<String>();
207: final ProjectConfiguration devConfigs[] = getConfigurations()
208: .toArray(new ProjectConfiguration[0]);
209: for (int i = 0; i < devConfigs.length; i++) {
210: if (includeConfigNames)
211: s.add(devConfigs[i].getDisplayName());
212: final String propName = getDefaultConfiguration().equals(
213: devConfigs[i]) ? DefaultPropertiesDescriptor.ABILITIES
214: : J2MEProjectProperties.CONFIG_PREFIX
215: + devConfigs[i].getDisplayName() + '.'
216: + DefaultPropertiesDescriptor.ABILITIES;
217: final String prop = h.getStandardPropertyEvaluator()
218: .getProperty(propName);
219: if (prop != null)
220: s.addAll(CommentingPreProcessor
221: .decodeAbilitiesMap(prop).keySet());
222: }
223: return s;
224: }
225:
226: /**
227: * Remove configuration from project.
228: * @param configName String configuration name
229: * @return boolean success
230: */
231: public final boolean removeConfiguration(
232: final ProjectConfiguration config) {
233: if (config == null || config.equals(getDefaultConfiguration()))
234: return false;
235: boolean ret = (ProjectManager.mutex()
236: .writeAccess(new Mutex.Action<Boolean>() {
237: public Boolean run() {
238: EditableProperties props = h
239: .getProperties(AntProjectHelper.PROJECT_PROPERTIES_PATH);
240: String cfgs = props
241: .getProperty(DefaultPropertiesDescriptor.ALL_CONFIGURATIONS);
242: if (cfgs == null)
243: return Boolean.FALSE;
244: boolean succ = false;
245: StringBuffer sb = new StringBuffer();
246: for (String s : cfgs.split(",")) { //NOI18N
247: if (s.equals(config.getDisplayName())) {
248: succ = true;
249: } else {
250: if (sb.length() > 0)
251: sb.append(',');
252: sb.append(s);
253: }
254: }
255: if (succ) {
256: final String projProp = "configs."
257: + config.getDisplayName(); // NOI18N
258: for (String key : props.keySet().toArray(
259: new String[0])) {
260: if (key.startsWith(projProp))
261: props.remove(key);
262: }
263: props
264: .put(
265: DefaultPropertiesDescriptor.ALL_CONFIGURATIONS,
266: sb.toString());
267: h
268: .putProperties(
269: AntProjectHelper.PROJECT_PROPERTIES_PATH,
270: props);
271: }
272: return Boolean.valueOf(succ);
273: }
274: })).booleanValue();
275: try {
276: ProjectManager.getDefault().saveProject(p);
277: return ret;
278: } catch (IOException ioe) {
279: ErrorManager.getDefault().notify(ioe);
280: }
281: return false;
282: }
283:
284: /**
285: * Implements ProjectConfigurationProvider.
286: * Get list of project configuration names.
287: * @return ProjectConfiguration[] list.
288: */
289: public final synchronized Collection<ProjectConfiguration> getConfigurations() {
290: return getConfigurations(configurations);
291: }
292:
293: private final synchronized Collection<ProjectConfiguration> getConfigurations(
294: final TreeMap<String, ProjectConfiguration> oldConfig) {
295: if (configurations == null) {
296: configurations = ProjectManager
297: .mutex()
298: .readAccess(
299: new Mutex.Action<TreeMap<String, ProjectConfiguration>>() {
300: public TreeMap<String, ProjectConfiguration> run() {
301: final TreeMap<String, ProjectConfiguration> newByName = new TreeMap<String, ProjectConfiguration>(
302: new Comparator<String>() {
303: public int compare(
304: String o1,
305: String o2) {
306: return DEFAULT_CONFIGURATION_NAME
307: .equals(o1) ? (DEFAULT_CONFIGURATION_NAME
308: .equals(o2) ? 0
309: : -1)
310: : (DEFAULT_CONFIGURATION_NAME
311: .equals(o2) ? 1
312: : o1
313: .compareToIgnoreCase(o2));
314: }
315: });
316: newByName.put(
317: getDefaultConfiguration()
318: .getDisplayName(),
319: getDefaultConfiguration());
320: String cfgs = h
321: .getProperties(
322: AntProjectHelper.PROJECT_PROPERTIES_PATH)
323: .getProperty(
324: DefaultPropertiesDescriptor.ALL_CONFIGURATIONS);
325: if (cfgs != null) {
326: for (String configName : cfgs
327: .split(",")) { //NOII8N
328: if (configName.length() > 0
329: && !configName
330: .equals(" ")) { //NOI18N
331: ProjectConfiguration conf = oldConfig == null ? null
332: : oldConfig
333: .get(configName);
334: newByName
335: .put(
336: configName,
337: conf == null ? createConfiguration(configName)
338: : conf);
339: }
340: }
341: }
342: return newByName;
343: }
344: });
345: }
346: return configurations == null ? null : Collections
347: .unmodifiableCollection(configurations.values());
348: }
349:
350: /**
351: * Implements ProjectConfigurationProvider.
352: * Allows listenning on configurations and active configuration.
353: * @param lst PropertyChangeListener
354: */
355: public final void addPropertyChangeListener(
356: final PropertyChangeListener lst) {
357: synchronized (this ) {
358: if (psp == null) {
359: psp = new PropertyChangeSupport(this );
360: getConfigurations();
361: getActiveConfiguration();
362: h.addAntProjectListener(this );
363: }
364: }
365: psp.addPropertyChangeListener(lst);
366: }
367:
368: /**
369: * Implements ProjectConfigurationProvider.
370: * Get currently active configuration of the project.
371: * @return String active configuration name.
372: */
373: public final synchronized ProjectConfiguration getActiveConfiguration() {
374: if (activeConfiguration == null) {
375: activeConfiguration = getDefaultConfiguration();
376: final String confName = h.getStandardPropertyEvaluator()
377: .getProperty(PROJ_PROP_CONFIGURATION_ACTIVE);
378: if (confName == null || confName.length() == 0)
379: return getDefaultConfiguration();
380: final ProjectConfiguration confs[] = getConfigurations()
381: .toArray(new ProjectConfiguration[0]);
382: for (int i = 0; i < confs.length; i++) {
383: if (confName.equals((confs[i]).getDisplayName())) {
384: activeConfiguration = confs[i];
385: return activeConfiguration;
386: }
387: }
388: }
389: return activeConfiguration;
390: }
391:
392: /**
393: * Helper method that returns ProjectConfiguration by name
394: * @param configName name of the ProjectConfiguration to retrieve
395: * @return ProjectConfiguration object that has the passed name
396: */
397: public final synchronized ProjectConfiguration getConfigurationByName(
398: String configName) {
399: return configurations.get(configName);
400: }
401:
402: /**
403: * Implements ProjectConfigurationProvider.
404: * Removes listener on configurations and active configuration.
405: * @param lst PropertyChangeListener
406: */
407: public final void removePropertyChangeListener(
408: final PropertyChangeListener lst) {
409: if (psp != null)
410: psp.removePropertyChangeListener(lst);
411: }
412:
413: protected ProjectConfiguration createConfiguration(
414: final String configName) {
415: return new ConfigurationImpl(configName);
416: }
417:
418: /**
419: * Implements ProjectConfigurationProvider.
420: * Set the active configuration name.
421: * Fire property change with PROP_CONFIGURATION_ACTIVE to all listeners.
422: * @param configuration new active ProjectConfiguration
423: */
424: public final synchronized void setActiveConfiguration(
425: ProjectConfiguration configuration)
426: throws IllegalArgumentException, IOException {
427: final ProjectConfiguration oldAC = activeConfiguration;
428: activeConfiguration = null;
429:
430: final EditableProperties ep = h
431: .getProperties(AntProjectHelper.PRIVATE_PROPERTIES_PATH);
432: ep.put(PROJ_PROP_CONFIGURATION_ACTIVE,
433: (configuration == null || configuration
434: .equals(getDefaultConfiguration())) ? ""
435: : configuration.getDisplayName()); //NOI18N
436: try {
437: ProjectManager.mutex().writeAccess(
438: new Mutex.ExceptionAction<Object>() {
439: public Object run() {
440: h
441: .putProperties(
442: AntProjectHelper.PRIVATE_PROPERTIES_PATH,
443: ep);
444: return null;
445: }
446: });
447: ProjectManager.getDefault().saveProject(p);
448: } catch (MutexException me) {
449: ErrorManager.getDefault().notify(me.getException());
450: }
451:
452: final ProjectConfiguration newAC = getActiveConfiguration();
453: if ((oldAC != null) ? (!oldAC.equals(newAC)) : (newAC != null))
454: psp.firePropertyChange(PROP_CONFIGURATION_ACTIVE, oldAC,
455: newAC);
456: }
457:
458: public void configurationXmlChanged(final AntProjectEvent ev) {
459: }
460:
461: public synchronized void propertiesChanged(final AntProjectEvent ev) {
462: if (psp == null)
463: return;
464: if (AntProjectHelper.PRIVATE_PROPERTIES_PATH.equals(ev
465: .getPath())) {
466: final ProjectConfiguration oldAC = activeConfiguration;
467: activeConfiguration = null;
468: final ProjectConfiguration newAC = getActiveConfiguration();
469: if ((oldAC == null && newAC != null)
470: || (oldAC != null && !oldAC.equals(newAC))) {
471: psp.firePropertyChange(PROP_CONFIGURATION_ACTIVE,
472: oldAC, newAC);
473: }
474: } else if (AntProjectHelper.PROJECT_PROPERTIES_PATH.equals(ev
475: .getPath())) {
476: invalid = true;
477: final TreeMap<String, ProjectConfiguration> old = configurations;
478: final ProjectConfiguration oldCFs[] = old.values().toArray(
479: new ProjectConfiguration[old.size()]);
480: configurations = null;
481: final ProjectConfiguration newCFs[] = getConfigurations(old)
482: .toArray(new ProjectConfiguration[0]);
483: if (!Arrays.equals(oldCFs, newCFs)) {
484: psp.firePropertyChange(PROP_CONFIGURATIONS, oldCFs,
485: newCFs);
486: }
487: psp.firePropertyChange(PROJECT_PROPERTIES, null,
488: getActiveConfiguration());
489: }
490: }
491:
492: public boolean hasCustomizer() {
493: return true;
494: }
495:
496: public void customize() {
497: final J2MECustomizerProvider cp = p.getLookup().lookup(
498: J2MECustomizerProvider.class);
499: if (cp != null)
500: SwingUtilities.invokeLater(new Runnable() {
501: public void run() {
502: cp.showCustomizer(true);
503: }
504: });
505: }
506:
507: public boolean configurationsAffectAction(String command) {
508: return false;
509: }
510:
511: private static final class ConfigurationImpl implements
512: ProjectConfiguration {
513:
514: private final String name;
515:
516: public ConfigurationImpl(String name) {
517: this .name = name;
518: }
519:
520: public String getDisplayName() {
521: return name;
522: }
523:
524: public boolean equals(final Object o) {
525: if (!(o instanceof ConfigurationImpl))
526: return false;
527: final String name2 = ((ConfigurationImpl) o)
528: .getDisplayName();
529: return (name != null) ? name.equals(name2) : name2 == null;
530: }
531:
532: public int hashCode() {
533: return (name != null) ? name.hashCode() : 0;
534: }
535:
536: }
537: }
|