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.apisupport.project.ui.customizer;
043:
044: import java.beans.PropertyChangeListener;
045: import java.beans.PropertyChangeSupport;
046: import java.io.File;
047: import java.io.IOException;
048: import java.util.ArrayList;
049: import java.util.Collection;
050: import java.util.List;
051: import java.util.Map;
052: import org.netbeans.api.java.platform.JavaPlatform;
053: import org.netbeans.api.java.platform.JavaPlatformManager;
054: import org.netbeans.api.project.ProjectManager;
055: import org.netbeans.modules.apisupport.project.NbModuleProjectGenerator;
056: import org.netbeans.modules.apisupport.project.Util;
057: import org.netbeans.modules.apisupport.project.universe.NbPlatform;
058: import org.netbeans.spi.project.support.ant.AntProjectHelper;
059: import org.netbeans.spi.project.support.ant.EditableProperties;
060: import org.netbeans.spi.project.support.ant.PropertyEvaluator;
061: import org.openide.DialogDescriptor;
062: import org.openide.DialogDisplayer;
063: import org.openide.filesystems.FileObject;
064: import org.openide.filesystems.FileUtil;
065: import org.openide.util.Mutex;
066: import org.openide.util.MutexException;
067: import org.openide.util.NbBundle;
068: import org.openide.util.RequestProcessor;
069:
070: /**
071: * Basic support for storing general module's properties.
072: *
073: * @author Martin Krauskopf
074: */
075: public abstract class ModuleProperties {
076:
077: public static final String PROPERTIES_REFRESHED = "propertiesRefreshed"; // NOI18N
078:
079: static final RequestProcessor RP = new RequestProcessor(
080: ModuleProperties.class.getName());
081:
082: // XXX better to inject own DialogDisplayer when running tests or similar trick
083: static boolean runFromTests;
084:
085: // Helpers for storing and retrieving real values currently stored on the disk
086: private final AntProjectHelper helper;
087: private final PropertyEvaluator evaluator;
088:
089: /** Represent main module's properties (nbproject/project.properties). */
090: private EditableProperties projectProperties;
091:
092: /** Represent module's private properties (nbproject/private/private.properties). */
093: private EditableProperties privateProperties;
094:
095: private final PropertyChangeSupport changeSupport = new PropertyChangeSupport(
096: this );
097:
098: /** Creates a new instance of ModuleProperties */
099: ModuleProperties(AntProjectHelper helper,
100: PropertyEvaluator evaluator) {
101: this .helper = helper;
102: this .evaluator = evaluator;
103: reloadProperties();
104: }
105:
106: protected void reloadProperties() {
107: this .projectProperties = helper
108: .getProperties(AntProjectHelper.PROJECT_PROPERTIES_PATH);
109: this .privateProperties = helper
110: .getProperties(AntProjectHelper.PRIVATE_PROPERTIES_PATH);
111: }
112:
113: /**
114: * Returns map of keys form main module properties and their default values.
115: * Must be overriden by a subclass.
116: */
117: abstract Map<String, String> getDefaultValues();
118:
119: AntProjectHelper getHelper() {
120: return helper;
121: }
122:
123: PropertyEvaluator getEvaluator() {
124: return evaluator;
125: }
126:
127: EditableProperties getProjectProperties() {
128: return projectProperties;
129: }
130:
131: EditableProperties getPrivateProperties() {
132: return privateProperties;
133: }
134:
135: final String getProperty(String key) {
136: String value = getProjectProperties().getProperty(key);
137: return value != null ? value : getDefaultValues().get(key);
138: }
139:
140: final boolean getBooleanProperty(String key) {
141: String bValue = getProperty(key);
142: return bValue != null && (bValue.equalsIgnoreCase("true") || // NOI18N
143: bValue.equalsIgnoreCase("yes")); // NOI18N
144: }
145:
146: final String removeProperty(String key) {
147: return getProjectProperties().remove(key);
148: }
149:
150: final String removePrivateProperty(String key) {
151: return getPrivateProperties().remove(key);
152: }
153:
154: /**
155: * The given property will be stored into the project's properties. If the
156: * given value is equals to the default value it will be removed from the
157: * properties.
158: */
159: final void setProperty(String key, String value) {
160: String def = getDefaultValues().get(key);
161: if (def == null) {
162: def = ""; // NOI18N
163: }
164: if (value == null || def.equals(value)) {
165: getProjectProperties().remove(key);
166: } else {
167: getProjectProperties().setProperty(key, value);
168: }
169: firePropertyChange(key, null, value);
170: }
171:
172: /**
173: * The given property will be stored into the project's properties. If the
174: * given value is equals to the default value it will be removed from the
175: * properties.
176: */
177: final void setPrivateProperty(String key, String value) {
178: String def = getDefaultValues().get(key);
179: if (def == null) {
180: def = ""; // NOI18N
181: }
182: if (def.equals(value)) {
183: getPrivateProperties().remove(key);
184: } else {
185: getPrivateProperties().setProperty(key, value);
186: }
187: firePropertyChange(key, null, value);
188: }
189:
190: void setProperty(String key, String[] value) {
191: getProjectProperties().setProperty(key, value);
192: firePropertyChange(key, null, null);
193: }
194:
195: final void setBooleanProperty(String key, boolean bProp) {
196: setProperty(key, Boolean.toString(bProp));
197: }
198:
199: String getProjectDisplayName() {
200: return Util.getDisplayName(getHelper().getProjectDirectory());
201: }
202:
203: final File getProjectDirectoryFile() {
204: return FileUtil.toFile(getHelper().getProjectDirectory());
205: }
206:
207: final String getProjectDirectory() {
208: return getProjectDirectoryFile().getAbsolutePath();
209: }
210:
211: /**
212: * Stores cached properties. This is called when the user press <em>OK</em>
213: * button in the properties customizer. If <em>Cancel</em> button is
214: * pressed properties will not be saved. Be sure this method is called
215: * whitin {@link ProjectManager#mutex}. However, you are well advised to
216: * explicitly enclose a <em>complete</em> operation within write access to
217: * prevent race conditions.
218: */
219: void storeProperties() throws IOException {
220: // Store changes into in nbproject/project.properties
221: getHelper().putProperties(
222: AntProjectHelper.PROJECT_PROPERTIES_PATH,
223: getProjectProperties());
224: // Store changes into in nbproject/private/private.properties
225: getHelper().putProperties(
226: AntProjectHelper.PRIVATE_PROPERTIES_PATH,
227: getPrivateProperties());
228: }
229:
230: /**
231: * Helper method for storing platform into the appropriate place.
232: */
233: static void storePlatform(AntProjectHelper helper,
234: NbPlatform platform) {
235: if (platform != null && platform.getID() != null) {
236: // store platform properties
237: EditableProperties props = helper
238: .getProperties(NbModuleProjectGenerator.PLATFORM_PROPERTIES_PATH);
239: props.put("nbplatform.active", platform.getID()); // NOI18N
240: helper.putProperties(
241: NbModuleProjectGenerator.PLATFORM_PROPERTIES_PATH,
242: props);
243: }
244: }
245:
246: public void addPropertyChangeListener(PropertyChangeListener pchl) {
247: changeSupport.addPropertyChangeListener(pchl);
248: }
249:
250: public void removePropertyChangeListener(PropertyChangeListener pchl) {
251: changeSupport.removePropertyChangeListener(pchl);
252: }
253:
254: protected void firePropertyChange(String propName, Object oldValue,
255: Object newValue) {
256: changeSupport.firePropertyChange(propName, oldValue, newValue);
257: }
258:
259: protected void firePropertiesRefreshed() {
260: firePropertyChange(PROPERTIES_REFRESHED, null, null);
261: }
262:
263: protected static void reportLostPlatform(
264: final NbPlatform lostPlatform) {
265: String plafText = lostPlatform != null ? '"' + lostPlatform
266: .getLabel() + '"' : NbBundle.getMessage(
267: ModuleProperties.class, "MSG_PreviouslySet");
268: String message = NbBundle.getMessage(ModuleProperties.class,
269: "MSG_PlatformNotFound", plafText);
270: if (!runFromTests) {
271: DialogDisplayer.getDefault().notify(
272: new DialogDescriptor.Message(message));
273: } else {
274: System.err.println(message);
275: }
276: }
277:
278: static String getPlatformID(JavaPlatform platform) {
279: // XXX why isn't there a real API for this??
280: String id = platform.getProperties().get("platform.ant.name"); // NOI18N
281: // Handle case that a platform is not an Ant platform:
282: return id != null ? id : "default"; // NOI18N
283: }
284:
285: static JavaPlatform findJavaPlatformByID(String id) {
286: if (id == null || id.equals("default")) { // NOI18N
287: return JavaPlatform.getDefault();
288: }
289: JavaPlatform[] platforms = JavaPlatformManager.getDefault()
290: .getInstalledPlatforms();
291: for (int i = 0; i < platforms.length; i++) {
292: if (id.equals(getPlatformID(platforms[i]))) {
293: return platforms[i];
294: }
295: }
296: return null;
297: }
298:
299: static void storeJavaPlatform(AntProjectHelper helper,
300: PropertyEvaluator eval, JavaPlatform platform,
301: boolean isNetBeansOrg) throws IOException {
302: if (isNetBeansOrg) {
303: final boolean isDefault = platform == null
304: || platform == JavaPlatform.getDefault();
305: final File home = isDefault ? null
306: : getPlatformLocation(platform);
307: if (home != null || isDefault) {
308: final FileObject nbbuild = helper
309: .resolveFileObject(eval
310: .evaluate("${nb_all}/nbbuild")); // NOI18N
311: if (nbbuild != null) {
312: try {
313: ProjectManager.mutex().writeAccess(
314: new Mutex.ExceptionAction<Void>() {
315: public Void run()
316: throws IOException {
317: FileObject userBuildProperties = nbbuild
318: .getFileObject("user.build.properties"); // NOI18N
319: if (userBuildProperties == null) {
320: userBuildProperties = nbbuild
321: .createData("user.build.properties"); // NOI18N
322: }
323: EditableProperties ep = Util
324: .loadProperties(userBuildProperties);
325: if (isDefault) {
326: // Have to remove it; no default value.
327: ep.remove("nbjdk.home");
328: } else {
329: ep
330: .setProperty(
331: "nbjdk.home",
332: home
333: .getAbsolutePath());
334: }
335: Util
336: .storeProperties(
337: userBuildProperties,
338: ep);
339: return null;
340: }
341: });
342: } catch (MutexException e) {
343: throw (IOException) e.getException();
344: }
345: }
346: }
347: } else {
348: EditableProperties props = helper
349: .getProperties(NbModuleProjectGenerator.PLATFORM_PROPERTIES_PATH);
350: if (platform == null
351: || platform == JavaPlatform.getDefault()) {
352: if (props.containsKey("nbjdk.active")) { // NOI18N
353: // Could also just remove it, but probably nicer to set it explicitly to 'default'.
354: props.put("nbjdk.active", "default"); // NOI18N
355: }
356: } else {
357: props.put("nbjdk.active", getPlatformID(platform)); // NOI18N
358: }
359: helper.putProperties(
360: NbModuleProjectGenerator.PLATFORM_PROPERTIES_PATH,
361: props);
362: }
363: }
364:
365: private static File getPlatformLocation(JavaPlatform platform) {
366: Collection<FileObject> installs = platform.getInstallFolders();
367: if (installs.size() == 1) {
368: return FileUtil.toFile(installs.iterator().next());
369: } else {
370: return null;
371: }
372: }
373:
374: public static JavaPlatform findJavaPlatformByLocation(String home) {
375: if (home == null) {
376: return JavaPlatform.getDefault();
377: }
378: for (JavaPlatform platform : JavaPlatformManager.getDefault()
379: .getInstalledPlatforms()) {
380: if (new File(home).equals(getPlatformLocation(platform))) {
381: return platform;
382: }
383: }
384: return null;
385: }
386:
387: void addLazyStorage(LazyStorage st) {
388: storages.add(st);
389: }
390:
391: void triggerLazyStorages() {
392: for (LazyStorage stor : storages) {
393: stor.store();
394: }
395: }
396:
397: private List<LazyStorage> storages = new ArrayList<LazyStorage>();
398:
399: /**
400: * Implement this interface when you want your panel to be told that the
401: * properties/customizer are going to be saved.
402: */
403: static interface LazyStorage {
404:
405: /** Called when user pressed <em>ok</em> before storing the model. */
406: void store();
407:
408: }
409:
410: }
|