1 19 20 package org.netbeans.nbbuild; 21 22 import java.io.*; 23 import java.util.*; 24 import java.util.Map ; import java.util.zip.*; 26 import java.util.jar.*; 27 28 import org.apache.tools.ant.*; 29 import org.apache.tools.ant.taskdefs.MatchingTask; 30 import org.apache.tools.ant.types.*; 31 32 56 public class LocalizedJar extends MatchingTask { 57 58 private List<FileSet> localeKits = new LinkedList<FileSet> (); 59 private List<LocaleOrB> locales = new LinkedList<LocaleOrB> (); 60 private List<LocaleOrB> brandings = new LinkedList<LocaleOrB> (); 61 private File jarFile; 62 private File baseDir; 63 private boolean doCompress = false; 64 private static long emptyCrc = new CRC32 ().getValue (); 65 private List<FileSet> filesets = new LinkedList<FileSet> (); 66 private File manifest; 67 private boolean checkPathLocale = true ; 68 private boolean warnMissingDir = false ; 69 private boolean warnMissingDirSet = false ; 70 private boolean preserveModuleJar = true; 71 private boolean alwaysIncludeManifest = false; 72 73 82 public class LocaleOrB { 83 String n; 84 85 public void setName (String n) { 86 this.n = n; 87 } 88 } 89 90 103 public void addLocalekit (FileSet fs) { 104 localeKits.add (fs); 105 } 106 107 108 public LocaleOrB createLocale () { 109 LocaleOrB l = new LocaleOrB (); 110 locales.add (l); 111 return l; 112 } 113 114 115 public LocaleOrB createBranding () { 116 LocaleOrB l = new LocaleOrB (); 117 brandings.add (l); 118 return l; 119 } 120 121 128 public void setJarfile (File jarFile) { 129 if (! jarFile.getName ().endsWith (".jar")) { 130 throw new BuildException ("jarfile attribute must be a file with *.jar extension"); 131 } 132 if (jarFile.getParentFile () == null) { 133 throw new BuildException ("jarfile attribute must have a containing directory"); 134 } 135 this.jarFile = jarFile; 136 } 137 138 141 public void setBasedir (File baseDir) { 142 this.baseDir = baseDir; 143 } 144 145 148 public void setCompress(boolean compress) { 149 doCompress = compress; 150 } 151 152 156 public void setPreserveModuleJar(boolean pmj) { 157 preserveModuleJar = pmj; 158 } 159 160 164 public void setAlwaysIncludeManifest(boolean aim) { 165 alwaysIncludeManifest = aim; 166 } 167 168 171 public void addFileset (FileSet set) { 172 filesets.add (set); 173 } 174 175 178 public void setManifest (File manifest) { 179 this.manifest = manifest; 180 } 181 182 187 public void setCheckPathLocale( boolean doit) { 188 checkPathLocale = doit ; 189 } 190 191 197 public void setWarnMissingDir( boolean b) { 198 warnMissingDir = b ; 199 warnMissingDirSet = true ; 200 } 201 202 public void execute () throws BuildException { 203 204 if (baseDir == null && filesets.size () == 0) { 206 throw new BuildException ("basedir attribute must be set, or at least one fileset must be given!"); 207 } 208 if (jarFile == null) { 209 throw new BuildException ("You must specify the JAR file to create!"); 210 } 211 if (manifest != null && ! manifest.isFile ()) { 212 throw new BuildException ("The specified manifest does not actually exist."); 213 } 214 215 if( shouldWarnMissingDir() && warnIfMissingDir()) { 217 218 return ; 220 } 221 222 addGlobalLocaleAndBranding() ; 224 225 Map <String , File> allFiles = new HashMap<String , File> (); { 230 List<FileScanner> scanners = new ArrayList<FileScanner> (filesets.size () + 1); 231 if (baseDir != null) { 232 scanners.add (getDirectoryScanner (baseDir)); 233 } 234 for (FileSet fs: filesets) { 235 scanners.add(fs.getDirectoryScanner(getProject())); 236 } 237 for (FileScanner scanner: scanners) { 238 File thisBaseDir = scanner.getBasedir (); 239 String [] files = scanner.getIncludedFiles (); 240 for (int i = 0; i < files.length; i++) { 241 String name = files[i].replace (File.separatorChar, '/'); 242 if (name.equalsIgnoreCase ("META-INF/MANIFEST.MF")) { 243 log ("Warning: ignoring META-INF/MANIFEST.MF found among scanned files", Project.MSG_WARN); 244 continue; 245 } 246 allFiles.put (name, new File (thisBaseDir, files[i])); 247 } 248 } 249 } 250 251 Set<File> localeKitFiles = new HashSet<File> (); { 258 for (FileSet fs: localeKits) { 259 FileScanner scanner = fs.getDirectoryScanner(getProject()); 260 File thisBaseDir = scanner.getBasedir (); 261 String [] files = scanner.getIncludedFiles (); 262 for (int i = 0; i < files.length; i++) { 263 localeKitFiles.add (new File (thisBaseDir, files[i])); 264 } 265 } 266 } 267 268 List<String > locales2 = new LinkedList<String > (); 271 List<String > brandings2 = new LinkedList<String > (); 274 for (LocaleOrB lob: locales) { 275 locales2.add (lob.n); 276 } 277 for (LocaleOrB lob: brandings) { 278 brandings2.add (lob.n); 279 } 280 class InverseLengthComparator implements Comparator<String > { 281 public int compare (String s1, String s2) { 282 return s2.length () - s1.length (); 283 } 284 } 285 Collections.sort (locales2, new InverseLengthComparator ()); 286 Collections.sort (brandings2, new InverseLengthComparator ()); 287 288 Set<File> jars = new HashSet<File> (); Map <File,String > localeMarks = new HashMap<File,String > (); Map <File,String > brandingMarks = new HashMap<File,String > (); Map <File,Map <String ,File>> router = new HashMap<File,Map <String ,File>> (); { 295 String localeDir ; 296 for (Map.Entry <String , File> entry: allFiles.entrySet()) { 297 String path = entry.getKey (); 298 299 log( "==> Examining file: " + path, Project.MSG_DEBUG) ; 300 301 File file = entry.getValue (); 302 String testpath = path; 304 int idx = testpath.lastIndexOf ('/'); 305 if (idx != -1) testpath = testpath.substring (idx + 1); 306 idx = testpath.lastIndexOf ('.'); 307 if (idx != -1) testpath = testpath.substring (0, idx); 308 String thisLocale = null; 309 Iterator it2 = locales2.iterator (); 310 while (it2.hasNext ()) { 311 String tryLocale = (String ) it2.next (); 312 if (testpath.endsWith ("_" + tryLocale)) { 313 thisLocale = tryLocale; 314 testpath = testpath.substring (0, testpath.length () - 1 - tryLocale.length ()); 315 break; 316 } 317 } 318 String thisBranding = null; 319 it2 = brandings2.iterator (); 320 while (it2.hasNext ()) { 321 String tryBranding = (String ) it2.next (); 322 if (testpath.endsWith ("_" + tryBranding)) { 323 thisBranding = tryBranding; 324 break; 325 } 326 } 327 File thisjar = null; 329 localeDir = checkInLocaleDir( file, locales2) ; 332 if( localeDir != null) { 333 thisLocale = localeDir ; 334 } 335 336 337 if( thisLocale != null) { 338 log( " Locale: " + thisLocale, Project.MSG_DEBUG) ; 339 } else { 340 log( " Locale not set", Project.MSG_DEBUG) ; 341 } 342 if( thisBranding != null) { 343 log( " Branding: " + thisBranding, Project.MSG_DEBUG) ; 344 } else { 345 log( " Branding not set", Project.MSG_DEBUG) ; 346 } 347 if( localeKitFiles.contains( file)) { 348 log( " Localizable file.", Project.MSG_DEBUG) ; 349 } 350 351 352 if (thisLocale != null || thisBranding != null || localeKitFiles.contains (file)) { 353 String name = jarFile.getName (); 354 name = name.substring (0, name.length () - 4); 356 if (thisBranding != null) { 357 name += '_' + thisBranding; 358 } 359 if (thisLocale != null) { 360 name += '_' + thisLocale; 361 } 362 name += ".jar"; 363 if ((preserveModuleJar) && (thisBranding == null) && (thisLocale == null)) { 364 thisjar = null; 365 log(" Preserving module file (1): " + jarFile.getName(), Project.MSG_DEBUG); 366 } else { 367 thisjar = new File (new File (jarFile.getParentFile (), "locale"), name); 368 localeMarks.put (thisjar, ((thisLocale != null) ? thisLocale : "-")); 369 brandingMarks.put (thisjar, ((thisBranding != null) ? thisBranding : "-")); 370 } 371 } else { 372 if (preserveModuleJar) { 373 thisjar = null; 374 log(" Preserving module file (2): " + jarFile.getName(), Project.MSG_DEBUG); 375 } else { 376 thisjar = jarFile; 377 localeMarks.put (thisjar, null); 378 brandingMarks.put (thisjar, null); 379 } 380 } 381 if (thisjar != null) { 382 log(" Adding file " + thisjar.getName() + " to 'jars' HashSet", Project.MSG_DEBUG); 383 jars.add (thisjar); 384 Map <String , File> files = router.get (thisjar); 385 if (files == null) { 386 files = new TreeMap<String , File> (); 387 router.put (thisjar, files); 388 } 389 files.put (path, file); 390 } 391 } 392 } 393 394 { 397 List<File> jars2 = new ArrayList<File> (jars); 398 class FileNameComparator implements Comparator<File> { 399 public int compare (File f1, File f2) { 400 return f1.toString ().compareTo (f2.toString ()); 401 } 402 } 403 Collections.sort (jars2, new FileNameComparator ()); 404 for (File jar: jars2) { 405 Map <String , File> files = router.get (jar); 406 if (jar.exists ()) { 407 long time = jar.lastModified (); 409 if (manifest == null || manifest.lastModified () <= time) { 410 boolean upToDate = true; 411 Iterator it2 = files.values ().iterator (); 412 while (it2.hasNext ()) { 413 File f = (File) it2.next (); 414 if (f.lastModified () > time) { 415 upToDate = false; 416 break; 417 } 418 } 419 if (upToDate) { 420 continue; 422 } 423 } 424 } 425 log ("Building localized/branded jar: " + jar); 426 IOException closing = null; 427 try { 428 jar.getParentFile ().mkdirs (); 429 ZipOutputStream out = new ZipOutputStream (new FileOutputStream (jar)); 430 try { 431 out.setMethod (doCompress ? ZipOutputStream.DEFLATED : ZipOutputStream.STORED); 432 String localeMark = localeMarks.get (jar); 433 String brandingMark = brandingMarks.get (jar); 434 Set<String > addedDirs = new HashSet<String > (); 435 InputStream is; 437 long time; 438 java.util.jar.Manifest mani; 439 if (manifest != null && localeMark == null && brandingMark == null) { 440 is = new FileInputStream (manifest); 442 time = manifest.lastModified (); 443 try { 444 mani = new java.util.jar.Manifest (is); 445 } finally { 446 is.close (); 447 } 448 } else if ((manifest != null) && (alwaysIncludeManifest)) { 449 is = new FileInputStream (manifest); 454 time = manifest.lastModified (); 455 try { 456 mani = new java.util.jar.Manifest (is); 457 } finally { 458 is.close (); 459 } 460 Attributes attr = mani.getMainAttributes (); 461 if ((attr.containsKey ("OpenIDE-Module")) && ((localeMark != null) || (brandingMark != null))){ 463 String lbmsg = ""; 464 if (localeMark != null) { 465 lbmsg = "locale: '"+localeMark+"' "; 466 } 467 if (brandingMark != null) { 468 lbmsg = "branding: '"+brandingMark+"' "; 469 } 470 log("WARNING: Ignoring supplied NetBeans module manifest for "+lbmsg+"jarfile '"+jar+"'. Using default Ant manifest bolilerplate. " 471 +"Use -verbose option to see more details.", Project.MSG_INFO); 472 log("WARNING(verbose): Supplied manifest file '"+manifest.getAbsolutePath()+"' contains " 473 +"key OpenIDE-Module, which cannot be included in manifest of localized " 474 +"and/or branded jar. Ignoring whole manifest for now. To fix this you have " 475 +"to avoid using NetBeans module manifest file together with attribute " 476 +"'allwaysincludemanifest' set to 'true' and non-empty properties 'locjar.locales' " 477 +"and 'locjar.brands'. You can accomplish that by i.e. using Ant's <jar> task " 478 +"for regular NetBeans module jarfile packaging and use NetBeans' Ant extension " 479 +"task <"+this.getTaskName()+"> for localized and/or branded jars. Using default " 480 +"Ant's manifest boilerplate instead.", Project.MSG_VERBOSE); 481 try { 482 is.close(); 483 } finally { 484 is = MatchingTask.class.getResourceAsStream ("/org/apache/tools/ant/defaultManifest.mf"); 485 time = System.currentTimeMillis (); 486 try { 487 mani = new java.util.jar.Manifest (is); 488 } finally { 489 is.close (); 490 } 491 } 492 } 493 } else { 494 is = MatchingTask.class.getResourceAsStream ("/org/apache/tools/ant/defaultManifest.mf"); 496 time = System.currentTimeMillis (); 497 try { 498 mani = new java.util.jar.Manifest (is); 499 } finally { 500 is.close (); 501 } 502 } 503 Attributes attr = mani.getMainAttributes (); 504 if (! attr.containsKey (Attributes.Name.MANIFEST_VERSION)) { 505 attr.put (Attributes.Name.MANIFEST_VERSION, "1.0"); 506 } 507 if (localeMark != null) { 508 attr.putValue ("X-Informational-Archive-Locale", localeMark); 509 } 510 if (brandingMark != null) { 511 attr.putValue ("X-Informational-Archive-Branding", brandingMark); 512 } 513 ByteArrayOutputStream baos = new ByteArrayOutputStream (); 514 mani.write (baos); 515 byte[] bytes = baos.toByteArray (); 516 addToJar (new ByteArrayInputStream (bytes), new ByteArrayInputStream (bytes), 517 out, "META-INF/MANIFEST.MF", time, addedDirs); 518 for (Map.Entry <String , File> entry: files.entrySet()) { 520 String path = entry.getKey (); 521 File file = entry.getValue (); 522 addToJar (new FileInputStream (file), new FileInputStream (file), 523 out, path, file.lastModified (), addedDirs); 524 } 525 526 writeSrcDir() ; 528 } finally { 529 try { 530 out.close (); 531 } catch (IOException ex) { 532 closing = ex; 533 } 534 } 535 536 if (closing != null) { 537 throw closing; 539 } 540 } catch (IOException ioe) { 541 String msg = "Problem creating JAR: " + ioe.getMessage (); 542 if (! jar.delete ()) { 543 msg += " (and the JAR is probably corrupt but I could not delete it)"; 544 } 545 throw new BuildException(msg, ioe, getLocation()); 546 } 547 } 548 } 549 550 } 552 private void addToJar (InputStream in1, InputStream in2, ZipOutputStream out, 553 String path, long lastModified, Set<String > addedDirs) throws IOException { 554 try { 555 if (path.endsWith ("/")) { 556 throw new IOException ("Bad path: " + path); 557 } 558 int pos = -1; 560 while ((pos = path.indexOf ('/', pos + 1)) != -1) { 561 String dir = path.substring (0, pos + 1); 562 if (! addedDirs.contains (dir)) { 563 addedDirs.add (dir); 564 ZipEntry ze = new ZipEntry (dir); 565 ze.setSize (0); 566 ze.setMethod (ZipEntry.STORED); 567 ze.setCrc (emptyCrc); 568 ze.setTime (lastModified); 569 out.putNextEntry (ze); 570 } 571 } 572 ZipEntry ze = new ZipEntry (path); 574 ze.setMethod (doCompress ? ZipEntry.DEFLATED : ZipEntry.STORED); 575 ze.setTime (lastModified); 576 long size = 0; 577 CRC32 crc = new CRC32 (); 578 byte[] buf = new byte[4096]; 579 int read; 580 while ((read = in1.read (buf)) != -1) { 581 crc.update (buf, 0, read); 582 size += read; 583 } 584 in1.close (); 585 ze.setCrc (crc.getValue ()); 586 ze.setSize (size); 587 out.putNextEntry (ze); 588 while ((read = in2.read (buf)) != -1) { 589 out.write (buf, 0, read); 590 } 591 } finally { 592 in2.close (); 593 in1.close (); 594 } 595 } 597 598 protected String checkInLocaleDir( File file, 601 List<String > locales) { 602 603 if( !checkPathLocale) { 605 return null ; 606 } 607 608 int idx ; 609 String locale_dir, ret = null ; 610 String path = file.getPath() ; 611 612 for (String loc: locales) { 614 615 locale_dir = new String ( file.separator) ; 618 locale_dir += loc ; 619 locale_dir += file.separator ; 620 idx = path.indexOf( locale_dir) ; 621 if( idx != -1) { 622 623 ret = loc ; 625 break ; 626 } 627 } 628 629 return( ret) ; 630 } 631 632 protected void addGlobalLocaleAndBranding() { 637 addGlobals( getGlobalLocaleVarName(), locales) ; 638 addGlobals( getOldGlobalLocaleVarName(), locales) ; 639 addGlobals( getGlobalBrandingVarName(), brandings) ; 640 addGlobals( getOldGlobalBrandingVarName(), brandings) ; 641 } 642 643 protected String getGlobalLocaleVarName() { 644 return( new String ( "locjar.locales")) ; 645 } 646 647 protected String getGlobalBrandingVarName() { 648 return( new String ( "locjar.brands")) ; 649 } 650 651 protected String getOldGlobalLocaleVarName() { 653 return( new String ( "locjar_global_locales")) ; 654 } 655 656 protected String getOldGlobalBrandingVarName() { 658 return( new String ( "locjar_global_brands")) ; 659 } 660 661 protected void addGlobals( String var_name, 662 List<LocaleOrB> list) { 663 String prop = null ; 664 StringTokenizer tokenizer = null ; 665 String tok = null ; 666 LocaleOrB lorb = null ; 667 668 prop = getProject().getProperty( var_name) ; 670 if( prop != null && !prop.equals( "")) { 671 tokenizer = new StringTokenizer( prop, ", ") ; 672 while( tokenizer.hasMoreTokens()) { 673 tok = tokenizer.nextToken() ; 674 675 lorb = new LocaleOrB() ; 677 lorb.setName( tok) ; 678 list.add( lorb) ; 679 } 680 } 681 } 682 684 protected boolean shouldWarnMissingDir() { 685 String s ; 686 boolean ret = false ; 688 if( warnMissingDirSet) { 690 ret = warnMissingDir ; 691 } 692 693 else { 695 s = getProject().getProperty("locjar.warnMissingDir"); 696 if( s != null && !s.trim().equals( "")) { 697 ret = getProject().toBoolean(s); 698 } 699 } 700 701 return( ret) ; 702 } 703 704 protected boolean warnIfMissingDir() { 706 File dir ; 707 boolean ret = false ; 708 709 if( baseDir != null && !baseDir.exists()) { 711 ret = true ; 712 printMissingDirWarning( baseDir) ; 713 } 714 715 for (FileSet fileset: filesets) { 717 dir = fileset.getDir(getProject()); 719 if( dir != null && !dir.exists()) { 720 ret = true ; 721 printMissingDirWarning( dir) ; 722 } 723 } 724 return( ret) ; 725 } 726 727 protected void printMissingDirWarning( File dir) { 729 log( "WARNING: Skipping this task: Directory " + dir.getPath() + " doesn't exist.") ; 730 } 731 732 protected boolean shouldWriteSrcDir() { 733 boolean ret = false ; 734 String s = getProject().getProperty("locjar.writeSrcDir"); 735 if( s != null && getProject().toBoolean(s)) { 736 ret = true ; 737 } 738 return( ret) ; 739 } 740 741 protected void writeSrcDir() { 742 String name ; 743 int idx, fromIdx ; 744 OutputStreamWriter osw ; 745 FileOutputStream fos ; 746 File file ; 747 748 if( shouldWriteSrcDir() && jarFile != null && baseDir != null) { 749 name = jarFile.getPath() ; 750 fromIdx = getNetbeansStartIdx() ; 751 idx = name.indexOf( File.separator+"netbeans"+File.separator, fromIdx) ; 752 if( idx != -1) { 753 try { 754 file = new File( name.substring( 0, idx) + File.separator + "srcdir.properties") ; 755 fos = new FileOutputStream( file) ; 756 osw = new OutputStreamWriter( fos) ; 757 osw.write( "srcdir=" + baseDir + "\n") ; 758 osw.close() ; 759 fos.close() ; 760 } 761 catch( Exception e) { 762 System.out.println( "ERROR: " + e.getMessage()) ; 763 e.printStackTrace() ; 764 throw new BuildException() ; 765 } 766 } 767 else { 768 throw new BuildException( "ERROR: Couldn't find netbeans dir to write srcdir.properties to.") ; 769 } 770 } 771 } 772 773 protected int getNetbeansStartIdx() { 777 int startIdx = 0 ; 778 int idx ; 779 780 idx = baseDir.getPath().lastIndexOf( File.separator+ 781 "netbeans"+File.separator) ; 782 if( idx != -1) { 783 startIdx = idx + 1 ; 784 } 785 return( startIdx) ; 786 } 787 } 788 | Popular Tags |