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.nbbuild;
043:
044: import java.util.*;
045: import org.apache.tools.ant.types.FileSet;
046: import org.apache.tools.ant.*;
047: import java.util.jar.*;
048: import java.io.*;
049: import java.util.zip.ZipEntry;
050:
051: /** Create XML files corresponding to the set of known modules
052: * without actually running the IDE.
053: * @author Jesse Glick
054: */
055: public class CreateModuleXML extends Task {
056:
057: private final List<FileSet> enabled = new ArrayList<FileSet>(1);
058: private final List<FileSet> disabled = new ArrayList<FileSet>(1);
059: private final List<FileSet> autoload = new ArrayList<FileSet>(1);
060: private final List<FileSet> eager = new ArrayList<FileSet>(1);
061: private final List<FileSet> hidden = new ArrayList<FileSet>(1);
062:
063: /** Add a set of module JARs that should be enabled.
064: */
065: public void addEnabled(FileSet fs) {
066: enabled.add(fs);
067: }
068:
069: /** Add a set of module JARs that should be disabled.
070: */
071: public void addDisabled(FileSet fs) {
072: disabled.add(fs);
073: }
074:
075: /** Add a set of module JARs that should be autoloads.
076: */
077: public void addAutoload(FileSet fs) {
078: autoload.add(fs);
079: }
080:
081: /** Add a set of module JARs that should be eager modules.
082: */
083: public void addEager(FileSet fs) {
084: eager.add(fs);
085: }
086:
087: /** Add a set of module JARs that should be hidden.
088: */
089: public void addHidden(FileSet fs) {
090: hidden.add(fs);
091: }
092:
093: private File xmldir = null;
094:
095: /** Set the modules directory where XML will be stored.
096: * Normally the system/Modules/ directory in an installation.
097: */
098: public void setXmldir(File f) {
099: xmldir = f;
100: }
101:
102: private List<String> enabledNames = new ArrayList<String>(50);
103: private List<String> disabledNames = new ArrayList<String>(10);
104: private List<String> autoloadNames = new ArrayList<String>(10);
105: private List<String> eagerNames = new ArrayList<String>(10);
106: private List<String> hiddenNames = new ArrayList<String>(10);
107:
108: public void execute() throws BuildException {
109: if (xmldir == null)
110: throw new BuildException("Must set xmldir attribute",
111: getLocation());
112: if (!xmldir.exists()) {
113: if (!xmldir.mkdirs())
114: throw new BuildException("Cannot create directory "
115: + xmldir, getLocation());
116: }
117: if (enabled.isEmpty() && disabled.isEmpty()
118: && autoload.isEmpty() && eager.isEmpty()
119: && hidden.isEmpty()) {
120: log("Warning: <createmodulexml> with no modules listed",
121: Project.MSG_WARN);
122: }
123: for (FileSet fs : enabled) {
124: scanModules(fs, true, false, false, false, enabledNames);
125: }
126: for (FileSet fs : disabled) {
127: scanModules(fs, false, false, false, false, disabledNames);
128: }
129: for (FileSet fs : autoload) {
130: scanModules(fs, false, true, false, false, autoloadNames);
131: }
132: for (FileSet fs : eager) {
133: scanModules(fs, false, false, true, false, eagerNames);
134: }
135: for (FileSet fs : hidden) {
136: scanModules(fs, false, false, false, true, hiddenNames);
137: }
138: Collections.sort(enabledNames);
139: Collections.sort(disabledNames);
140: Collections.sort(autoloadNames);
141: Collections.sort(eagerNames);
142: Collections.sort(hiddenNames);
143: if (!enabledNames.isEmpty()) {
144: log("Enabled modules: " + enabledNames);
145: }
146: if (!disabledNames.isEmpty()) {
147: log("Disabled modules: " + disabledNames);
148: }
149: if (!autoloadNames.isEmpty()) {
150: log("Autoload modules: " + autoloadNames);
151: }
152: if (!eagerNames.isEmpty()) {
153: log("Eager modules: " + eagerNames);
154: }
155: if (!hiddenNames.isEmpty()) {
156: log("Hidden modules: " + hiddenNames);
157: }
158: }
159:
160: private void scanModules(FileSet fs, boolean isEnabled,
161: boolean isAutoload, boolean isEager, boolean isHidden,
162: List<String> names) throws BuildException {
163: FileScanner scan = fs.getDirectoryScanner(getProject());
164: File dir = scan.getBasedir();
165: for (String kid : scan.getIncludedFiles()) {
166: File module = new File(dir, kid);
167: if (!module.exists())
168: throw new BuildException("Module file does not exist: "
169: + module, getLocation());
170: if (!module.getName().endsWith(".jar"))
171: throw new BuildException(
172: "Only *.jar may be listed, check the fileset: "
173: + module, getLocation());
174: try {
175: JarFile jar = new JarFile(module);
176: try {
177: Manifest m = jar.getManifest();
178: Attributes attr = m.getMainAttributes();
179: String codename = attr.getValue("OpenIDE-Module");
180: if (codename == null) {
181: throw new BuildException(
182: "Missing manifest tag OpenIDE-Module; "
183: + module + " is not a module",
184: getLocation());
185: }
186: if (codename.endsWith(" ")
187: || codename.endsWith("\t")) { // #62887
188: throw new BuildException(
189: "Illegal trailing space in OpenIDE-Module value from "
190: + module, getLocation());
191: }
192: int idx = codename.lastIndexOf('/');
193: String codenamebase;
194: int rel;
195: if (idx == -1) {
196: codenamebase = codename;
197: rel = -1;
198: } else {
199: codenamebase = codename.substring(0, idx);
200: try {
201: rel = Integer.parseInt(codename
202: .substring(idx + 1));
203: } catch (NumberFormatException e) {
204: throw new BuildException(
205: "Invalid OpenIDE-Module '"
206: + codename + "' in "
207: + module, getLocation());
208: }
209: }
210: File xml = new File(xmldir, codenamebase.replace(
211: '.', '-')
212: + ".xml");
213: if (xml.exists()) {
214: // XXX should check that the old file actually matches what we would have written
215: log("Will not overwrite " + xml
216: + "; skipping...", Project.MSG_VERBOSE);
217: continue;
218: }
219: String displayname = attr
220: .getValue("OpenIDE-Module-Name");
221: if (displayname == null) {
222: String bundle = attr
223: .getValue("OpenIDE-Module-Localizing-Bundle");
224: if (bundle != null) {
225: // Display name actually found in a bundle, not manifest.
226: ZipEntry entry = jar.getEntry(bundle);
227: InputStream is;
228: if (entry != null) {
229: is = jar.getInputStream(entry);
230: } else {
231: File moduleloc = new File(new File(
232: module.getParentFile(),
233: "locale"), module.getName());
234: if (!moduleloc.isFile()) {
235: throw new BuildException(
236: "Expecting localizing bundle: "
237: + bundle + " in: "
238: + module);
239: }
240: JarFile jarloc = new JarFile(moduleloc);
241: try {
242: ZipEntry entry2 = jarloc
243: .getEntry(bundle);
244: if (entry2 == null) {
245: throw new BuildException(
246: "Expecting localizing bundle: "
247: + bundle
248: + " in: "
249: + module);
250: }
251: is = jarloc.getInputStream(entry2);
252: } finally {
253: jarloc.close();
254: }
255: }
256: try {
257: Properties p = new Properties();
258: p.load(is);
259: displayname = p
260: .getProperty("OpenIDE-Module-Name");
261: } finally {
262: is.close();
263: }
264: }
265: }
266: if (displayname == null)
267: displayname = codename;
268: names.add(displayname);
269: String spec = attr
270: .getValue("OpenIDE-Module-Specification-Version");
271:
272: if (isHidden) {
273: File h = new File(xml.getParentFile(), xml
274: .getName()
275: + "_hidden");
276: h.createNewFile();
277: }
278:
279: if (isEager || isAutoload || isEnabled) {
280: OutputStream os = new FileOutputStream(xml);
281: try {
282: PrintWriter pw = new PrintWriter(
283: new OutputStreamWriter(os, "UTF-8"));
284: // Please make sure formatting matches what the IDE actually spits
285: // out; it could matter.
286: pw
287: .println("<?xml version=\"1.0\" encoding=\"UTF-8\"?>");
288: pw
289: .println("<!DOCTYPE module PUBLIC \"-//NetBeans//DTD Module Status 1.0//EN\"");
290: pw
291: .println(" \"http://www.netbeans.org/dtds/module-status-1_0.dtd\">");
292: pw.println("<module name=\"" + codenamebase
293: + "\">");
294: pw.println(" <param name=\"autoload\">"
295: + isAutoload + "</param>");
296: pw.println(" <param name=\"eager\">"
297: + isEager + "</param>");
298: if (!isAutoload && !isEager) {
299: pw
300: .println(" <param name=\"enabled\">"
301: + isEnabled
302: + "</param>");
303: }
304: pw.println(" <param name=\"jar\">"
305: + kid.replace(File.separatorChar,
306: '/') + "</param>");
307: if (rel != -1) {
308: pw
309: .println(" <param name=\"release\">"
310: + rel + "</param>");
311: }
312: pw
313: .println(" <param name=\"reloadable\">false</param>");
314: if (spec != null) {
315: pw
316: .println(" <param name=\"specversion\">"
317: + spec + "</param>");
318: }
319: pw.println("</module>");
320: pw.flush();
321: pw.close();
322: } finally {
323: os.close();
324: }
325: }
326: } finally {
327: jar.close();
328: }
329: } catch (IOException ioe) {
330: throw new BuildException("Caught while processing "
331: + module + ": " + ioe, ioe, getLocation());
332: }
333: }
334: }
335:
336: }
|