001: /*
002: * Sun Public License Notice
003: *
004: * The contents of this file are subject to the Sun Public License
005: * Version 1.0 (the "License"). You may not use this file except in
006: * compliance with the License. A copy of the License is available at
007: * http://www.sun.com/
008: *
009: * The Original Code is NetBeans. The Initial Developer of the Original
010: * Code is Sun Microsystems, Inc. Portions Copyright 1997-2006 Sun
011: * Microsystems, Inc. All Rights Reserved.
012:
013: If you wish your version of this file to be governed by only the CDDL
014: or only the GPL Version 2, indicate your decision by adding
015: "[Contributor] elects to include this software in this distribution
016: under the [CDDL or GPL Version 2] license." If you do not indicate a
017: single choice of license, a recipient has the option to distribute
018: your version of this file under either the CDDL, the GPL Version 2 or
019: to extend the choice of license to its licensees as provided above.
020: However, if you add GPL Version 2 code and therefore, elected the GPL
021: Version 2 license, then the option applies only if the new code is
022: made subject to such option by the copyright holder.
023: */
024:
025: package org.netbeans.modules.java.j2seproject;
026:
027: import java.beans.PropertyChangeEvent;
028: import java.beans.PropertyChangeListener;
029: import java.beans.PropertyChangeSupport;
030: import java.io.IOException;
031: import java.io.InputStream;
032: import java.text.Collator;
033: import java.util.ArrayList;
034: import java.util.Collection;
035: import java.util.Collections;
036: import java.util.Comparator;
037: import java.util.HashMap;
038: import java.util.List;
039: import java.util.Map;
040: import java.util.Properties;
041: import java.util.Set;
042: import java.util.logging.Level;
043: import java.util.logging.Logger;
044: import org.netbeans.api.project.ProjectManager;
045: import org.netbeans.modules.java.j2seproject.ui.customizer.CustomizerProviderImpl;
046: import org.netbeans.modules.java.j2seproject.ui.customizer.J2SECompositePanelProvider;
047: import org.netbeans.spi.project.ActionProvider;
048: import org.netbeans.spi.project.ProjectConfiguration;
049: import org.netbeans.spi.project.ProjectConfigurationProvider;
050: import org.netbeans.spi.project.support.ant.EditableProperties;
051: import org.openide.filesystems.FileChangeAdapter;
052: import org.openide.filesystems.FileChangeListener;
053: import org.openide.filesystems.FileEvent;
054: import org.openide.filesystems.FileObject;
055: import org.openide.filesystems.FileRenameEvent;
056: import org.openide.filesystems.FileUtil;
057: import org.openide.util.NbBundle;
058: import org.openide.util.Utilities;
059:
060: /**
061: * Manages configurations for a Java SE project.
062: * @author Jesse Glick
063: */
064: final class J2SEConfigurationProvider implements
065: ProjectConfigurationProvider<J2SEConfigurationProvider.Config> {
066:
067: private static final Logger LOGGER = Logger
068: .getLogger(J2SEConfigurationProvider.class.getName());
069:
070: /**
071: * Ant property name for active config.
072: */
073: public static final String PROP_CONFIG = "config"; // NOI18N
074: /**
075: * Ant property file which specified active config.
076: */
077: public static final String CONFIG_PROPS_PATH = "nbproject/private/config.properties"; // NOI18N
078:
079: public static final class Config implements ProjectConfiguration {
080: /** file basename, or null for default config */
081: public final String name;
082: private final String displayName;
083:
084: public Config(String name, String displayName) {
085: this .name = name;
086: this .displayName = displayName;
087: }
088:
089: public String getDisplayName() {
090: return displayName;
091: }
092:
093: public int hashCode() {
094: return name != null ? name.hashCode() : 0;
095: }
096:
097: public boolean equals(Object o) {
098: return (o instanceof Config)
099: && Utilities
100: .compareObjects(name, ((Config) o).name);
101: }
102:
103: public String toString() {
104: return "J2SEConfigurationProvider.Config[" + name + ","
105: + displayName + "]"; // NOI18N
106: }
107: }
108:
109: private static final Config DEFAULT = new Config(null, NbBundle
110: .getMessage(J2SEConfigurationProvider.class,
111: "J2SEConfigurationProvider.default.label"));
112:
113: private final J2SEProject p;
114: private final PropertyChangeSupport pcs = new PropertyChangeSupport(
115: this );
116: private final FileChangeListener fcl = new FileChangeAdapter() {
117: public void fileFolderCreated(FileEvent fe) {
118: update(fe);
119: }
120:
121: public void fileDataCreated(FileEvent fe) {
122: update(fe);
123: }
124:
125: public void fileDeleted(FileEvent fe) {
126: update(fe);
127: }
128:
129: public void fileRenamed(FileRenameEvent fe) {
130: update(fe);
131: }
132:
133: private void update(FileEvent ev) {
134: LOGGER.log(Level.FINEST, "Received {0}", ev);
135: Set<String> oldConfigs = configs != null ? configs.keySet()
136: : Collections.<String> emptySet();
137: configDir = p.getProjectDirectory().getFileObject(
138: "nbproject/configs"); // NOI18N
139: if (configDir != null) {
140: configDir.removeFileChangeListener(fclWeak);
141: configDir.addFileChangeListener(fclWeak);
142: LOGGER.log(Level.FINEST, "(Re-)added listener to {0}",
143: configDir);
144: } else {
145: LOGGER.log(Level.FINEST, "No nbproject/configs exists");
146: }
147: calculateConfigs();
148: Set<String> newConfigs = configs.keySet();
149: if (!oldConfigs.equals(newConfigs)) {
150: LOGGER
151: .log(
152: Level.FINER,
153: "Firing "
154: + ProjectConfigurationProvider.PROP_CONFIGURATIONS
155: + ": {0} -> {1}", new Object[] {
156: oldConfigs, newConfigs });
157: pcs
158: .firePropertyChange(
159: ProjectConfigurationProvider.PROP_CONFIGURATIONS,
160: null, null);
161: // XXX also fire PROP_ACTIVE_CONFIGURATION?
162: }
163: }
164: };
165: private final FileChangeListener fclWeak;
166: private FileObject configDir;
167: private Map<String, Config> configs;
168: private FileObject nbp;
169:
170: public J2SEConfigurationProvider(J2SEProject p) {
171: this .p = p;
172: fclWeak = FileUtil.weakFileChangeListener(fcl, null);
173: nbp = p.getProjectDirectory().getFileObject("nbproject"); // NOI18N
174: if (nbp != null) {
175: nbp.addFileChangeListener(fclWeak);
176: LOGGER.log(Level.FINEST, "Added listener to {0}", nbp);
177: configDir = nbp.getFileObject("configs"); // NOI18N
178: if (configDir != null) {
179: configDir.addFileChangeListener(fclWeak);
180: LOGGER.log(Level.FINEST, "Added listener to {0}",
181: configDir);
182: }
183: }
184: p.evaluator().addPropertyChangeListener(
185: new PropertyChangeListener() {
186: public void propertyChange(PropertyChangeEvent evt) {
187: if (PROP_CONFIG.equals(evt.getPropertyName())) {
188: LOGGER
189: .log(
190: Level.FINER,
191: "Refiring "
192: + PROP_CONFIG
193: + " -> "
194: + ProjectConfigurationProvider.PROP_CONFIGURATION_ACTIVE);
195: pcs
196: .firePropertyChange(
197: ProjectConfigurationProvider.PROP_CONFIGURATION_ACTIVE,
198: null, null);
199: }
200: }
201: });
202: }
203:
204: private void calculateConfigs() {
205: configs = new HashMap<String, Config>();
206: if (configDir != null) {
207: for (FileObject kid : configDir.getChildren()) {
208: if (!kid.hasExt("properties")) {
209: continue;
210: }
211: try {
212: InputStream is = kid.getInputStream();
213: try {
214: Properties p = new Properties();
215: p.load(is);
216: String name = kid.getName();
217: String label = p.getProperty("$label"); // NOI18N
218: configs.put(name, new Config(name,
219: label != null ? label : name));
220: } finally {
221: is.close();
222: }
223: } catch (IOException x) {
224: LOGGER.log(Level.INFO, null, x);
225: }
226: }
227: }
228: LOGGER.log(Level.FINEST, "Calculated configurations: {0}",
229: configs);
230: }
231:
232: public Collection<Config> getConfigurations() {
233: calculateConfigs();
234: List<Config> l = new ArrayList<Config>();
235: l.addAll(configs.values());
236: Collections.sort(l, new Comparator<Config>() {
237: Collator c = Collator.getInstance();
238:
239: public int compare(Config c1, Config c2) {
240: return c.compare(c1.getDisplayName(), c2
241: .getDisplayName());
242: }
243: });
244: l.add(0, DEFAULT);
245: return l;
246: }
247:
248: public Config getActiveConfiguration() {
249: calculateConfigs();
250: String config = p.evaluator().getProperty(PROP_CONFIG);
251: if (config != null && configs.containsKey(config)) {
252: return configs.get(config);
253: } else {
254: return DEFAULT;
255: }
256: }
257:
258: public void setActiveConfiguration(Config c)
259: throws IllegalArgumentException, IOException {
260: if (c != DEFAULT && !configs.values().contains(c)) {
261: throw new IllegalArgumentException();
262: }
263: final String n = c.name;
264: EditableProperties ep = p.getUpdateHelper().getProperties(
265: CONFIG_PROPS_PATH);
266: if (Utilities.compareObjects(n, ep.getProperty(PROP_CONFIG))) {
267: return;
268: }
269: if (n != null) {
270: ep.setProperty(PROP_CONFIG, n);
271: } else {
272: ep.remove(PROP_CONFIG);
273: }
274: p.getUpdateHelper().putProperties(CONFIG_PROPS_PATH, ep);
275: pcs.firePropertyChange(
276: ProjectConfigurationProvider.PROP_CONFIGURATION_ACTIVE,
277: null, null);
278: ProjectManager.getDefault().saveProject(p);
279: assert p.getProjectDirectory().getFileObject(CONFIG_PROPS_PATH) != null;
280: }
281:
282: public boolean hasCustomizer() {
283: return true;
284: }
285:
286: public void customize() {
287: p.getLookup().lookup(CustomizerProviderImpl.class)
288: .showCustomizer(J2SECompositePanelProvider.RUN);
289: }
290:
291: public boolean configurationsAffectAction(String command) {
292: return command.equals(ActionProvider.COMMAND_RUN)
293: || command.equals(ActionProvider.COMMAND_DEBUG);
294: }
295:
296: public void addPropertyChangeListener(PropertyChangeListener lst) {
297: pcs.addPropertyChangeListener(lst);
298: }
299:
300: public void removePropertyChangeListener(PropertyChangeListener lst) {
301: pcs.removePropertyChangeListener(lst);
302: }
303:
304: }
|