0001: /*
0002: * DO NOT ALTER OR REMOVE COPYRIGHT NOTICES OR THIS HEADER.
0003: *
0004: * Copyright 1997-2007 Sun Microsystems, Inc. All rights reserved.
0005: *
0006: * The contents of this file are subject to the terms of either the GNU
0007: * General Public License Version 2 only ("GPL") or the Common
0008: * Development and Distribution License("CDDL") (collectively, the
0009: * "License"). You may not use this file except in compliance with the
0010: * License. You can obtain a copy of the License at
0011: * http://www.netbeans.org/cddl-gplv2.html
0012: * or nbbuild/licenses/CDDL-GPL-2-CP. See the License for the
0013: * specific language governing permissions and limitations under the
0014: * License. When distributing the software, include this License Header
0015: * Notice in each file and include the License file at
0016: * nbbuild/licenses/CDDL-GPL-2-CP. Sun designates this
0017: * particular file as subject to the "Classpath" exception as provided
0018: * by Sun in the GPL Version 2 section of the License file that
0019: * accompanied this code. If applicable, add the following below the
0020: * License Header, with the fields enclosed by brackets [] replaced by
0021: * your own identifying information:
0022: * "Portions Copyrighted [year] [name of copyright owner]"
0023: *
0024: * Contributor(s):
0025: *
0026: * The Original Software is NetBeans. The Initial Developer of the Original
0027: * Software is Sun Microsystems, Inc. Portions Copyright 1997-2006 Sun
0028: * Microsystems, Inc. All Rights Reserved.
0029: *
0030: * If you wish your version of this file to be governed by only the CDDL
0031: * or only the GPL Version 2, indicate your decision by adding
0032: * "[Contributor] elects to include this software in this distribution
0033: * under the [CDDL or GPL Version 2] license." If you do not indicate a
0034: * single choice of license, a recipient has the option to distribute
0035: * your version of this file under either the CDDL, the GPL Version 2 or
0036: * to extend the choice of license to its licensees as provided above.
0037: * However, if you add GPL Version 2 code and therefore, elected the GPL
0038: * Version 2 license, then the option applies only if the new code is
0039: * made subject to such option by the copyright holder.
0040: */
0041:
0042: package org.netbeans.nbbuild;
0043:
0044: import java.io.File;
0045: import java.io.FileInputStream;
0046: import java.io.FileOutputStream;
0047: import java.io.IOException;
0048: import java.io.InputStream;
0049: import java.io.InputStreamReader;
0050: import java.io.OutputStream;
0051: import java.io.OutputStreamWriter;
0052: import java.io.PrintWriter;
0053: import java.io.Reader;
0054: import java.net.URI;
0055: import java.util.ArrayList;
0056: import java.util.Collections;
0057: import java.util.Enumeration;
0058: import java.util.Iterator;
0059: import java.util.List;
0060: import java.util.Map;
0061: import java.util.Properties;
0062: import java.util.StringTokenizer;
0063: import java.util.Vector;
0064: import java.util.jar.Attributes;
0065: import java.util.jar.JarFile;
0066: import java.util.jar.Manifest;
0067: import java.util.zip.ZipEntry;
0068: import java.util.zip.ZipFile;
0069: import org.apache.tools.ant.BuildException;
0070: import org.apache.tools.ant.Project;
0071: import org.apache.tools.ant.taskdefs.Jar;
0072: import org.apache.tools.ant.taskdefs.MatchingTask;
0073: import org.apache.tools.ant.taskdefs.SignJar;
0074: import org.apache.tools.ant.types.FileSet;
0075:
0076: /** Makes a localized <code>.nbm</code> (<b>N</b>et<b>B</b>eans <b>M</b>odule) file.
0077: * This version is temporary, intended to be used only until
0078: * the functionality added to this version since rev 1.29
0079: * of MakeNBM.java can be added into MakeNBM.java
0080: *
0081: * @author Jerry Huth (email: jerry@solidstep.com)
0082: */
0083: public class MakeLNBM extends MatchingTask {
0084:
0085: /** The same syntax may be used for either <samp><license></samp> or
0086: * <samp><description></samp> subelements.
0087: * <p>By setting the property <code>makenbm.nocdata</code> to <code>true</code>,
0088: * you can avoid using XML <code>CDATA</code> (for compatibility with older versions
0089: * of Auto Update which could not handle it).
0090: */
0091: public class Blurb {
0092: /** You may embed a <samp><file></samp> element inside the blurb.
0093: * If there is text on either side of it, that will be separated
0094: * with a line of dashes automatically.
0095: * But use nested <samp><text></samp> for this purpose.
0096: */
0097: public class FileInsert {
0098: /** File location. */
0099: public void setLocation(File file) throws BuildException {
0100: log("Including contents of " + file,
0101: Project.MSG_VERBOSE);
0102: long lmod = file.lastModified();
0103: if (lmod > mostRecentInput)
0104: mostRecentInput = lmod;
0105: addSeparator();
0106: try {
0107: InputStream is = new FileInputStream(file);
0108: try {
0109: Reader r = new InputStreamReader(is, "UTF-8"); //NOI18N
0110: char[] buf = new char[4096];
0111: int len;
0112: while ((len = r.read(buf)) != -1)
0113: text.append(buf, 0, len);
0114: } finally {
0115: is.close();
0116: }
0117: } catch (IOException ioe) {
0118: throw new BuildException(
0119: "Exception reading blurb from " + file,
0120: ioe, getLocation());
0121: }
0122: }
0123: }
0124:
0125: private StringBuffer text = new StringBuffer();
0126: private String name = null;
0127:
0128: /** There may be freeform text inside the element. Prefer to use nested elements. */
0129: public void addText(String t) {
0130: addSeparator();
0131: // Strips indentation. Needed because of common style:
0132: // <description>
0133: // Some text here.
0134: // And another line.
0135: // </description>
0136: t = getProject().replaceProperties(t.trim());
0137: int min = Integer.MAX_VALUE;
0138: StringTokenizer tok = new StringTokenizer(t, "\n"); //NOI18N
0139: boolean first = true;
0140: while (tok.hasMoreTokens()) {
0141: String line = tok.nextToken();
0142: if (first) {
0143: first = false;
0144: } else {
0145: int i;
0146: for (i = 0; i < line.length()
0147: && Character.isWhitespace(line.charAt(i)); i++)
0148: ;
0149: if (i < min)
0150: min = i;
0151: }
0152: }
0153: if (min == 0) {
0154: text.append(t);
0155: } else {
0156: tok = new StringTokenizer(t, "\n"); //NOI18N
0157: first = true;
0158: while (tok.hasMoreTokens()) {
0159: String line = tok.nextToken();
0160: if (first) {
0161: first = false;
0162: } else {
0163: text.append('\n'); //NOI18N
0164: line = line.substring(min);
0165: }
0166: text.append(line);
0167: }
0168: }
0169: }
0170:
0171: /** Contents of a file to include. */
0172: public FileInsert createFile() {
0173: return new FileInsert();
0174: }
0175:
0176: /** Text to include literally. */
0177: public class Text {
0178: public void addText(String t) {
0179: Blurb.this .addText(t);
0180: }
0181: }
0182:
0183: // At least on Ant 1.3, mixed content does not work: all the text is added
0184: // first, then all the file inserts. Need to use subelements to be sure.
0185: /** Include nested literal text. */
0186: public Text createText() {
0187: return new Text();
0188: }
0189:
0190: private void addSeparator() {
0191: if (text.length() > 0) {
0192: // some sort of separator
0193: if (text.charAt(text.length() - 1) != '\n') //NOI18N
0194: text.append('\n'); //NOI18N
0195: text
0196: .append("-----------------------------------------------------\n"); //NOI18N
0197: }
0198: }
0199:
0200: public String getText() {
0201: String nocdata = getProject()
0202: .getProperty("makenbm.nocdata"); //NOI18N
0203: if (nocdata != null && Project.toBoolean(nocdata)) {
0204: return xmlEscape(text.toString());
0205: } else {
0206: return "<![CDATA[" + text.toString() + "]]>"; //NOI18N
0207: }
0208: }
0209:
0210: /** You can either set a name for the blurb, or using the <code>file</code> attribute does this.
0211: * The name is mandatory for licenses, as this identifies the license in
0212: * an update description.
0213: */
0214: public void setName(String name) {
0215: this .name = name;
0216: }
0217:
0218: public String getName() {
0219: return name;
0220: }
0221:
0222: /** Include a file (and set the license name according to its basename). */
0223: public void setFile(File file) {
0224: // This actually adds the text and so on:
0225: new FileInsert().setLocation(file);
0226: // Default for the name too, as a convenience.
0227: if (name == null)
0228: name = file.getName();
0229: }
0230: }
0231:
0232: public class ExternalPackage {
0233: String name = null;
0234: String targetName = null;
0235: String startUrl = null;
0236: String description = null;
0237:
0238: public void setName(String n) {
0239: this .name = n;
0240: }
0241:
0242: public void setTargetName(String t) {
0243: this .targetName = t;
0244: }
0245:
0246: public void setStartURL(String u) {
0247: this .startUrl = u;
0248: }
0249:
0250: public void setDescription(String d) {
0251: this .description = d;
0252: }
0253:
0254: }
0255:
0256: // Similar to org.openide.xml.XMLUtil methods.
0257: private static String xmlEscape(String s) {
0258: int max = s.length();
0259: StringBuffer s2 = new StringBuffer((int) (max * 1.1 + 1));
0260: for (int i = 0; i < max; i++) {
0261: char c = s.charAt(i);
0262: switch (c) {
0263: case '<': //NOI18N
0264: s2.append("<"); //NOI18N
0265: break;
0266: case '>': //NOI18N
0267: s2.append(">"); //NOI18N
0268: break;
0269: case '&': //NOI18N
0270: s2.append("&"); //NOI18N
0271: break;
0272: case '"': //NOI18N
0273: s2.append("""); //NOI18N
0274: break;
0275: default:
0276: s2.append(c);
0277: break;
0278: }
0279: }
0280: return s2.toString();
0281: }
0282:
0283: /** <samp><signature></samp> subelement for signing the NBM. */
0284: public/*static*/class Signature {
0285: public File keystore;
0286: public String storepass, alias;
0287:
0288: /** Path to the keystore (private key). */
0289: public void setKeystore(File f) {
0290: keystore = f;
0291: }
0292:
0293: /** Password for the keystore.
0294: * If a question mark (<samp>?</samp>), the NBM will not be signed
0295: * and a warning will be printed.
0296: */
0297: public void setStorepass(String s) {
0298: storepass = s;
0299: }
0300:
0301: /** Alias for the private key. */
0302: public void setAlias(String s) {
0303: alias = s;
0304: }
0305: }
0306:
0307: private File file = null;
0308: private File topdir = null;
0309: private File manifest = null;
0310: /** see #13850 for explanation */
0311: private File module = null;
0312: private String homepage = null;
0313: private String distribution = null;
0314: private String needsrestart = null;
0315: private Blurb license = null;
0316: private Blurb description = null;
0317: private Blurb notification = null;
0318: private Signature signature = null;
0319: private long mostRecentInput = 0L;
0320: private boolean isStandardInclude = true;
0321: private Vector<ExternalPackage> externalPackages = null;
0322: private boolean manOrModReq = true;
0323: private boolean manOrModReqSet = false;
0324: private String langCode = null;
0325: private String brandingCode = null;
0326: private String modInfo = null;
0327: private File locBundle = null; // Localizing Bundle
0328:
0329: /** Include netbeans directory - default is true */
0330: public void setIsStandardInclude(boolean isStandardInclude) {
0331: this .isStandardInclude = isStandardInclude;
0332: }
0333:
0334: /** Name of resulting NBM file. */
0335: public void setFile(File file) {
0336: this .file = file;
0337: }
0338:
0339: /** Top directory.
0340: * Expected to contain a subdirectory <samp>netbeans/</samp> with the
0341: * desired contents of the NBM.
0342: * Will create <samp>Info/info.xml</samp> with metadata.
0343: */
0344: public void setTopdir(File topdir) {
0345: this .topdir = topdir;
0346: }
0347:
0348: /** Module manifest needed for versioning.
0349: * @deprecated Use {@link #setModule} instead.
0350: */
0351: @Deprecated
0352: public void setManifest(File manifest) {
0353: this .manifest = manifest;
0354: long lmod = manifest.lastModified();
0355: if (lmod > mostRecentInput)
0356: mostRecentInput = lmod;
0357: log(
0358: getLocation()
0359: + "The 'manifest' attr on <makenbm> is deprecated, please use 'module' instead",
0360: Project.MSG_WARN);
0361: }
0362:
0363: /** Module JAR needed for generating the info file.
0364: * Information may be gotten either from its manifest,
0365: * or if it declares OpenIDE-Module-Localizing-Bundle in its
0366: * manifest, from that bundle.
0367: * The base locale variant, if any, is also checked if necessary
0368: * for the named bundle.
0369: * Currently no other locale variants of the module are examined;
0370: * the information is available but there is no published specification
0371: * of what the resulting variant NBMs (or variant information within
0372: * the NBM) should look like.
0373: */
0374: public void setModule(File module) {
0375: this .module = module;
0376: // mostRecentInput updated below...
0377: }
0378:
0379: /** URL to a home page describing the module. */
0380: public void setHomepage(String homepage) {
0381: this .homepage = homepage;
0382: }
0383:
0384: /** Does module need IDE restart to be installed? */
0385: public void setNeedsrestart(String needsrestart) {
0386: this .needsrestart = needsrestart;
0387: }
0388:
0389: /** URL where this NBM file is expected to be downloadable from. */
0390: public void setDistribution(String distribution)
0391: throws BuildException {
0392: if (distribution.startsWith("http://")) { //NOI18N
0393: this .distribution = distribution;
0394: } else if (!(distribution.equals(""))) {
0395: // workaround for typical bug in build script
0396: this .distribution = "http://" + distribution; //NOI18N
0397: } else {
0398: throw new BuildException(
0399: "Distribution URL is empty, check build.xml file",
0400: getLocation());
0401: }
0402: // check the URL
0403: try {
0404: URI uri = java.net.URI.create(this .distribution);
0405: } catch (IllegalArgumentException ile) {
0406: throw new BuildException("Distribution URL \""
0407: + this .distribution + "\" is not a valid URI", ile,
0408: getLocation());
0409: }
0410: }
0411:
0412: public Blurb createLicense() {
0413: return (license = new Blurb());
0414: }
0415:
0416: public Blurb createNotification() {
0417: return (notification = new Blurb());
0418: }
0419:
0420: public Blurb createDescription() {
0421: log(
0422: getLocation()
0423: + "The <description> subelement in <makenbm> is deprecated except for emergency patches, please ensure your module has an OpenIDE-Module-Long-Description instead",
0424: Project.MSG_WARN);
0425: return (description = new Blurb());
0426: }
0427:
0428: public Signature createSignature() {
0429: return (signature = new Signature());
0430: }
0431:
0432: public ExternalPackage createExternalPackage() {
0433: ExternalPackage externalPackage = new ExternalPackage();
0434: if (externalPackages == null)
0435: externalPackages = new Vector<ExternalPackage>();
0436: externalPackages.add(externalPackage);
0437: return externalPackage;
0438: }
0439:
0440: public void execute() throws BuildException {
0441: if (file == null) {
0442: throw new BuildException("must set file for makenbm",
0443: getLocation());
0444: }
0445: if (manifest == null && module == null && reqManOrMod()) {
0446: throw new BuildException("must set module for makenbm",
0447: getLocation());
0448: }
0449: if (manifest != null && module != null) {
0450: throw new BuildException(
0451: "cannot set both manifest and module for makenbm",
0452: getLocation());
0453: }
0454: // Will create a file Info/info.xml to be stored alongside netbeans/ contents.
0455: File infodir = new File(topdir, "Info"); //NOI18N
0456: infodir.mkdirs();
0457: File infofile = new File(infodir, "info.xml"); //NOI18N
0458: if (infofile.exists()) {
0459: infofile.delete();
0460: }
0461: Attributes attr = null;
0462: if (module != null) {
0463: // The normal case; read attributes from its manifest and maybe bundle.
0464: long mMod = module.lastModified();
0465: if (mostRecentInput < mMod)
0466: mostRecentInput = mMod;
0467: try {
0468: JarFile modulejar = new JarFile(module);
0469: try {
0470: attr = modulejar.getManifest().getMainAttributes();
0471: String bundlename = attr
0472: .getValue("OpenIDE-Module-Localizing-Bundle"); //NOI18N
0473: if (bundlename != null) {
0474: Properties p = new Properties();
0475: ZipEntry bundleentry = modulejar
0476: .getEntry(bundlename);
0477: if (bundleentry != null) {
0478: InputStream is = modulejar
0479: .getInputStream(bundleentry);
0480: try {
0481: p.load(is);
0482: } finally {
0483: is.close();
0484: }
0485: } else {
0486: // Not found in main JAR, check locale variant JAR.
0487: File variant = new File(new File(module
0488: .getParentFile(), "locale"), module
0489: .getName()); //NOI18N
0490: if (!variant.isFile())
0491: throw new BuildException(bundlename
0492: + " not found in " + module,
0493: getLocation());
0494: long vmMod = variant.lastModified();
0495: if (mostRecentInput < vmMod)
0496: mostRecentInput = vmMod;
0497: ZipFile variantjar = new ZipFile(variant);
0498: try {
0499: bundleentry = variantjar
0500: .getEntry(bundlename);
0501: if (bundleentry == null)
0502: throw new BuildException(bundlename
0503: + " not found in " + module
0504: + " nor in " + variant,
0505: getLocation());
0506: InputStream is = variantjar
0507: .getInputStream(bundleentry);
0508: try {
0509: p.load(is);
0510: } finally {
0511: is.close();
0512: }
0513: } finally {
0514: variantjar.close();
0515: }
0516: }
0517: // Now pick up attributes from the bundle.
0518: Iterator it = p.entrySet().iterator();
0519: while (it.hasNext()) {
0520: Map.Entry entry = (Map.Entry) it.next();
0521: String name = (String) entry.getKey();
0522: if (!name.startsWith("OpenIDE-Module-"))
0523: continue; //NOI18N
0524: attr.putValue(name, (String) entry
0525: .getValue());
0526: }
0527: } // else all loc attrs in main manifest, OK
0528: } finally {
0529: modulejar.close();
0530: }
0531: } catch (IOException ioe) {
0532: throw new BuildException("exception while reading "
0533: + module, ioe, getLocation());
0534: }
0535: } // else we will read attr later if info file is out of date
0536: boolean skipInfo = false;
0537: if (infofile.exists()) {
0538: // Check for up-to-date w.r.t. manifest and maybe license file.
0539: long iMod = infofile.lastModified();
0540: if (mostRecentInput < iMod)
0541: skipInfo = true;
0542: }
0543: if (!skipInfo) {
0544: log("Creating NBM info file " + infofile);
0545: if (manifest != null) {
0546: // Read module manifest for main attributes.
0547: try {
0548: InputStream manifestStream = new FileInputStream(
0549: manifest);
0550: try {
0551: attr = new Manifest(manifestStream)
0552: .getMainAttributes();
0553: } finally {
0554: manifestStream.close();
0555: }
0556: } catch (IOException e) {
0557: throw new BuildException(
0558: "exception when reading manifest "
0559: + manifest, e, getLocation());
0560: }
0561: } // else we read attr before
0562: try {
0563: OutputStream infoStream = new FileOutputStream(infofile);
0564: try {
0565: PrintWriter ps = new PrintWriter(
0566: new OutputStreamWriter(infoStream, "UTF-8"));
0567: // Begin writing XML.
0568: ps
0569: .println("<?xml version=\"1.0\" encoding=\"UTF-8\"?>"); //NOI18N
0570: ps
0571: .println("<!DOCTYPE module PUBLIC \"-//NetBeans//DTD Autoupdate Module Info 2.4//EN\" \"http://www.netbeans.org/dtds/autoupdate-info-2_4.dtd\">"); //NOI18N
0572: if (attr != null) {
0573: String codenamebase = attr
0574: .getValue("OpenIDE-Module"); //NOI18N
0575: if (codenamebase == null)
0576: throw new BuildException(
0577: "invalid manifest, does not contain OpenIDE-Module",
0578: getLocation());
0579: // Strip major release number if any.
0580: codenamebase = getCodenameBase(codenamebase);
0581: ps.println("<module codenamebase=\""
0582: + xmlEscape(codenamebase) + "\""); //NOI18N
0583: } else {
0584: ps.print("<module "); //NOI18N
0585: if (modInfo != null
0586: && !modInfo.trim().equals("")) {
0587: String codenamebase = getCodenameBase(modInfo);
0588: ps.println("codenamebase=\""
0589: + xmlEscape(codenamebase) + "\""); //NOI18N
0590: } else {
0591: ps.println(""); //NOI18N
0592: }
0593: }
0594: if (homepage != null)
0595: ps.println(" homepage=\""
0596: + xmlEscape(homepage) + "\""); //NOI18N
0597: if (distribution != null) {
0598: ps.println(" distribution=\""
0599: + xmlEscape(distribution) + "\""); //NOI18N
0600: } else {
0601: throw new BuildException(
0602: "NBM distribution URL is not set",
0603: getLocation());
0604: }
0605: // Here we only write a name for the license.
0606: if (license != null) {
0607: String name = license.getName();
0608: if (name == null)
0609: throw new BuildException(
0610: "Every license must have a name or file attribute",
0611: getLocation());
0612: ps.println(" license=\""
0613: + xmlEscape(name) + "\""); //NOI18N
0614: }
0615: ps.println(" downloadsize=\"0\""); //NOI18N
0616: if (needsrestart != null)
0617: ps.println(" needsrestart=\""
0618: + xmlEscape(needsrestart) + "\""); //NOI18N
0619: ps.println(">"); //NOI18N
0620: if (description != null) {
0621: ps.print(" <description>"); //NOI18N
0622: ps.print(description.getText());
0623: ps.println("</description>"); //NOI18N
0624: }
0625:
0626: // Write manifest attributes.
0627: if (attr != null) {
0628: ps.print(" <manifest "); //NOI18N
0629: boolean firstline = true;
0630: List<String> attrNames = new ArrayList<String>(
0631: attr.size());
0632: Iterator<Object> it = attr.keySet().iterator();
0633: while (it.hasNext()) {
0634: attrNames.add(((Attributes.Name) it.next())
0635: .toString());
0636: }
0637: Collections.sort(attrNames);
0638: for (String name : attrNames) {
0639: // Ignore irrelevant attributes (cf. www/www/dtds/autoupdate-catalog-2_0.dtd
0640: // and www/www/dtds/autoupdate-info-1_0.dtd):
0641: if (!name.startsWith("OpenIDE-Module"))
0642: continue; //NOI18N
0643: if (name
0644: .equals("OpenIDE-Module-Localizing-Bundle"))
0645: continue; //NOI18N
0646: if (name.equals("OpenIDE-Module-Install"))
0647: continue; //NOI18N
0648: if (name.equals("OpenIDE-Module-Layer"))
0649: continue; //NOI18N
0650: if (name
0651: .equals("OpenIDE-Module-Description"))
0652: continue; //NOI18N
0653: if (name
0654: .equals("OpenIDE-Module-Package-Dependency-Message"))
0655: continue; //NOI18N
0656: if (name
0657: .equals("OpenIDE-Module-Public-Packages"))
0658: continue; //NOI18N
0659: if (firstline)
0660: firstline = false;
0661: else
0662: ps.print(" "); //NOI18N
0663: ps.println(name + "=\""
0664: + xmlEscape(attr.getValue(name))
0665: + "\""); //NOI18N
0666: }
0667: ps.println(" />"); //NOI18N
0668: } else if (modInfo != null
0669: && !modInfo.trim().equals("")) { //NOI18N
0670: String specver, majorver;
0671:
0672: // Write the l10n tag and lang/branding codes. //
0673: ps.println(" <l10n "); //NOI18N
0674: if (langCode != null
0675: && !langCode.trim().equals("")) { //NOI18N
0676: ps.println(" langcode=\""
0677: + xmlEscape(langCode) + "\""); //NOI18N
0678: }
0679: if (brandingCode != null
0680: && !brandingCode.trim().equals("")) { //NOI18N
0681: ps.println(" brandingcode=\""
0682: + xmlEscape(brandingCode) + "\""); //NOI18N
0683: }
0684:
0685: // Write the major version if possible. //
0686: majorver = getMajorVer(modInfo);
0687: if (majorver != null
0688: && !majorver.trim().equals("")) { //NOI18N
0689: ps
0690: .println(" module_major_version=\""
0691: + xmlEscape(majorver)
0692: + "\""); //NOI18N
0693: }
0694:
0695: // Write the spec version if possible. //
0696: specver = getSpecVer(modInfo);
0697: if (specver != null
0698: && !specver.trim().equals("")) { //NOI18N
0699: ps.println(" module_spec_version=\""
0700: + xmlEscape(specver) + "\""); //NOI18N
0701: }
0702:
0703: // Read localizing bundle and write relevant attr's. //
0704: if (locBundle != null) {
0705: writeLocBundleAttrs(ps);
0706: }
0707:
0708: ps.println(" />"); //NOI18N
0709: }
0710:
0711: // Maybe write out license text.
0712: if (license != null) {
0713: ps.print(" <license name=\""
0714: + xmlEscape(license.getName()) + "\">"); //NOI18N
0715: ps.print(license.getText());
0716: ps.println("</license>"); //NOI18N
0717: }
0718: if (notification != null) {
0719: ps.print(" <module_notification>"); //NOI18N
0720: ps.print(notification.getText());
0721: ps.println("</module_notification>"); //NOI18N
0722: }
0723: if (externalPackages != null) {
0724: Enumeration<ExternalPackage> exp = externalPackages
0725: .elements();
0726: while (exp.hasMoreElements()) {
0727: ExternalPackage externalPackage = exp
0728: .nextElement();
0729: if (externalPackage.name == null
0730: || externalPackage.targetName == null
0731: || externalPackage.startUrl == null)
0732: throw new BuildException(
0733: "Must define name, targetname, starturl for external package");
0734: ps.print(" <external_package "); //NOI18N
0735: ps.print("name=\""
0736: + xmlEscape(externalPackage.name)
0737: + "\" "); //NOI18N
0738: ps
0739: .print("target_name=\""
0740: + xmlEscape(externalPackage.targetName)
0741: + "\" "); //NOI18N
0742: ps
0743: .print("start_url=\""
0744: + xmlEscape(externalPackage.startUrl)
0745: + "\""); //NOI18N
0746: if (externalPackage.description != null)
0747: ps
0748: .print(" description=\""
0749: + xmlEscape(externalPackage.description)
0750: + "\""); //NOI18N
0751: ps.println("/>"); //NOI18N
0752: }
0753: }
0754: ps.println("</module>"); //NOI18N
0755: ps.flush();
0756: } finally {
0757: infoStream.close();
0758: }
0759: } catch (IOException e) {
0760: throw new BuildException(
0761: "exception when creating Info/info.xml", e,
0762: getLocation());
0763: }
0764: }
0765:
0766: // JAR it all up together.
0767: long jarModified = file.lastModified(); // may be 0
0768: //log ("Ensuring existence of NBM file " + file);
0769: Jar jar = (Jar) getProject().createTask("jar"); //NOI18N
0770: jar.setDestFile(file);
0771: //jar.setBasedir (topdir.getAbsolutePath ());
0772: jar.setCompress(true);
0773: //jar.createInclude ().setName ("netbeans/"); //NOI18N
0774: //jar.createInclude ().setName ("Info/info.xml"); //NOI18N
0775: jar.addFileset(getFileSet());
0776: jar.setLocation(getLocation());
0777: jar.init();
0778: jar.execute();
0779: // Maybe sign it.
0780: if (signature != null && file.lastModified() != jarModified) {
0781: if (signature.keystore == null)
0782: throw new BuildException(
0783: "must define keystore attribute on <signature/>");
0784: if (signature.storepass == null)
0785: throw new BuildException(
0786: "must define storepass attribute on <signature/>");
0787: if (signature.alias == null)
0788: throw new BuildException(
0789: "must define alias attribute on <signature/>");
0790: if (signature.storepass.equals("?")
0791: || !signature.keystore.exists()) {
0792: log(
0793: "Not signing NBM file "
0794: + file
0795: + "; no stored-key password provided or keystore ("
0796: + signature.keystore.toString()
0797: + ") doesn't exist", Project.MSG_WARN);
0798: } else {
0799: log("Signing NBM file " + file);
0800: SignJar signjar = (SignJar) getProject().createTask(
0801: "signjar"); //NOI18N
0802: //I have to use Reflection API, because there was changed API in ANT1.5
0803: try {
0804: try {
0805: Class[] paramsT = { String.class };
0806: Object[] paramsV1 = { signature.keystore
0807: .getAbsolutePath() };
0808: Object[] paramsV2 = { file.getAbsolutePath() };
0809: signjar.getClass().getDeclaredMethod(
0810: "setKeystore", paramsT).invoke(signjar,
0811: paramsV1); //NOI18N
0812: signjar.getClass().getDeclaredMethod("setJar",
0813: paramsT).invoke(signjar, paramsV2); //NOI18N
0814: } catch (NoSuchMethodException ex1) {
0815: //Probably ANT 1.5
0816: try {
0817: Class[] paramsT = { File.class };
0818: Object[] paramsV1 = { signature.keystore };
0819: Object[] paramsV2 = { file };
0820: signjar.getClass().getDeclaredMethod(
0821: "setKeystore", paramsT).invoke(
0822: signjar, paramsV1); //NOI18N
0823: signjar.getClass().getDeclaredMethod(
0824: "setJar", paramsT).invoke(signjar,
0825: paramsV2); //NOI18N
0826: } catch (NoSuchMethodException ex2) {
0827: //Probably ANT1.5.3
0828: try {
0829: Class[] paramsT1 = { File.class };
0830: Class[] paramsT2 = { String.class };
0831: Object[] paramsV1 = { signature.keystore
0832: .getAbsolutePath() };
0833: Object[] paramsV2 = { file };
0834: signjar.getClass().getDeclaredMethod(
0835: "setKeystore", paramsT2)
0836: .invoke(signjar, paramsV1); //NOI18N
0837: signjar.getClass().getDeclaredMethod(
0838: "setJar", paramsT1).invoke(
0839: signjar, paramsV2); //NOI18N
0840: } catch (NoSuchMethodException ex3) {
0841: throw new BuildException(
0842: "Unknown Ant version, only Ant 1.6.5+ is currently supported.");
0843: }
0844: }
0845: }
0846: } catch (IllegalAccessException ex4) {
0847: throw new BuildException(ex4);
0848: } catch (java.lang.reflect.InvocationTargetException ex5) {
0849: throw new BuildException(ex5);
0850: }
0851: signjar.setStorepass(signature.storepass);
0852: signjar.setAlias(signature.alias);
0853: signjar.setLocation(getLocation());
0854: signjar.init();
0855: signjar.execute();
0856: }
0857: }
0858: }
0859:
0860: // Reflection access from MakeListOfNBM:
0861:
0862: public FileSet getFileSet() {
0863: FileSet fs = fileset; //makes in apperance to excludes and includes files defined in XML
0864: fs.setDir(topdir);
0865:
0866: if (isStandardInclude) {
0867: fs.createInclude().setName("netbeans/"); //NOI18N
0868: fs.createExclude()
0869: .setName("netbeans/update_tracking/*.xml"); //NOI18N
0870: }
0871:
0872: fs.createInclude().setName("Info/info.xml"); //NOI18N
0873: return fs;
0874: }
0875:
0876: public Attributes getAttributes() throws IOException {
0877: if (manifest != null) {
0878: InputStream is = new FileInputStream(manifest);
0879: try {
0880: return new Manifest(is).getMainAttributes();
0881: } finally {
0882: is.close();
0883: }
0884: } else if (module != null) {
0885: JarFile jar = new JarFile(module);
0886: try {
0887: return jar.getManifest().getMainAttributes();
0888: } finally {
0889: jar.close();
0890: }
0891: } else {
0892: throw new IOException(
0893: getLocation()
0894: + "must give either 'manifest' or 'module' on <makenbm>");
0895: }
0896: }
0897:
0898: protected String getCodenameBase(String openide_module) {
0899: String ret = openide_module;
0900: int idx = ret.indexOf('/'); //NOI18N
0901: if (idx != -1) {
0902: ret = ret.substring(0, idx);
0903: }
0904: return (ret);
0905: }
0906:
0907: protected String getSpecVer(String mod_info) {
0908: String ret = null;
0909: int first_idx, second_idx;
0910:
0911: // If there are 2 slashes. //
0912: first_idx = mod_info.indexOf('/'); //NOI18N
0913: if (first_idx != -1) {
0914: second_idx = mod_info.indexOf('/', first_idx + 1); //NOI18N
0915: if (second_idx != -1) {
0916:
0917: // Return the string after the second slash. //
0918: ret = mod_info.substring(second_idx + 1, mod_info
0919: .length());
0920: }
0921: }
0922:
0923: // Return null rather than an empty string. //
0924: if (ret != null && ret.trim().equals("")) { //NOI18N
0925: ret = null;
0926: }
0927: return (ret);
0928: }
0929:
0930: protected String getMajorVer(String mod_info) {
0931: String ret = null;
0932: int first_idx, second_idx;
0933:
0934: // If there are 2 slashes. //
0935: first_idx = mod_info.indexOf('/'); //NOI18N
0936: if (first_idx != -1) {
0937: second_idx = mod_info.indexOf('/', first_idx + 1); //NOI18N
0938: if (second_idx != -1) {
0939:
0940: // Return the string between the slashes. //
0941: ret = mod_info.substring(first_idx + 1, second_idx);
0942: }
0943:
0944: // Else return the string after the first slash. //
0945: else {
0946: ret = mod_info.substring(first_idx + 1, mod_info
0947: .length());
0948: }
0949: }
0950:
0951: // Return null rather than an empty string. //
0952: if (ret != null && ret.trim().equals("")) { //NOI18N
0953: ret = null;
0954: }
0955: return (ret);
0956: }
0957:
0958: /** For l10n NBM's, this is the localizing bundle file
0959: * that we'll look in to get module name, description, etc.
0960: */
0961: public void setLocBundle(File f) {
0962: locBundle = f;
0963: }
0964:
0965: /** See reqManOrMod() */
0966: public void setManOrModReq(boolean b) {
0967: manOrModReq = b;
0968: manOrModReqSet = true;
0969: }
0970:
0971: /** If the manifest and module aren't required, use this
0972: * to set the module codename, major version and spec version.
0973: */
0974: public void setModInfo(String s) {
0975: modInfo = s;
0976: }
0977:
0978: /** Set the language code for localized NBM's. */
0979: public void setLangCode(String s) {
0980: langCode = s;
0981: }
0982:
0983: /** Set the branding code for branded NBM's. */
0984: public void setBrandingCode(String s) {
0985: brandingCode = s;
0986: }
0987:
0988: /** Returns true if either a manifest or a module must be specified.
0989: * This is true unless either the global property
0990: * makenbm.manOrModReq is false, or the manOrModReq attribute of
0991: * this task is false. The attribute, if set, has priority over the
0992: * global property.
0993: */
0994: public boolean reqManOrMod() {
0995: String s = null;
0996: boolean req = true;
0997:
0998: if (manOrModReqSet) {
0999: req = manOrModReq;
1000: } else {
1001: s = getProject().getProperty("makenbm.manOrModReq"); //NOI18N
1002: if (s != null && !s.equals("")) { //NOI18N
1003: req = getProject().toBoolean(s);
1004: }
1005: }
1006:
1007: return (req);
1008: }
1009:
1010: protected void writeLocBundleAttrs(PrintWriter ps) {
1011: FileInputStream fis;
1012: Properties p = new Properties();
1013: String s;
1014: boolean hadone = false;
1015:
1016: try {
1017: fis = new FileInputStream(locBundle);
1018: p.load(fis);
1019: fis.close();
1020: } catch (Exception e) {
1021: System.out.println("ERROR: " + e.getMessage());
1022: e.printStackTrace();
1023: throw new BuildException();
1024: }
1025:
1026: s = p.getProperty("OpenIDE-Module-Name"); //NOI18N
1027: if (writeProp("OpenIDE-Module-Name", s, ps)) { //NOI18N
1028: hadone = true;
1029: }
1030:
1031: s = p.getProperty("OpenIDE-Module-Long-Description"); //NOI18N
1032: if (writeProp("OpenIDE-Module-Long-Description", s, ps)) { //NOI18N
1033: hadone = true;
1034: }
1035:
1036: if (!hadone) {
1037: log("WARNING: Localizing bundle had neither property: "
1038: + locBundle);
1039: }
1040: }
1041:
1042: protected boolean writeProp(String name, String val, PrintWriter ps) {
1043: boolean ret = false;
1044: if (val != null) {
1045: ps.println(name + "=\"" + xmlEscape(val) + "\""); //NOI18N
1046: ret = true;
1047: }
1048: return (ret);
1049: }
1050:
1051: }
|