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.java.j2seproject;
043:
044: import java.io.File;
045: import java.io.IOException;
046: import java.nio.charset.Charset;
047: import org.netbeans.api.java.platform.JavaPlatform;
048: import org.netbeans.api.java.platform.JavaPlatformManager;
049: import org.netbeans.api.project.ProjectManager;
050: import org.netbeans.api.java.project.JavaProjectConstants;
051: import org.netbeans.api.project.Sources;
052: import org.netbeans.api.project.libraries.LibraryManager;
053: import org.netbeans.api.queries.FileEncodingQuery;
054: import org.netbeans.modules.java.j2seproject.ui.customizer.J2SEProjectProperties;
055: import org.netbeans.spi.project.support.ant.AntProjectHelper;
056: import org.netbeans.spi.project.support.ant.EditableProperties;
057: import org.netbeans.spi.project.support.ant.ProjectGenerator;
058: import org.netbeans.spi.project.support.ant.PropertyUtils;
059: import org.netbeans.spi.project.support.ant.ReferenceHelper;
060: import org.openide.filesystems.FileObject;
061: import org.openide.filesystems.FileSystem;
062: import org.openide.filesystems.FileUtil;
063: import org.openide.filesystems.Repository;
064: import org.openide.loaders.DataFolder;
065: import org.openide.loaders.DataObject;
066: import org.openide.modules.SpecificationVersion;
067: import org.openide.util.Mutex;
068: import org.openide.util.MutexException;
069: import org.openide.ErrorManager;
070: import org.openide.util.Exceptions;
071: import org.openide.util.NbBundle;
072: import org.w3c.dom.Document;
073: import org.w3c.dom.Element;
074: import org.w3c.dom.NodeList;
075:
076: /**
077: * Creates a J2SEProject from scratch according to some initial configuration.
078: */
079: public class J2SEProjectGenerator {
080:
081: static final String MINIMUM_ANT_VERSION = "1.6.5";
082:
083: private J2SEProjectGenerator() {
084: }
085:
086: /**
087: * Create a new empty J2SE project.
088: * @param dir the top-level directory (need not yet exist but if it does it must be empty)
089: * @param name the name for the project
090: * @param librariesDefinition project relative or absolute OS path to libraries definition; can be null
091: * @return the helper object permitting it to be further customized
092: * @throws IOException in case something went wrong
093: */
094: public static AntProjectHelper createProject(final File dir,
095: final String name, final String mainClass,
096: final String manifestFile, final String librariesDefinition)
097: throws IOException {
098: final FileObject dirFO = FileUtil.createFolder(dir);
099: // if manifestFile is null => it's TYPE_LIB
100: final AntProjectHelper[] h = new AntProjectHelper[1];
101: dirFO.getFileSystem().runAtomicAction(
102: new FileSystem.AtomicAction() {
103: public void run() throws IOException {
104: h[0] = createProject(dirFO, name, "src",
105: "test", mainClass, manifestFile,
106: manifestFile == null,
107: librariesDefinition); //NOI18N
108: final J2SEProject p = (J2SEProject) ProjectManager
109: .getDefault().findProject(dirFO);
110: ProjectManager.getDefault().saveProject(p);
111: final ReferenceHelper refHelper = p
112: .getReferenceHelper();
113: try {
114: ProjectManager.mutex().writeAccess(
115: new Mutex.ExceptionAction<Void>() {
116: public Void run()
117: throws Exception {
118: copyRequiredLibraries(h[0],
119: refHelper);
120: return null;
121: }
122: });
123: } catch (MutexException ex) {
124: Exceptions.printStackTrace(ex
125: .getException());
126: }
127: FileObject srcFolder = dirFO
128: .createFolder("src"); // NOI18N
129: dirFO.createFolder("test"); // NOI18N
130: if (mainClass != null) {
131: createMainClass(mainClass, srcFolder);
132: }
133: }
134: });
135:
136: return h[0];
137: }
138:
139: public static AntProjectHelper createProject(final File dir,
140: final String name, final File[] sourceFolders,
141: final File[] testFolders, final String manifestFile,
142: final String librariesDefinition, final String buildXmlName)
143: throws IOException {
144: assert sourceFolders != null && testFolders != null : "Package roots can't be null"; //NOI18N
145: final FileObject dirFO = FileUtil.createFolder(dir);
146: final AntProjectHelper[] h = new AntProjectHelper[1];
147: // this constructor creates only java application type
148: dirFO.getFileSystem().runAtomicAction(
149: new FileSystem.AtomicAction() {
150: public void run() throws IOException {
151: h[0] = createProject(dirFO, name, null, null,
152: null, manifestFile, false,
153: librariesDefinition);
154: final J2SEProject p = (J2SEProject) ProjectManager
155: .getDefault().findProject(dirFO);
156: final ReferenceHelper refHelper = p
157: .getReferenceHelper();
158: try {
159: ProjectManager.mutex().writeAccess(
160: new Mutex.ExceptionAction<Void>() {
161: public Void run()
162: throws Exception {
163: Element data = h[0]
164: .getPrimaryConfigurationData(true);
165: Document doc = data
166: .getOwnerDocument();
167: NodeList nl = data
168: .getElementsByTagNameNS(
169: J2SEProjectType.PROJECT_CONFIGURATION_NAMESPACE,
170: "source-roots");
171: assert nl.getLength() == 1;
172: Element sourceRoots = (Element) nl
173: .item(0);
174: nl = data
175: .getElementsByTagNameNS(
176: J2SEProjectType.PROJECT_CONFIGURATION_NAMESPACE,
177: "test-roots"); //NOI18N
178: assert nl.getLength() == 1;
179: Element testRoots = (Element) nl
180: .item(0);
181: for (int i = 0; i < sourceFolders.length; i++) {
182: String propName;
183: if (i == 0) {
184: //Name the first src root src.dir to be compatible with NB 4.0
185: propName = "src.dir"; //NOI18N
186: } else {
187: String name = sourceFolders[i]
188: .getName();
189: propName = name
190: + ".dir"; //NOI18N
191: }
192:
193: int rootIndex = 1;
194: EditableProperties props = h[0]
195: .getProperties(AntProjectHelper.PROJECT_PROPERTIES_PATH);
196: while (props
197: .containsKey(propName)) {
198: rootIndex++;
199: propName = name
200: + rootIndex
201: + ".dir"; //NOI18N
202: }
203: String srcReference = refHelper
204: .createForeignFileReference(
205: sourceFolders[i],
206: JavaProjectConstants.SOURCES_TYPE_JAVA);
207: Element root = doc
208: .createElementNS(
209: J2SEProjectType.PROJECT_CONFIGURATION_NAMESPACE,
210: "root"); //NOI18N
211: root.setAttribute("id",
212: propName); //NOI18N
213: sourceRoots
214: .appendChild(root);
215: props = h[0]
216: .getProperties(AntProjectHelper.PROJECT_PROPERTIES_PATH);
217: props.put(propName,
218: srcReference);
219: h[0]
220: .putProperties(
221: AntProjectHelper.PROJECT_PROPERTIES_PATH,
222: props); // #47609
223: }
224: for (int i = 0; i < testFolders.length; i++) {
225: if (!testFolders[i]
226: .exists()) {
227: testFolders[i]
228: .mkdirs();
229: }
230: String propName;
231: if (i == 0) {
232: //Name the first test root test.src.dir to be compatible with NB 4.0
233: propName = "test.src.dir"; //NOI18N
234: } else {
235: String name = testFolders[i]
236: .getName();
237: propName = "test."
238: + name
239: + ".dir"; // NOI18N
240: }
241: int rootIndex = 1;
242: EditableProperties props = h[0]
243: .getProperties(AntProjectHelper.PROJECT_PROPERTIES_PATH);
244: while (props
245: .containsKey(propName)) {
246: rootIndex++;
247: propName = "test."
248: + name
249: + rootIndex
250: + ".dir"; // NOI18N
251: }
252: String testReference = refHelper
253: .createForeignFileReference(
254: testFolders[i],
255: JavaProjectConstants.SOURCES_TYPE_JAVA);
256: Element root = doc
257: .createElementNS(
258: J2SEProjectType.PROJECT_CONFIGURATION_NAMESPACE,
259: "root"); // NOI18N
260: root.setAttribute("id",
261: propName); // NOI18N
262: testRoots
263: .appendChild(root);
264: props = h[0]
265: .getProperties(AntProjectHelper.PROJECT_PROPERTIES_PATH); // #47609
266: props.put(propName,
267: testReference);
268: h[0]
269: .putProperties(
270: AntProjectHelper.PROJECT_PROPERTIES_PATH,
271: props);
272: }
273: h[0]
274: .putPrimaryConfigurationData(
275: data, true);
276: if (buildXmlName != null) {
277: final EditableProperties props = h[0]
278: .getProperties(AntProjectHelper.PROJECT_PROPERTIES_PATH);
279: props
280: .put(
281: J2SEProjectProperties.BUILD_SCRIPT,
282: buildXmlName);
283: h[0]
284: .putProperties(
285: AntProjectHelper.PROJECT_PROPERTIES_PATH,
286: props);
287: }
288: ProjectManager.getDefault()
289: .saveProject(p);
290: copyRequiredLibraries(h[0],
291: refHelper);
292: return null;
293: }
294: });
295: } catch (MutexException me) {
296: ErrorManager.getDefault().notify(me);
297: }
298: }
299: });
300: return h[0];
301: }
302:
303: private static AntProjectHelper createProject(FileObject dirFO,
304: String name, String srcRoot, String testRoot,
305: String mainClass, String manifestFile, boolean isLibrary,
306: String librariesDefinition) throws IOException {
307: AntProjectHelper h = ProjectGenerator.createProject(dirFO,
308: J2SEProjectType.TYPE, librariesDefinition);
309: Element data = h.getPrimaryConfigurationData(true);
310: Document doc = data.getOwnerDocument();
311: Element nameEl = doc
312: .createElementNS(
313: J2SEProjectType.PROJECT_CONFIGURATION_NAMESPACE,
314: "name"); // NOI18N
315: nameEl.appendChild(doc.createTextNode(name));
316: data.appendChild(nameEl);
317: Element minant = doc.createElementNS(
318: J2SEProjectType.PROJECT_CONFIGURATION_NAMESPACE,
319: "minimum-ant-version"); // NOI18N
320: minant.appendChild(doc.createTextNode(MINIMUM_ANT_VERSION)); // NOI18N
321: data.appendChild(minant);
322: EditableProperties ep = h
323: .getProperties(AntProjectHelper.PROJECT_PROPERTIES_PATH);
324: Element sourceRoots = doc.createElementNS(
325: J2SEProjectType.PROJECT_CONFIGURATION_NAMESPACE,
326: "source-roots"); //NOI18N
327: if (srcRoot != null) {
328: Element root = doc.createElementNS(
329: J2SEProjectType.PROJECT_CONFIGURATION_NAMESPACE,
330: "root"); //NOI18N
331: root.setAttribute("id", "src.dir"); //NOI18N
332: sourceRoots.appendChild(root);
333: ep.setProperty("src.dir", srcRoot); // NOI18N
334: }
335: data.appendChild(sourceRoots);
336: Element testRoots = doc.createElementNS(
337: J2SEProjectType.PROJECT_CONFIGURATION_NAMESPACE,
338: "test-roots"); //NOI18N
339: if (testRoot != null) {
340: Element root = doc.createElementNS(
341: J2SEProjectType.PROJECT_CONFIGURATION_NAMESPACE,
342: "root"); //NOI18N
343: root.setAttribute("id", "test.src.dir"); //NOI18N
344: testRoots.appendChild(root);
345: ep.setProperty("test.src.dir", testRoot); // NOI18N
346: }
347: data.appendChild(testRoots);
348: h.putPrimaryConfigurationData(data, true);
349: ep.setProperty("dist.dir", "dist"); // NOI18N
350: ep.setComment("dist.dir", new String[] { "# "
351: + NbBundle.getMessage(J2SEProjectGenerator.class,
352: "COMMENT_dist.dir") }, false); // NOI18N
353: ep.setProperty("dist.jar", "${dist.dir}/"
354: + PropertyUtils.getUsablePropertyName(name) + ".jar"); // NOI18N
355: ep.setProperty("javac.classpath", new String[0]); // NOI18N
356: ep.setProperty("build.sysclasspath", "ignore"); // NOI18N
357: ep.setComment("build.sysclasspath", new String[] { "# "
358: + NbBundle.getMessage(J2SEProjectGenerator.class,
359: "COMMENT_build.sysclasspath") }, false); // NOI18N
360: ep.setProperty("run.classpath", new String[] { // NOI18N
361: "${javac.classpath}:", // NOI18N
362: "${build.classes.dir}", // NOI18N
363: });
364: ep.setProperty("debug.classpath", new String[] { // NOI18N
365: "${run.classpath}", // NOI18N
366: });
367: ep.setProperty("jar.compress", "false"); // NOI18N
368: if (!isLibrary) {
369: ep.setProperty("main.class", mainClass == null ? ""
370: : mainClass); // NOI18N
371: }
372:
373: ep.setProperty("javac.compilerargs", ""); // NOI18N
374: ep.setComment("javac.compilerargs", new String[] { "# "
375: + NbBundle.getMessage(J2SEProjectGenerator.class,
376: "COMMENT_javac.compilerargs"), // NOI18N
377: }, false);
378: SpecificationVersion sourceLevel = getDefaultSourceLevel();
379: ep.setProperty("javac.source", sourceLevel.toString()); // NOI18N
380: ep.setProperty("javac.target", sourceLevel.toString()); // NOI18N
381: ep.setProperty("javac.deprecation", "false"); // NOI18N
382: ep.setProperty("javac.test.classpath", new String[] { // NOI18N
383: "${javac.classpath}:", // NOI18N
384: "${build.classes.dir}:", // NOI18N
385: "${libs.junit.classpath}:", // NOI18N
386: "${libs.junit_4.classpath}", //NOI18N
387: });
388: ep.setProperty("run.test.classpath", new String[] { // NOI18N
389: "${javac.test.classpath}:", // NOI18N
390: "${build.test.classes.dir}", // NOI18N
391: });
392: ep.setProperty("debug.test.classpath", new String[] { // NOI18N
393: "${run.test.classpath}", // NOI18N
394: });
395:
396: ep.setProperty("build.generated.dir", "${build.dir}/generated"); // NOI18N
397: ep.setProperty("meta.inf.dir", "${src.dir}/META-INF"); // NOI18N
398:
399: ep.setProperty("build.dir", "build"); // NOI18N
400: ep.setComment("build.dir", new String[] { "# "
401: + NbBundle.getMessage(J2SEProjectGenerator.class,
402: "COMMENT_build.dir") }, false); // NOI18N
403: ep.setProperty("build.classes.dir", "${build.dir}/classes"); // NOI18N
404: ep.setProperty("build.test.classes.dir",
405: "${build.dir}/test/classes"); // NOI18N
406: ep.setProperty("build.test.results.dir",
407: "${build.dir}/test/results"); // NOI18N
408: ep.setProperty("build.classes.excludes", "**/*.java,**/*.form"); // NOI18N
409: ep.setProperty("dist.javadoc.dir", "${dist.dir}/javadoc"); // NOI18N
410: ep.setProperty("platform.active", "default_platform"); // NOI18N
411:
412: ep.setProperty("run.jvmargs", ""); // NOI18N
413: ep.setComment("run.jvmargs", new String[] {
414: "# "
415: + NbBundle.getMessage(
416: J2SEProjectGenerator.class,
417: "COMMENT_run.jvmargs"), // NOI18N
418: "# "
419: + NbBundle.getMessage(
420: J2SEProjectGenerator.class,
421: "COMMENT_run.jvmargs_2"), // NOI18N
422: "# "
423: + NbBundle.getMessage(
424: J2SEProjectGenerator.class,
425: "COMMENT_run.jvmargs_3"), // NOI18N
426: }, false);
427:
428: ep.setProperty(J2SEProjectProperties.JAVADOC_PRIVATE, "false"); // NOI18N
429: ep.setProperty(J2SEProjectProperties.JAVADOC_NO_TREE, "false"); // NOI18N
430: ep.setProperty(J2SEProjectProperties.JAVADOC_USE, "true"); // NOI18N
431: ep
432: .setProperty(J2SEProjectProperties.JAVADOC_NO_NAVBAR,
433: "false"); // NOI18N
434: ep.setProperty(J2SEProjectProperties.JAVADOC_NO_INDEX, "false"); // NOI18N
435: ep.setProperty(J2SEProjectProperties.JAVADOC_SPLIT_INDEX,
436: "true"); // NOI18N
437: ep.setProperty(J2SEProjectProperties.JAVADOC_AUTHOR, "false"); // NOI18N
438: ep.setProperty(J2SEProjectProperties.JAVADOC_VERSION, "false"); // NOI18N
439: ep.setProperty(J2SEProjectProperties.JAVADOC_WINDOW_TITLE, ""); // NOI18N
440: ep.setProperty(J2SEProjectProperties.JAVADOC_ENCODING, "${"
441: + J2SEProjectProperties.SOURCE_ENCODING + "}"); // NOI18N
442: ep.setProperty(J2SEProjectProperties.JAVADOC_ADDITIONALPARAM,
443: ""); // NOI18N
444: Charset enc = FileEncodingQuery.getDefaultEncoding();
445: ep.setProperty(J2SEProjectProperties.SOURCE_ENCODING, enc
446: .name());
447: if (manifestFile != null) {
448: ep.setProperty("manifest.file", manifestFile); // NOI18N
449: }
450: h.putProperties(AntProjectHelper.PROJECT_PROPERTIES_PATH, ep);
451: return h;
452: }
453:
454: private static void copyRequiredLibraries(AntProjectHelper h,
455: ReferenceHelper rh) throws IOException {
456: if (!h.isSharableProject()) {
457: return;
458: }
459: if (rh.getProjectLibraryManager().getLibrary("junit") == null) {
460: rh.copyLibrary(LibraryManager.getDefault().getLibrary(
461: "junit")); // NOI18N
462: }
463: if (rh.getProjectLibraryManager().getLibrary("junit_4") == null) {
464: rh.copyLibrary(LibraryManager.getDefault().getLibrary(
465: "junit_4")); // NOI18N
466: }
467: if (rh.getProjectLibraryManager().getLibrary("CopyLibs") == null) {
468: rh.copyLibrary(LibraryManager.getDefault().getLibrary(
469: "CopyLibs")); // NOI18N
470: }
471: }
472:
473: private static void createMainClass(String mainClassName,
474: FileObject srcFolder) throws IOException {
475:
476: int lastDotIdx = mainClassName.lastIndexOf('.');
477: String mName, pName;
478: if (lastDotIdx == -1) {
479: mName = mainClassName.trim();
480: pName = null;
481: } else {
482: mName = mainClassName.substring(lastDotIdx + 1).trim();
483: pName = mainClassName.substring(0, lastDotIdx).trim();
484: }
485:
486: if (mName.length() == 0) {
487: return;
488: }
489:
490: FileObject mainTemplate = Repository.getDefault()
491: .getDefaultFileSystem().findResource(
492: "Templates/Classes/Main.java"); // NOI18N
493:
494: if (mainTemplate == null) {
495: return; // Don't know the template
496: }
497:
498: DataObject mt = DataObject.find(mainTemplate);
499:
500: FileObject pkgFolder = srcFolder;
501: if (pName != null) {
502: String fName = pName.replace('.', '/'); // NOI18N
503: pkgFolder = FileUtil.createFolder(srcFolder, fName);
504: }
505: DataFolder pDf = DataFolder.findFolder(pkgFolder);
506: mt.createFromTemplate(pDf, mName);
507:
508: }
509:
510: //------------ Used by unit tests -------------------
511: private static SpecificationVersion defaultSourceLevel;
512:
513: private static SpecificationVersion getDefaultSourceLevel() {
514: if (defaultSourceLevel != null) {
515: return defaultSourceLevel;
516: } else {
517: JavaPlatform defaultPlatform = JavaPlatformManager
518: .getDefault().getDefaultPlatform();
519: SpecificationVersion v = defaultPlatform.getSpecification()
520: .getVersion();
521: if (v.equals(new SpecificationVersion("1.6"))
522: || v.equals(new SpecificationVersion("1.7"))) {
523: // #89131: these levels are not actually distinct from 1.5. - xxx not true, but may be acceptable to have 1.5 as default
524: return new SpecificationVersion("1.5");
525: } else {
526: return v;
527: }
528: }
529: }
530:
531: /**
532: * Unit test only method. Sets the default source level for tests
533: * where the default platform is not available.
534: * @param version the default source level set to project when it is created
535: *
536: */
537: public static void setDefaultSourceLevel(
538: SpecificationVersion version) {
539: defaultSourceLevel = version;
540: }
541: }
|