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.io.*;
045: import java.util.*;
046: import java.util.Map; // override org.apache.tools.ant.Map
047: import java.util.zip.*;
048: import java.util.jar.*;
049:
050: import org.apache.tools.ant.*;
051: import org.apache.tools.ant.taskdefs.MatchingTask;
052: import org.apache.tools.ant.types.*;
053:
054: /** Create a JAR file with locale variants.
055: * Whenever files are found which should be localized, or are the result
056: * of localization, places them in separate JAR files named according to the locale,
057: * in a <samp>locale/</samp> subdirectory of the directory containing the master JAR.
058: * Each sub-JAR gets a manifest which just has some informational tags
059: * indicating its purpose (locale and branding):
060: * <code>X-Informational-Archive-Locale</code> and/or <code>X-Informational-Archive-Branding</code>.
061: * The values may be e.g. <code>ja</code> or <code>f4j_ce</code>; or <code>-</code>
062: * if there is no suffix for this JAR.
063: * You can control the available locales; brandings; and set of files which should
064: * always be considered part of the localizable base kit.
065: * You can use the "branding" and "locale" subelements to control the branded
066: * and localized .jar files that will be produced. Also, you can set the global
067: * properties "locjar.brands" and "locjar.locales" to comma-separated
068: * lists of branding or locale identifiers so that NetBeans-based projects can
069: * brand or localize NetBeans without having to maintain modified versions of all
070: * the individual Ant scripts
071: * Originally this Ant task didn't recognize files below a directory with the
072: * same name as a locale as being localized. Now it does so by default.
073: * <p>Based on <code><zip></code> and <code><jar></code> tasks in Ant,
074: * but not feasible to simply subclass or compose them.
075: * @see <a href="http://www.netbeans.org/devhome/docs/i18n/index.html">NetBeans I18N documentation</a>
076: * @author Jesse Glick
077: */
078: public class LocalizedJar extends MatchingTask {
079:
080: private List<FileSet> localeKits = new LinkedList<FileSet>();
081: private List<LocaleOrB> locales = new LinkedList<LocaleOrB>();
082: private List<LocaleOrB> brandings = new LinkedList<LocaleOrB>();
083: private File jarFile;
084: private File baseDir;
085: private boolean doCompress = false;
086: private static long emptyCrc = new CRC32().getValue();
087: private List<FileSet> filesets = new LinkedList<FileSet>();
088: private File manifest;
089: private boolean checkPathLocale = true;
090: private boolean warnMissingDir = false;
091: private boolean warnMissingDirSet = false;
092: private boolean preserveModuleJar = true;
093: private boolean alwaysIncludeManifest = false;
094:
095: /** Locale or branding specifier.
096: * Represents a complete locale or branding suffix,
097: * e.g. <code>ja</code> or <code>ja_JP</code>
098: * (but not simply <code>JP</code>).
099: * For branding, e.g. <code>f4j</code> or <code>f4j_ce</code>.
100: * You must include all relevant suffixes if you expect them to match
101: * (but the task will handle a branding suffix combined with a locale
102: * suffix, in that order).
103: */
104: public class LocaleOrB {
105: String n;
106:
107: /** The suffix. */
108: public void setName(String n) {
109: this .n = n;
110: }
111: }
112:
113: /** Distinguish particular files that will be considered localizable.
114: * This nested <samp><localekit></samp> has the same syntax as a FileSet.
115: * So typically you will build a JAR from a fileset containing all build
116: * products within a given directory; the "locale kit" should be a fileset
117: * matching all properties files, for example, and maybe icons which you would
118: * expect someone to try to localize--in general, anything which will be referenced
119: * in code using a localized lookup (<code>NbBundle</code>).
120: * While files with recognized localized (or branded) suffixes automatically go into
121: * marked JARs in the <samp>locale/</samp> subdirectory, files considered part of the
122: * locale kit also always go into this subdirectory; if they have no suffix, they will
123: * be placed in a suffixless locale JAR.
124: * So localizers can simply look at this JAR file and brand/localize it as they see fit.
125: */
126: public void addLocalekit(FileSet fs) {
127: localeKits.add(fs);
128: }
129:
130: /** Add a recognized locale suffix. */
131: public LocaleOrB createLocale() {
132: LocaleOrB l = new LocaleOrB();
133: locales.add(l);
134: return l;
135: }
136:
137: /** Add a recognized branding suffix. */
138: public LocaleOrB createBranding() {
139: LocaleOrB l = new LocaleOrB();
140: brandings.add(l);
141: return l;
142: }
143:
144: /** JAR file to create.
145: * In fact this is the location of the "base" JAR;
146: * locale-specific JARs may be created in the <samp>locale/</samp> subdirectory
147: * of the directory containing this JAR, and will be named according to the name
148: * of this JAR.
149: * Compare Ant's <samp><jar></samp> task.
150: */
151: public void setJarfile(File jarFile) {
152: if (!jarFile.getName().endsWith(".jar")) {
153: throw new BuildException(
154: "jarfile attribute must be a file with *.jar extension");
155: }
156: if (jarFile.getParentFile() == null) {
157: throw new BuildException(
158: "jarfile attribute must have a containing directory");
159: }
160: this .jarFile = jarFile;
161: }
162:
163: /** Base directory to JAR.
164: * Compare Ant's <samp><jar></samp> task.
165: */
166: public void setBasedir(File baseDir) {
167: this .baseDir = baseDir;
168: }
169:
170: /** Turn on or off compression (default off).
171: * Compare Ant's <samp><jar></samp> task.
172: */
173: public void setCompress(boolean compress) {
174: doCompress = compress;
175: }
176:
177: /** Turn on/off preserving original module jars with 'code'
178: * Set it to 'true' to not allow overwriting of module jars
179: * with localization/branding stuff
180: */
181: public void setPreserveModuleJar(boolean pmj) {
182: preserveModuleJar = pmj;
183: }
184:
185: /** Turn on/off inclusion of manifest even for localized and/or branded
186: * jars. Set it to true if you want to add special manifest tags
187: * to all produced jarfiles
188: */
189: public void setAlwaysIncludeManifest(boolean aim) {
190: alwaysIncludeManifest = aim;
191: }
192:
193: /** A set of files to JAR up.
194: * Compare Ant's <samp><jar></samp> task.
195: */
196: public void addFileset(FileSet set) {
197: filesets.add(set);
198: }
199:
200: /** Manifest file for the JAR.
201: * Compare Ant's <samp><jar></samp> task.
202: */
203: public void setManifest(File manifest) {
204: this .manifest = manifest;
205: }
206:
207: /** By default this is true. If set to false, then this task will
208: * not recognize files below a directory with the same name as a
209: * locale as being localized (unless the simple filename also
210: * includes the locale).
211: */
212: public void setCheckPathLocale(boolean doit) {
213: checkPathLocale = doit;
214: }
215:
216: /** This is false by default, in which case missing dirs in the
217: * filesets cause a BuildException to be thrown. If true, then
218: * a warning is printed but the build will continue.
219: * This task will also look for a global property
220: * "locjar.warnMissingDir" if this attribute isn't set.
221: */
222: public void setWarnMissingDir(boolean b) {
223: warnMissingDir = b;
224: warnMissingDirSet = true;
225: }
226:
227: public void execute() throws BuildException {
228:
229: // Sanity checks:
230: if (baseDir == null && filesets.size() == 0) {
231: throw new BuildException(
232: "basedir attribute must be set, or at least one fileset must be given!");
233: }
234: if (jarFile == null) {
235: throw new BuildException(
236: "You must specify the JAR file to create!");
237: }
238: if (manifest != null && !manifest.isFile()) {
239: throw new BuildException(
240: "The specified manifest does not actually exist.");
241: }
242:
243: // If needed, warn that directories are missing. //
244: if (shouldWarnMissingDir() && warnIfMissingDir()) {
245:
246: // Stop if dirs were missing. //
247: return;
248: }
249:
250: // Look for global locales or brandings to use. //
251: addGlobalLocaleAndBranding();
252:
253: //System.err.println ("Stage #1");
254: // First find out which files need to be archived.
255: Map<String, File> allFiles = new HashMap<String, File>(); // all files to do something with; Map<String,File> from JAR path to actual file
256: // Populate it.
257: {
258: List<FileScanner> scanners = new ArrayList<FileScanner>(
259: filesets.size() + 1);
260: if (baseDir != null) {
261: scanners.add(getDirectoryScanner(baseDir));
262: }
263: for (FileSet fs : filesets) {
264: scanners.add(fs.getDirectoryScanner(getProject()));
265: }
266: for (FileScanner scanner : scanners) {
267: File this BaseDir = scanner.getBasedir();
268: String[] files = scanner.getIncludedFiles();
269: for (int i = 0; i < files.length; i++) {
270: String name = files[i].replace(File.separatorChar,
271: '/');
272: if (name.equalsIgnoreCase("META-INF/MANIFEST.MF")) {
273: log(
274: "Warning: ignoring META-INF/MANIFEST.MF found among scanned files",
275: Project.MSG_WARN);
276: continue;
277: }
278: allFiles.put(name, new File(this BaseDir, files[i]));
279: }
280: }
281: }
282:
283: //System.err.println ("Stage #2");
284: // Now find all files which should always be put into a locale
285: // kit (e.g. dir/locale/name.jar, no special locale or
286: // branding, but distinguished as localizable/brandable).
287: Set<File> localeKitFiles = new HashSet<File>(); // all locale-kit files
288: // Populate this one.
289: {
290: for (FileSet fs : localeKits) {
291: FileScanner scanner = fs
292: .getDirectoryScanner(getProject());
293: File this BaseDir = scanner.getBasedir();
294: String[] files = scanner.getIncludedFiles();
295: for (int i = 0; i < files.length; i++) {
296: localeKitFiles.add(new File(this BaseDir, files[i]));
297: }
298: }
299: }
300:
301: //System.err.println ("Stage #3");
302: // Compute list of supported locales and brandings.
303: List<String> locales2 = new LinkedList<String>();
304: List<String> brandings2 = new LinkedList<String>(); // all brandings
305: // Initialize above two.
306:
307: for (LocaleOrB lob : locales) {
308: locales2.add(lob.n);
309: }
310: for (LocaleOrB lob : brandings) {
311: brandings2.add(lob.n);
312: }
313: class InverseLengthComparator implements Comparator<String> {
314: public int compare(String s1, String s2) {
315: return s2.length() - s1.length();
316: }
317: }
318: Collections.sort(locales2, new InverseLengthComparator());
319: Collections.sort(brandings2, new InverseLengthComparator());
320:
321: //System.err.println ("Stage #4");
322: // Analyze where everything goes.
323: Set<File> jars = new HashSet<File>(); //JAR files to build
324: Map<File, String> localeMarks = new HashMap<File, String>(); // JAR files to locale (or null for basic JAR, "-" for blank)
325: Map<File, String> brandingMarks = new HashMap<File, String>(); // JAR files to branding (or null for basic JAR, "-" for blank)
326: Map<File, Map<String, File>> router = new HashMap<File, Map<String, File>>(); // JAR files to map of JAR path to actual file (file may be null for dirs)
327: {
328: String localeDir;
329: for (Map.Entry<String, File> entry : allFiles.entrySet()) {
330: String path = entry.getKey();
331:
332: log("==> Examining file: " + path, Project.MSG_DEBUG);
333:
334: File file = entry.getValue();
335: // First see if it matches a known branding, locale, or pair of one of each.
336: String testpath = path;
337: int idx = testpath.lastIndexOf('/');
338: if (idx != -1)
339: testpath = testpath.substring(idx + 1);
340: idx = testpath.lastIndexOf('.');
341: if (idx != -1)
342: testpath = testpath.substring(0, idx);
343: String this Locale = null;
344: Iterator it2 = locales2.iterator();
345: while (it2.hasNext()) {
346: String tryLocale = (String) it2.next();
347: if (testpath.endsWith("_" + tryLocale)) {
348: this Locale = tryLocale;
349: testpath = testpath.substring(0, testpath
350: .length()
351: - 1 - tryLocale.length());
352: break;
353: }
354: }
355: String this Branding = null;
356: it2 = brandings2.iterator();
357: while (it2.hasNext()) {
358: String tryBranding = (String) it2.next();
359: if (testpath.endsWith("_" + tryBranding)) {
360: this Branding = tryBranding;
361: break;
362: }
363: }
364: File this jar = null; // JAR to send this file to
365:
366: // Check if this file has a parent directory with the //
367: // same name as one of the locales. //
368: localeDir = checkInLocaleDir(file, locales2);
369: if (localeDir != null) {
370: this Locale = localeDir;
371: }
372:
373: if (this Locale != null) {
374: log(" Locale: " + this Locale, Project.MSG_DEBUG);
375: } else {
376: log(" Locale not set", Project.MSG_DEBUG);
377: }
378: if (this Branding != null) {
379: log(" Branding: " + this Branding,
380: Project.MSG_DEBUG);
381: } else {
382: log(" Branding not set", Project.MSG_DEBUG);
383: }
384: if (localeKitFiles.contains(file)) {
385: log(" Localizable file.", Project.MSG_DEBUG);
386: }
387:
388: if (this Locale != null || this Branding != null
389: || localeKitFiles.contains(file)) {
390: String name = jarFile.getName();
391: // We know jarFile is a *.jar so this is safe:
392: name = name.substring(0, name.length() - 4);
393: if (this Branding != null) {
394: name += '_' + this Branding;
395: }
396: if (this Locale != null) {
397: name += '_' + this Locale;
398: }
399: name += ".jar";
400: if ((preserveModuleJar) && (this Branding == null)
401: && (this Locale == null)) {
402: this jar = null;
403: log(" Preserving module file (1): "
404: + jarFile.getName(), Project.MSG_DEBUG);
405: } else {
406: this jar = new File(new File(jarFile
407: .getParentFile(), "locale"), name);
408: localeMarks.put(this jar,
409: ((this Locale != null) ? this Locale
410: : "-"));
411: brandingMarks.put(this jar,
412: ((this Branding != null) ? this Branding
413: : "-"));
414: }
415: } else {
416: if (preserveModuleJar) {
417: this jar = null;
418: log(" Preserving module file (2): "
419: + jarFile.getName(), Project.MSG_DEBUG);
420: } else {
421: this jar = jarFile;
422: localeMarks.put(this jar, null);
423: brandingMarks.put(this jar, null);
424: }
425: }
426: if (this jar != null) {
427: log(" Adding file " + this jar.getName()
428: + " to 'jars' HashSet", Project.MSG_DEBUG);
429: jars.add(this jar);
430: Map<String, File> files = router.get(this jar);
431: if (files == null) {
432: files = new TreeMap<String, File>();
433: router.put(this jar, files);
434: }
435: files.put(path, file);
436: }
437: }
438: }
439:
440: //System.err.println ("Stage #5");
441: // Go through JARs one by one, and build them (if necessary).
442: {
443: List<File> jars2 = new ArrayList<File>(jars);
444: class FileNameComparator implements Comparator<File> {
445: public int compare(File f1, File f2) {
446: return f1.toString().compareTo(f2.toString());
447: }
448: }
449: Collections.sort(jars2, new FileNameComparator());
450: for (File jar : jars2) {
451: Map<String, File> files = router.get(jar);
452: if (jar.exists()) {
453: // Do an up-to-date check first.
454: long time = jar.lastModified();
455: if (manifest == null
456: || manifest.lastModified() <= time) {
457: boolean upToDate = true;
458: Iterator it2 = files.values().iterator();
459: while (it2.hasNext()) {
460: File f = (File) it2.next();
461: if (f.lastModified() > time) {
462: upToDate = false;
463: break;
464: }
465: }
466: if (upToDate) {
467: // Skip this JAR.
468: continue;
469: }
470: }
471: }
472: log("Building localized/branded jar: " + jar);
473: IOException closing = null;
474: try {
475: jar.getParentFile().mkdirs();
476: ZipOutputStream out = new ZipOutputStream(
477: new FileOutputStream(jar));
478: try {
479: out
480: .setMethod(doCompress ? ZipOutputStream.DEFLATED
481: : ZipOutputStream.STORED);
482: String localeMark = localeMarks.get(jar);
483: String brandingMark = brandingMarks.get(jar);
484: Set<String> addedDirs = new HashSet<String>();
485: // Add the manifest.
486: InputStream is;
487: long time;
488: java.util.jar.Manifest mani;
489: if (manifest != null && localeMark == null
490: && brandingMark == null) {
491: // Master JAR, and it has a manifest.
492: is = new FileInputStream(manifest);
493: time = manifest.lastModified();
494: try {
495: mani = new java.util.jar.Manifest(is);
496: } finally {
497: is.close();
498: }
499: } else if ((manifest != null)
500: && (alwaysIncludeManifest)) {
501: // always include specified manifest in localized/branded jars
502: // such manifest must not contain attribute OpenIDE-Module
503: // check supplied manifest and if contains key OpenIDE-Module, issue warning
504: // and fallback to default Ant's manifest boilerplate (like no manifest was supplied)
505: is = new FileInputStream(manifest);
506: time = manifest.lastModified();
507: try {
508: mani = new java.util.jar.Manifest(is);
509: } finally {
510: is.close();
511: }
512: Attributes attr = mani.getMainAttributes();
513: //check if it's not module manifest for jarfile with localized/branded resources
514: if ((attr.containsKey("OpenIDE-Module"))
515: && ((localeMark != null) || (brandingMark != null))) {
516: String lbmsg = "";
517: if (localeMark != null) {
518: lbmsg = "locale: '" + localeMark
519: + "' ";
520: }
521: if (brandingMark != null) {
522: lbmsg = "branding: '"
523: + brandingMark + "' ";
524: }
525: log(
526: "WARNING: Ignoring supplied NetBeans module manifest for "
527: + lbmsg
528: + "jarfile '"
529: + jar
530: + "'. Using default Ant manifest bolilerplate. "
531: + "Use -verbose option to see more details.",
532: Project.MSG_INFO);
533: log(
534: "WARNING(verbose): Supplied manifest file '"
535: + manifest
536: .getAbsolutePath()
537: + "' contains "
538: + "key OpenIDE-Module, which cannot be included in manifest of localized "
539: + "and/or branded jar. Ignoring whole manifest for now. To fix this you have "
540: + "to avoid using NetBeans module manifest file together with attribute "
541: + "'allwaysincludemanifest' set to 'true' and non-empty properties 'locjar.locales' "
542: + "and 'locjar.brands'. You can accomplish that by i.e. using Ant's <jar> task "
543: + "for regular NetBeans module jarfile packaging and use NetBeans' Ant extension "
544: + "task <"
545: + this .getTaskName()
546: + "> for localized and/or branded jars. Using default "
547: + "Ant's manifest boilerplate instead.",
548: Project.MSG_VERBOSE);
549: try {
550: is.close();
551: } finally {
552: is = MatchingTask.class
553: .getResourceAsStream("/org/apache/tools/ant/defaultManifest.mf");
554: time = System.currentTimeMillis();
555: try {
556: mani = new java.util.jar.Manifest(
557: is);
558: } finally {
559: is.close();
560: }
561: }
562: }
563: } else {
564: // Some subsidiary JAR.
565: is = MatchingTask.class
566: .getResourceAsStream("/org/apache/tools/ant/defaultManifest.mf");
567: time = System.currentTimeMillis();
568: try {
569: mani = new java.util.jar.Manifest(is);
570: } finally {
571: is.close();
572: }
573: }
574: Attributes attr = mani.getMainAttributes();
575: if (!attr
576: .containsKey(Attributes.Name.MANIFEST_VERSION)) {
577: attr.put(Attributes.Name.MANIFEST_VERSION,
578: "1.0");
579: }
580: if (localeMark != null) {
581: attr.putValue(
582: "X-Informational-Archive-Locale",
583: localeMark);
584: }
585: if (brandingMark != null) {
586: attr.putValue(
587: "X-Informational-Archive-Branding",
588: brandingMark);
589: }
590: ByteArrayOutputStream baos = new ByteArrayOutputStream();
591: mani.write(baos);
592: byte[] bytes = baos.toByteArray();
593: addToJar(new ByteArrayInputStream(bytes),
594: new ByteArrayInputStream(bytes), out,
595: "META-INF/MANIFEST.MF", time, addedDirs);
596: // Now regular files.
597: for (Map.Entry<String, File> entry : files
598: .entrySet()) {
599: String path = entry.getKey();
600: File file = entry.getValue();
601: addToJar(new FileInputStream(file),
602: new FileInputStream(file), out,
603: path, file.lastModified(),
604: addedDirs);
605: }
606:
607: // If desired, write the root of the srcDir to a file. //
608: writeSrcDir();
609: } finally {
610: try {
611: out.close();
612: } catch (IOException ex) {
613: closing = ex;
614: }
615: }
616:
617: if (closing != null) {
618: // if there was a closing exception and no other one
619: throw closing;
620: }
621: } catch (IOException ioe) {
622: String msg = "Problem creating JAR: "
623: + ioe.getMessage();
624: if (!jar.delete()) {
625: msg += " (and the JAR is probably corrupt but I could not delete it)";
626: }
627: throw new BuildException(msg, ioe, getLocation());
628: }
629: }
630: }
631:
632: } // end execute()
633:
634: private void addToJar(InputStream in1, InputStream in2,
635: ZipOutputStream out, String path, long lastModified,
636: Set<String> addedDirs) throws IOException {
637: try {
638: if (path.endsWith("/")) {
639: throw new IOException("Bad path: " + path);
640: }
641: // Add parent dirs as needed:
642: int pos = -1;
643: while ((pos = path.indexOf('/', pos + 1)) != -1) {
644: String dir = path.substring(0, pos + 1);
645: if (!addedDirs.contains(dir)) {
646: addedDirs.add(dir);
647: ZipEntry ze = new ZipEntry(dir);
648: ze.setSize(0);
649: ze.setMethod(ZipEntry.STORED);
650: ze.setCrc(emptyCrc);
651: ze.setTime(lastModified);
652: out.putNextEntry(ze);
653: }
654: }
655: // Add the file itself:
656: ZipEntry ze = new ZipEntry(path);
657: ze.setMethod(doCompress ? ZipEntry.DEFLATED
658: : ZipEntry.STORED);
659: ze.setTime(lastModified);
660: long size = 0;
661: CRC32 crc = new CRC32();
662: byte[] buf = new byte[4096];
663: int read;
664: while ((read = in1.read(buf)) != -1) {
665: crc.update(buf, 0, read);
666: size += read;
667: }
668: in1.close();
669: ze.setCrc(crc.getValue());
670: ze.setSize(size);
671: out.putNextEntry(ze);
672: while ((read = in2.read(buf)) != -1) {
673: out.write(buf, 0, read);
674: }
675: } finally {
676: in2.close();
677: in1.close();
678: }
679: } // end addToJar()
680:
681: // If the name of any parent directory of this file is the same as //
682: // one of the locales, return the locale. //
683: protected String checkInLocaleDir(File file, List<String> locales) {
684:
685: // See if this functionality is disabled. //
686: if (!checkPathLocale) {
687: return null;
688: }
689:
690: int idx;
691: String locale_dir, ret = null;
692: String path = file.getPath();
693:
694: // For each locale. //
695: for (String loc : locales) {
696:
697: // If the path contains a dir with the same name as the //
698: // locale. //
699: locale_dir = new String(file.separator);
700: locale_dir += loc;
701: locale_dir += file.separator;
702: idx = path.indexOf(locale_dir);
703: if (idx != -1) {
704:
705: // Stop and return this locale. //
706: ret = loc;
707: break;
708: }
709: }
710:
711: return (ret);
712: }
713:
714: ////////////////////////////////////////////////////////////////////
715: // This section of code supports the feature that this class will //
716: // look for global properties that specify locales and brandings //
717: // that should be used. //
718: protected void addGlobalLocaleAndBranding() {
719: addGlobals(getGlobalLocaleVarName(), locales);
720: addGlobals(getOldGlobalLocaleVarName(), locales);
721: addGlobals(getGlobalBrandingVarName(), brandings);
722: addGlobals(getOldGlobalBrandingVarName(), brandings);
723: }
724:
725: protected String getGlobalLocaleVarName() {
726: return (new String("locjar.locales"));
727: }
728:
729: protected String getGlobalBrandingVarName() {
730: return (new String("locjar.brands"));
731: }
732:
733: // For backwards compatibility. //
734: protected String getOldGlobalLocaleVarName() {
735: return (new String("locjar_global_locales"));
736: }
737:
738: // For backwards compatibility. //
739: protected String getOldGlobalBrandingVarName() {
740: return (new String("locjar_global_brands"));
741: }
742:
743: protected void addGlobals(String var_name, List<LocaleOrB> list) {
744: String prop = null;
745: StringTokenizer tokenizer = null;
746: String tok = null;
747: LocaleOrB lorb = null;
748:
749: // Foreach string in the global list. //
750: prop = getProject().getProperty(var_name);
751: if (prop != null && !prop.equals("")) {
752: tokenizer = new StringTokenizer(prop, ", ");
753: while (tokenizer.hasMoreTokens()) {
754: tok = tokenizer.nextToken();
755:
756: // Add a new entry in the given list. //
757: lorb = new LocaleOrB();
758: lorb.setName(tok);
759: list.add(lorb);
760: }
761: }
762: }
763:
764: //////////////////////////////////////////////////////////////////////
765:
766: protected boolean shouldWarnMissingDir() {
767: String s;
768: boolean ret = false; // Default false. //
769:
770: // If the attribute is set, use its value. //
771: if (warnMissingDirSet) {
772: ret = warnMissingDir;
773: }
774:
775: // Otherwise use the global property value, if set. //
776: else {
777: s = getProject().getProperty("locjar.warnMissingDir");
778: if (s != null && !s.trim().equals("")) {
779: ret = getProject().toBoolean(s);
780: }
781: }
782:
783: return (ret);
784: }
785:
786: // If any dir's don't exist, warn the user and return true. //
787: protected boolean warnIfMissingDir() {
788: File dir;
789: boolean ret = false;
790:
791: // Print warning if the basedir doesn't exist. //
792: if (baseDir != null && !baseDir.exists()) {
793: ret = true;
794: printMissingDirWarning(baseDir);
795: }
796:
797: // For each fileset. //
798: for (FileSet fileset : filesets) {
799: // Print warning if the dir doesn't exist. //
800: dir = fileset.getDir(getProject());
801: if (dir != null && !dir.exists()) {
802: ret = true;
803: printMissingDirWarning(dir);
804: }
805: }
806: return (ret);
807: }
808:
809: // Warn the user that the given dir doesn't exist. //
810: protected void printMissingDirWarning(File dir) {
811: log("WARNING: Skipping this task: Directory " + dir.getPath()
812: + " doesn't exist.");
813: }
814:
815: protected boolean shouldWriteSrcDir() {
816: boolean ret = false;
817: String s = getProject().getProperty("locjar.writeSrcDir");
818: if (s != null && getProject().toBoolean(s)) {
819: ret = true;
820: }
821: return (ret);
822: }
823:
824: protected void writeSrcDir() {
825: String name;
826: int idx, fromIdx;
827: OutputStreamWriter osw;
828: FileOutputStream fos;
829: File file;
830:
831: if (shouldWriteSrcDir() && jarFile != null && baseDir != null) {
832: name = jarFile.getPath();
833: fromIdx = getNetbeansStartIdx();
834: idx = name.indexOf(File.separator + "netbeans"
835: + File.separator, fromIdx);
836: if (idx != -1) {
837: try {
838: file = new File(name.substring(0, idx)
839: + File.separator + "srcdir.properties");
840: fos = new FileOutputStream(file);
841: osw = new OutputStreamWriter(fos);
842: osw.write("srcdir=" + baseDir + "\n");
843: osw.close();
844: fos.close();
845: } catch (Exception e) {
846: System.out.println("ERROR: " + e.getMessage());
847: e.printStackTrace();
848: throw new BuildException();
849: }
850: } else {
851: throw new BuildException(
852: "ERROR: Couldn't find netbeans dir to write srcdir.properties to.");
853: }
854: }
855: }
856:
857: // Return the index to start searching from to find the "netbeans"
858: // directory into which the "srcdir.properties" file will be
859: // written.
860: protected int getNetbeansStartIdx() {
861: int startIdx = 0;
862: int idx;
863:
864: idx = baseDir.getPath().lastIndexOf(
865: File.separator + "netbeans" + File.separator);
866: if (idx != -1) {
867: startIdx = idx + 1;
868: }
869: return (startIdx);
870: }
871: }
|