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.suite;
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.Collections;
050: import java.util.HashMap;
051: import java.util.List;
052: import java.util.Map;
053: import java.util.Set;
054: import javax.swing.Icon;
055: import javax.swing.ImageIcon;
056: import org.netbeans.api.project.Project;
057: import org.netbeans.api.project.ProjectInformation;
058: import org.netbeans.api.project.ProjectManager;
059: import org.netbeans.modules.apisupport.project.SuiteProvider;
060: import org.netbeans.modules.apisupport.project.Util;
061: import org.netbeans.modules.apisupport.project.queries.FileEncodingQueryImpl;
062: import org.netbeans.modules.apisupport.project.queries.TemplateAttributesProvider;
063: import org.netbeans.modules.apisupport.project.ui.SuiteActions;
064: import org.netbeans.modules.apisupport.project.ui.SuiteLogicalView;
065: import org.netbeans.modules.apisupport.project.ui.SuiteOperations;
066: import org.netbeans.modules.apisupport.project.ui.customizer.SuiteCustomizer;
067: import org.netbeans.modules.apisupport.project.ui.customizer.SuiteProperties;
068: import org.netbeans.modules.apisupport.project.universe.NbPlatform;
069: import org.netbeans.spi.project.support.LookupProviderSupport;
070: import org.netbeans.spi.project.support.ant.AntProjectEvent;
071: import org.netbeans.spi.project.support.ant.AntProjectHelper;
072: import org.netbeans.spi.project.support.ant.AntProjectListener;
073: import org.netbeans.spi.project.support.ant.EditableProperties;
074: import org.netbeans.spi.project.support.ant.GeneratedFilesHelper;
075: import org.netbeans.spi.project.support.ant.ProjectXmlSavedHook;
076: import org.netbeans.spi.project.support.ant.PropertyEvaluator;
077: import org.netbeans.spi.project.support.ant.PropertyProvider;
078: import org.netbeans.spi.project.support.ant.PropertyUtils;
079: import org.netbeans.spi.project.ui.PrivilegedTemplates;
080: import org.netbeans.spi.project.ui.ProjectOpenedHook;
081: import org.netbeans.spi.project.ui.RecommendedTemplates;
082: import org.netbeans.spi.project.ui.support.UILookupMergerSupport;
083: import org.openide.ErrorManager;
084: import org.openide.filesystems.FileObject;
085: import org.openide.filesystems.FileUtil;
086: import org.openide.util.Lookup;
087: import org.openide.util.Mutex;
088: import org.openide.util.Utilities;
089: import org.openide.util.lookup.Lookups;
090: import org.w3c.dom.Element;
091:
092: /**
093: * Represents one module suite project.
094: * @author Jesse Glick
095: */
096: public final class SuiteProject implements Project {
097:
098: public static final String SUITE_ICON_PATH = "org/netbeans/modules/apisupport/project/suite/resources/suite.png"; // NOI18N
099:
100: private final AntProjectHelper helper;
101: private Lookup lookup;
102: private final PropertyEvaluator eval;
103: private final GeneratedFilesHelper genFilesHelper;
104:
105: public SuiteProject(AntProjectHelper helper) throws IOException {
106: this .helper = helper;
107: eval = createEvaluator();
108: genFilesHelper = new GeneratedFilesHelper(helper);
109: Util.err.log("Loading suite project in "
110: + getProjectDirectory());
111: lookup = Lookups.fixed(
112: this ,
113: new Info(),
114: helper.createAuxiliaryConfiguration(),
115: helper.createCacheDirectoryProvider(),
116: new SavedHook(),
117: UILookupMergerSupport
118: .createProjectOpenHookMerger(new OpenedHook()),
119: helper.createSharabilityQuery(eval, new String[0],
120: new String[] { "build", "${dist.dir}" }), // NOI18N
121: new SuiteSubprojectProviderImpl(helper, eval),
122: new SuiteProviderImpl(), new SuiteActions(this ),
123: new SuiteLogicalView(this ), new SuiteCustomizer(this ,
124: helper, eval), new PrivilegedTemplatesImpl(),
125: new SuiteOperations(this ),
126: new TemplateAttributesProvider(helper, false),
127: new FileEncodingQueryImpl());
128: lookup = LookupProviderSupport
129: .createCompositeLookup(lookup,
130: "Projects/org-netbeans-modules-apisupport-project-suite/Lookup");
131: }
132:
133: public @Override
134: String toString() {
135: return "SuiteProject[" + getProjectDirectory() + "]"; // NOI18N
136: }
137:
138: public Lookup getLookup() {
139: return lookup;
140: }
141:
142: public FileObject getProjectDirectory() {
143: return helper.getProjectDirectory();
144: }
145:
146: public File getProjectDirectoryFile() {
147: return FileUtil.toFile(getProjectDirectory());
148: }
149:
150: /** For unit tests purpose only. */
151: public AntProjectHelper getHelper() {
152: return helper;
153: }
154:
155: /** For unit tests purpose only. */
156: public PropertyEvaluator getEvaluator() {
157: return eval;
158: }
159:
160: /**
161: * Get the platform selected for use with this suite.
162: * @param fallback if true, fall back to the default platform if necessary
163: * @return the current platform; or null if fallback is false and there is no
164: * platform specified, or an invalid platform is specified, or even if
165: * fallback is true but even the default platform is not available
166: */
167: public NbPlatform getPlatform(boolean fallback) {
168: NbPlatform p;
169: // #65652: more reliable to use the dest dir, in case nbplatform.active is not set.
170: String destdir = getEvaluator()
171: .getProperty("netbeans.dest.dir"); // NOI18N
172: if (destdir != null) {
173: p = NbPlatform.getPlatformByDestDir(getHelper()
174: .resolveFile(destdir));
175: } else {
176: p = null;
177: }
178: if (fallback && (p == null || !p.isValid())) {
179: p = NbPlatform.getDefaultPlatform();
180: }
181: return p;
182: }
183:
184: private PropertyEvaluator createEvaluator() {
185: PropertyProvider predefs = helper.getStockPropertyPreprovider();
186: File dir = getProjectDirectoryFile();
187: List<PropertyProvider> providers = new ArrayList<PropertyProvider>();
188: providers
189: .add(helper
190: .getPropertyProvider("nbproject/private/platform-private.properties")); // NOI18N
191: providers.add(helper
192: .getPropertyProvider("nbproject/platform.properties")); // NOI18N
193: PropertyEvaluator baseEval = PropertyUtils
194: .sequentialPropertyEvaluator(predefs,
195: providers
196: .toArray(new PropertyProvider[providers
197: .size()]));
198: providers
199: .add(new Util.UserPropertiesFileProvider(baseEval, dir));
200: baseEval = PropertyUtils
201: .sequentialPropertyEvaluator(predefs,
202: providers
203: .toArray(new PropertyProvider[providers
204: .size()]));
205: class DestDirProvider extends Util.ComputedPropertyProvider {
206: public DestDirProvider(PropertyEvaluator eval) {
207: super (eval);
208: }
209:
210: protected Map<String, String> getProperties(
211: Map<String, String> inputPropertyValues) {
212: String platformS = inputPropertyValues
213: .get("nbplatform.active"); // NOI18N
214: if (platformS != null) {
215: return Collections
216: .singletonMap("netbeans.dest.dir",
217: "${nbplatform." + platformS
218: + ".netbeans.dest.dir}"); // NOI18N
219: } else {
220: return Collections.emptyMap();
221: }
222: }
223:
224: protected Set<String> inputProperties() {
225: return Collections.singleton("nbplatform.active"); // NOI18N
226: }
227: }
228: providers.add(new DestDirProvider(baseEval));
229: providers
230: .add(helper
231: .getPropertyProvider(AntProjectHelper.PRIVATE_PROPERTIES_PATH));
232: providers
233: .add(helper
234: .getPropertyProvider(AntProjectHelper.PROJECT_PROPERTIES_PATH));
235: Map<String, String> fixedProps = new HashMap<String, String>();
236: // synchronize with suite.xml
237: fixedProps.put(SuiteProperties.ENABLED_CLUSTERS_PROPERTY, "");
238: fixedProps.put(SuiteProperties.DISABLED_CLUSTERS_PROPERTY, "");
239: fixedProps.put(SuiteProperties.DISABLED_MODULES_PROPERTY, "");
240: fixedProps.put(BrandingSupport.BRANDING_DIR_PROPERTY,
241: "branding"); // NOI18N
242: fixedProps.put("dist.dir", "dist"); // NOI18N
243: providers.add(PropertyUtils.fixedPropertyProvider(fixedProps));
244: return PropertyUtils
245: .sequentialPropertyEvaluator(predefs,
246: providers
247: .toArray(new PropertyProvider[providers
248: .size()]));
249: }
250:
251: private final class Info implements ProjectInformation,
252: AntProjectListener {
253:
254: private final PropertyChangeSupport pcs = new PropertyChangeSupport(
255: this );
256:
257: Info() {
258: helper.addAntProjectListener(this );
259: }
260:
261: private String getSimpleName() {
262: Element nameEl = Util.findElement(helper
263: .getPrimaryConfigurationData(true), "name",
264: SuiteProjectType.NAMESPACE_SHARED); // NOI18N
265: String text = (nameEl != null) ? Util.findText(nameEl)
266: : null;
267: return (text != null) ? text : "???"; // NOI18N
268: }
269:
270: public String getName() {
271: return PropertyUtils.getUsablePropertyName(getSimpleName());
272: }
273:
274: public String getDisplayName() {
275: String appTitle = getEvaluator().getProperty("app.title"); // NOI18N
276: if (appTitle != null) {
277: return appTitle;
278: } else {
279: return getSimpleName();
280: }
281: }
282:
283: public Icon getIcon() {
284: return new ImageIcon(Utilities.loadImage(SUITE_ICON_PATH));
285: }
286:
287: public Project getProject() {
288: return SuiteProject.this ;
289: }
290:
291: public void addPropertyChangeListener(
292: PropertyChangeListener listener) {
293: pcs.addPropertyChangeListener(listener);
294: }
295:
296: public void removePropertyChangeListener(
297: PropertyChangeListener listener) {
298: pcs.removePropertyChangeListener(listener);
299: }
300:
301: public void configurationXmlChanged(AntProjectEvent ev) {
302: fireNameChange();
303: }
304:
305: public void propertiesChanged(AntProjectEvent ev) {
306: fireNameChange();
307: }
308:
309: private void fireNameChange() {
310: pcs.firePropertyChange(ProjectInformation.PROP_NAME, null,
311: getName());
312: pcs.firePropertyChange(
313: ProjectInformation.PROP_DISPLAY_NAME, null,
314: getDisplayName());
315: }
316:
317: }
318:
319: /** For access from tests. */
320: public void open() {
321: // XXX skip this in case nbplatform.active is not defined
322: ProjectManager.mutex().writeAccess(new Mutex.Action<Void>() {
323: public Void run() {
324: String path = "nbproject/private/platform-private.properties"; // NOI18N
325: EditableProperties ep = helper.getProperties(path);
326: File buildProperties = new File(System
327: .getProperty("netbeans.user"),
328: "build.properties"); // NOI18N
329: ep.setProperty("user.properties.file", buildProperties
330: .getAbsolutePath()); //NOI18N
331: helper.putProperties(path, ep);
332: try {
333: ProjectManager.getDefault().saveProject(
334: SuiteProject.this );
335: } catch (IOException e) {
336: ErrorManager.getDefault().notify(e);
337: }
338: return null;
339: }
340: });
341: // refresh build.xml and build-impl.xml
342: try {
343: genFilesHelper.refreshBuildScript(
344: GeneratedFilesHelper.BUILD_IMPL_XML_PATH,
345: SuiteProject.class
346: .getResource("resources/build-impl.xsl"),
347: true);
348: genFilesHelper.refreshBuildScript(
349: GeneratedFilesHelper.BUILD_XML_PATH,
350: SuiteProject.class
351: .getResource("resources/build.xsl"), true);
352: } catch (IOException e) {
353: ErrorManager.getDefault().notify(
354: ErrorManager.INFORMATIONAL, e);
355: }
356: }
357:
358: private final class OpenedHook extends ProjectOpenedHook {
359: OpenedHook() {
360: }
361:
362: public void projectOpened() {
363: open();
364: }
365:
366: protected void projectClosed() {
367: try {
368: ProjectManager.getDefault().saveProject(
369: SuiteProject.this );
370: } catch (IOException e) {
371: Util.err.notify(e);
372: }
373: }
374: }
375:
376: private final class SavedHook extends ProjectXmlSavedHook {
377:
378: SavedHook() {
379: }
380:
381: protected void projectXmlSaved() throws IOException {
382: // refresh build.xml and build-impl.xml
383: genFilesHelper.refreshBuildScript(
384: GeneratedFilesHelper.BUILD_IMPL_XML_PATH,
385: SuiteProject.class
386: .getResource("resources/build-impl.xsl"),
387: false);
388: genFilesHelper.refreshBuildScript(
389: GeneratedFilesHelper.BUILD_XML_PATH,
390: SuiteProject.class
391: .getResource("resources/build.xsl"), false);
392: }
393:
394: }
395:
396: private final class SuiteProviderImpl implements SuiteProvider {
397:
398: public File getSuiteDirectory() {
399: return getProjectDirectoryFile();
400: }
401:
402: }
403:
404: private static final class PrivilegedTemplatesImpl implements
405: PrivilegedTemplates, RecommendedTemplates {
406:
407: private static final String[] PRIVILEGED_NAMES = new String[] {
408: "Templates/Ant/Project.xml", // NOI18N
409: "Templates/Other/properties.properties", // NOI18N
410: };
411:
412: private static final String[] RECOMMENDED_TYPES = new String[] {
413: "oasis-XML-catalogs", // NOI18N
414: "XML", // NOI18N
415: "ant-script", // NOI18N
416: "simple-files", // NOI18N
417: };
418:
419: public String[] getPrivilegedTemplates() {
420: return PRIVILEGED_NAMES;
421: }
422:
423: public String[] getRecommendedTypes() {
424: return RECOMMENDED_TYPES;
425: }
426: }
427:
428: }
|