1 33 34 package bsh.classpath; 35 36 import java.util.*; 37 import java.util.zip.*; 38 import java.io.*; 39 import java.net.*; 40 import java.io.File ; 41 import bsh.ConsoleInterface; 42 import bsh.StringUtil; 43 import bsh.ClassPathException; 44 import java.lang.ref.WeakReference ; 45 import bsh.NameSource; 46 47 67 public class BshClassPath 68 implements ClassPathListener, NameSource 69 { 70 String name; 71 72 73 private List path; 74 75 private List compPaths; 76 77 78 private Map packageMap; 79 80 private Map classSource; 81 82 private boolean mapsInitialized; 83 84 private UnqualifiedNameTable unqNameTable; 85 86 89 private boolean nameCompletionIncludesUnqNames = true; 90 91 Vector listeners = new Vector(); 92 93 95 public BshClassPath( String name ) { 96 this.name = name; 97 reset(); 98 } 99 100 public BshClassPath( String name, URL [] urls ) { 101 this( name ); 102 add( urls ); 103 } 104 105 107 109 public void setPath( URL[] urls ) { 110 reset(); 111 add( urls ); 112 } 113 114 118 public void addComponent( BshClassPath bcp ) { 119 if ( compPaths == null ) 120 compPaths = new ArrayList(); 121 compPaths.add( bcp ); 122 bcp.addListener( this ); 123 } 124 125 public void add( URL [] urls ) { 126 path.addAll( Arrays.asList(urls) ); 127 if ( mapsInitialized ) 128 map( urls ); 129 } 130 131 public void add( URL url ) throws IOException { 132 path.add(url); 133 if ( mapsInitialized ) 134 map( url ); 135 } 136 137 140 public URL [] getPathComponents() { 141 return (URL[])getFullPath().toArray( new URL[0] ); 142 } 143 144 148 synchronized public Set getClassesForPackage( String pack ) { 149 insureInitialized(); 150 Set set = new HashSet(); 151 Collection c = (Collection)packageMap.get( pack ); 152 if ( c != null ) 153 set.addAll( c ); 154 155 if ( compPaths != null ) 156 for (int i=0; i<compPaths.size(); i++) { 157 c = ((BshClassPath)compPaths.get(i)).getClassesForPackage( 158 pack ); 159 if ( c != null ) 160 set.addAll( c ); 161 } 162 return set; 163 } 164 165 169 synchronized public ClassSource getClassSource( String className ) 170 { 171 ClassSource cs = (ClassSource)classSource.get( className ); 175 if ( cs != null ) 176 return cs; 177 178 insureInitialized(); 180 cs = (ClassSource)classSource.get( className ); 181 if ( cs == null && compPaths != null ) 182 for (int i=0; i<compPaths.size() && cs==null; i++) 183 cs = ((BshClassPath)compPaths.get(i)).getClassSource(className); 184 return cs; 185 } 186 187 192 synchronized public void setClassSource( String className, ClassSource cs ) 193 { 194 classSource.put( className, cs ); 195 } 196 197 220 public void insureInitialized() 221 { 222 insureInitialized( true ); 223 } 224 225 229 protected synchronized void insureInitialized( boolean topPath ) 230 { 231 if ( topPath && !mapsInitialized ) 234 startClassMapping(); 235 236 if ( compPaths != null ) 238 for (int i=0; i< compPaths.size(); i++) 239 ((BshClassPath)compPaths.get(i)).insureInitialized( false ); 240 241 if ( !mapsInitialized ) 243 map( (URL[])path.toArray( new URL[0] ) ); 244 245 if ( topPath && !mapsInitialized ) 246 endClassMapping(); 247 248 mapsInitialized = true; 249 } 250 251 256 protected List getFullPath() 257 { 258 List list = new ArrayList(); 259 if ( compPaths != null ) { 260 for (int i=0; i<compPaths.size(); i++) { 261 List l = ((BshClassPath)compPaths.get(i)).getFullPath(); 262 Iterator it = l.iterator(); 265 while ( it.hasNext() ) { 266 Object o = it.next(); 267 if ( !list.contains(o) ) 268 list.add( o ); 269 } 270 } 271 } 272 list.addAll( path ); 273 return list; 274 } 275 276 277 283 public String getClassNameByUnqName( String name ) 284 throws ClassPathException 285 { 286 insureInitialized(); 287 UnqualifiedNameTable unqNameTable = getUnqualifiedNameTable(); 288 289 Object obj = unqNameTable.get( name ); 290 if ( obj instanceof AmbiguousName ) 291 throw new ClassPathException("Ambigous class names: "+ 292 ((AmbiguousName)obj).get() ); 293 294 return (String )obj; 295 } 296 297 301 private UnqualifiedNameTable getUnqualifiedNameTable() { 302 if ( unqNameTable == null ) 303 unqNameTable = buildUnqualifiedNameTable(); 304 return unqNameTable; 305 } 306 307 private UnqualifiedNameTable buildUnqualifiedNameTable() 308 { 309 UnqualifiedNameTable unqNameTable = new UnqualifiedNameTable(); 310 311 if ( compPaths != null ) 313 for (int i=0; i<compPaths.size(); i++) { 314 Set s = ((BshClassPath)compPaths.get(i)).classSource.keySet(); 315 Iterator it = s.iterator(); 316 while(it.hasNext()) 317 unqNameTable.add( (String )it.next() ); 318 } 319 320 Iterator it = classSource.keySet().iterator(); 322 while(it.hasNext()) 323 unqNameTable.add( (String )it.next() ); 324 325 return unqNameTable; 326 } 327 328 public String [] getAllNames() 329 { 330 insureInitialized(); 331 332 List names = new ArrayList(); 333 Iterator it = getPackagesSet().iterator(); 334 while( it.hasNext() ) { 335 String pack = (String )it.next(); 336 names.addAll( 337 removeInnerClassNames( getClassesForPackage( pack ) ) ); 338 } 339 340 if ( nameCompletionIncludesUnqNames ) 341 names.addAll( getUnqualifiedNameTable().keySet() ); 342 343 return (String [])names.toArray(new String [0]); 344 } 345 346 349 synchronized void map( URL [] urls ) 350 { 351 for(int i=0; i< urls.length; i++) 352 try{ 353 map( urls[i] ); 354 } catch ( IOException e ) { 355 String s = "Error constructing classpath: " +urls[i]+": "+e; 356 errorWhileMapping( s ); 357 } 358 } 359 360 synchronized void map( URL url ) 361 throws IOException 362 { 363 String name = url.getFile(); 364 File f = new File ( name ); 365 366 if ( f.isDirectory() ) { 367 classMapping( "Directory "+ f.toString() ); 368 map( traverseDirForClasses( f ), new DirClassSource(f) ); 369 } else if ( isArchiveFileName( name ) ) { 370 classMapping("Archive: "+url ); 371 map( searchJarForClasses( url ), new JarClassSource(url) ); 372 } 373 377 else { 378 String s = "Not a classpath component: "+ name ; 379 errorWhileMapping( s ); 380 } 381 } 382 383 private void map( String [] classes, Object source ) { 384 for(int i=0; i< classes.length; i++) { 385 mapClass( classes[i], source ); 387 } 388 } 389 390 private void mapClass( String className, Object source ) 391 { 392 String [] sa = splitClassname( className ); 394 String pack = sa[0]; 395 String clas = sa[1]; 396 Set set = (Set)packageMap.get( pack ); 397 if ( set == null ) { 398 set = new HashSet(); 399 packageMap.put( pack, set ); 400 } 401 set.add( className ); 402 403 Object obj = classSource.get( className ); 405 if ( obj == null ) 408 classSource.put( className, source ); 409 } 410 411 414 synchronized private void reset() { 415 path = new ArrayList(); 416 compPaths = null; 417 clearCachedStructures(); 418 } 419 420 423 synchronized private void clearCachedStructures() { 424 mapsInitialized = false; 425 packageMap = new HashMap(); 426 classSource = new HashMap(); 427 unqNameTable = null; 428 nameSpaceChanged(); 429 } 430 431 public void classPathChanged() { 432 clearCachedStructures(); 433 notifyListeners(); 434 } 435 436 444 445 447 static String [] traverseDirForClasses( File dir ) 448 throws IOException 449 { 450 List list = traverseDirForClassesAux( dir, dir ); 451 return (String [])list.toArray( new String [0] ); 452 } 453 454 static List traverseDirForClassesAux( File topDir, File dir ) 455 throws IOException 456 { 457 List list = new ArrayList(); 458 String top = topDir.getAbsolutePath(); 459 460 File [] children = dir.listFiles(); 461 for (int i=0; i< children.length; i++) { 462 File child = children[i]; 463 if ( child.isDirectory() ) 464 list.addAll( traverseDirForClassesAux( topDir, child ) ); 465 else { 466 String name = child.getAbsolutePath(); 467 if ( isClassFileName( name ) ) { 468 472 if ( name.startsWith( top ) ) 473 name = name.substring( top.length()+1 ); 474 else 475 throw new IOException( "problem parsing paths" ); 476 477 name = canonicalizeClassName(name); 478 list.add( name ); 479 } 480 } 481 } 482 483 484 return list; 485 } 486 487 490 static String [] searchJarForClasses( URL jar ) 491 throws IOException 492 { 493 Vector v = new Vector(); 494 InputStream in = jar.openStream(); 495 ZipInputStream zin = new ZipInputStream(in); 496 497 ZipEntry ze; 498 while( (ze= zin.getNextEntry()) != null ) { 499 String name=ze.getName(); 500 if ( isClassFileName( name ) ) 501 v.addElement( canonicalizeClassName(name) ); 502 } 503 zin.close(); 504 505 String [] sa = new String [v.size()]; 506 v.copyInto(sa); 507 return sa; 508 } 509 510 public static boolean isClassFileName( String name ){ 511 return ( name.toLowerCase().endsWith(".class") ); 512 } 514 515 public static boolean isArchiveFileName( String name ){ 516 name = name.toLowerCase(); 517 return ( name.endsWith(".jar") || name.endsWith(".zip") ); 518 } 519 520 526 public static String canonicalizeClassName( String name ) 527 { 528 String classname=name.replace('/', '.'); 529 classname=classname.replace('\\', '.'); 530 if ( classname.startsWith("class ") ) 531 classname=classname.substring(6); 532 if ( classname.endsWith(".class") ) 533 classname=classname.substring(0,classname.length()-6); 534 return classname; 535 } 536 537 540 public static String [] splitClassname ( String classname ) { 541 classname = canonicalizeClassName( classname ); 542 543 int i=classname.lastIndexOf("."); 544 String classn, packn; 545 if ( i == -1 ) { 546 classn = classname; 548 packn="<unpackaged>"; 549 } else { 550 packn = classname.substring(0,i); 551 classn = classname.substring(i+1); 552 } 553 return new String [] { packn, classn }; 554 } 555 556 559 public static Collection removeInnerClassNames( Collection col ) { 560 List list = new ArrayList(); 561 list.addAll(col); 562 Iterator it = list.iterator(); 563 while(it.hasNext()) { 564 String name =(String )it.next(); 565 if (name.indexOf("$") != -1 ) 566 it.remove(); 567 } 568 return list; 569 } 570 571 575 576 static URL [] userClassPathComp; 577 public static URL [] getUserClassPathComponents() 578 throws ClassPathException 579 { 580 if ( userClassPathComp != null ) 581 return userClassPathComp; 582 583 String cp=System.getProperty("java.class.path"); 584 String [] paths=StringUtil.split(cp, File.pathSeparator); 585 586 URL [] urls = new URL[ paths.length ]; 587 try { 588 for ( int i=0; i<paths.length; i++) 589 urls[i] = new File ( 593 new File (paths[i]).getCanonicalPath() ).toURL(); 594 } catch ( IOException e ) { 595 throw new ClassPathException("can't parse class path: "+e); 596 } 597 598 userClassPathComp = urls; 599 return urls; 600 } 601 602 605 public Set getPackagesSet() 606 { 607 insureInitialized(); 608 Set set = new HashSet(); 609 set.addAll( packageMap.keySet() ); 610 611 if ( compPaths != null ) 612 for (int i=0; i<compPaths.size(); i++) 613 set.addAll( 614 ((BshClassPath)compPaths.get(i)).packageMap.keySet() ); 615 return set; 616 } 617 618 public void addListener( ClassPathListener l ) { 619 listeners.addElement( new WeakReference (l) ); 620 } 621 public void removeListener( ClassPathListener l ) { 622 listeners.removeElement( l ); 623 } 624 625 627 void notifyListeners() { 628 for (Enumeration e = listeners.elements(); e.hasMoreElements(); ) { 629 WeakReference wr = (WeakReference )e.nextElement(); 630 ClassPathListener l = (ClassPathListener)wr.get(); 631 if ( l == null ) listeners.removeElement( wr ); 633 else 634 l.classPathChanged(); 635 } 636 } 637 638 static BshClassPath userClassPath; 639 643 public static BshClassPath getUserClassPath() 644 throws ClassPathException 645 { 646 if ( userClassPath == null ) 647 userClassPath = new BshClassPath( 648 "User Class Path", getUserClassPathComponents() ); 649 return userClassPath; 650 } 651 652 static BshClassPath bootClassPath; 653 656 public static BshClassPath getBootClassPath() 657 throws ClassPathException 658 { 659 if ( bootClassPath == null ) 660 { 661 try 662 { 663 String rtjar = getRTJarPath(); 665 URL url = new File ( rtjar ).toURL(); 666 bootClassPath = new BshClassPath( 667 "Boot Class Path", new URL[] { url } ); 668 } catch ( MalformedURLException e ) { 669 throw new ClassPathException(" can't find boot jar: "+e); 670 } 671 } 672 return bootClassPath; 673 } 674 675 676 private static String getRTJarPath() 677 { 678 String urlString = 679 Class .class.getResource("/java/lang/String.class").toExternalForm(); 680 681 if ( !urlString.startsWith("jar:file:") ) 682 return null; 683 684 int i = urlString.indexOf("!"); 685 if ( i == -1 ) 686 return null; 687 688 return urlString.substring( "jar:file:".length(), i ); 689 } 690 691 public abstract static class ClassSource { 692 Object source; 693 abstract byte [] getCode( String className ); 694 } 695 696 public static class JarClassSource extends ClassSource { 697 JarClassSource( URL url ) { source = url; } 698 public URL getURL() { return (URL)source; } 699 704 public byte [] getCode( String className ) { 705 throw new Error ("Unimplemented"); 706 } 707 public String toString() { return "Jar: "+source; } 708 } 709 710 public static class DirClassSource extends ClassSource 711 { 712 DirClassSource( File dir ) { source = dir; } 713 public File getDir() { return (File )source; } 714 public String toString() { return "Dir: "+source; } 715 716 public byte [] getCode( String className ) { 717 return readBytesFromFile( getDir(), className ); 718 } 719 720 public static byte [] readBytesFromFile( File base, String className ) 721 { 722 String n = className.replace( '.', File.separatorChar ) + ".class"; 723 File file = new File ( base, n ); 724 725 if ( file == null || !file.exists() ) 726 return null; 727 728 byte [] bytes; 729 try { 730 FileInputStream fis = new FileInputStream(file); 731 DataInputStream dis = new DataInputStream( fis ); 732 733 bytes = new byte [ (int)file.length() ]; 734 735 dis.readFully( bytes ); 736 dis.close(); 737 } catch(IOException ie ) { 738 throw new RuntimeException ("Couldn't load file: "+file); 739 } 740 741 return bytes; 742 } 743 744 } 745 746 public static class GeneratedClassSource extends ClassSource 747 { 748 GeneratedClassSource( byte [] bytecode ) { source = bytecode; } 749 public byte [] getCode( String className ) { 750 return (byte [])source; 751 } 752 } 753 754 public static void main( String [] args ) throws Exception { 755 URL [] urls = new URL [ args.length ]; 756 for(int i=0; i< args.length; i++) 757 urls[i] = new File (args[i]).toURL(); 758 BshClassPath bcp = new BshClassPath( "Test", urls ); 759 } 760 761 public String toString() { 762 return "BshClassPath "+name+"("+super.toString()+") path= "+path +"\n" 763 + "compPaths = {" + compPaths +" }"; 764 } 765 766 767 771 static class UnqualifiedNameTable extends HashMap { 772 void add( String fullname ) { 773 String name = splitClassname( fullname )[1]; 774 Object have = super.get( name ); 775 776 if ( have == null ) 777 super.put( name, fullname ); 778 else 779 if ( have instanceof AmbiguousName ) 780 ((AmbiguousName)have).add( fullname ); 781 else { 783 AmbiguousName an = new AmbiguousName(); 784 an.add( (String )have ); 785 an.add( fullname ); 786 super.put( name, an ); 787 } 788 } 789 } 790 791 public static class AmbiguousName { 792 List list = new ArrayList(); 793 public void add( String name ) { 794 list.add( name ); 795 } 796 public List get() { 797 return list; 799 } 800 } 801 802 805 void nameSpaceChanged() 806 { 807 if ( nameSourceListeners == null ) 808 return; 809 810 for(int i=0; i<nameSourceListeners.size(); i++) 811 ((NameSource.Listener)(nameSourceListeners.get(i))) 812 .nameSourceChanged( this ); 813 } 814 815 List nameSourceListeners; 816 820 public void addNameSourceListener( NameSource.Listener listener ) { 821 if ( nameSourceListeners == null ) 822 nameSourceListeners = new ArrayList(); 823 nameSourceListeners.add( listener ); 824 } 825 826 827 static MappingFeedback mappingFeedbackListener; 828 829 831 public static void addMappingFeedback( MappingFeedback mf ) 832 { 833 if ( mappingFeedbackListener != null ) 834 throw new RuntimeException ("Unimplemented: already a listener"); 835 mappingFeedbackListener = mf; 836 } 837 838 void startClassMapping() { 839 if ( mappingFeedbackListener != null ) 840 mappingFeedbackListener.startClassMapping(); 841 else 842 System.err.println( "Start ClassPath Mapping" ); 843 } 844 845 void classMapping( String msg ) { 846 if ( mappingFeedbackListener != null ) { 847 mappingFeedbackListener.classMapping( msg ); 848 } else 849 System.err.println( "Mapping: "+msg ); 850 } 851 852 void errorWhileMapping( String s ) { 853 if ( mappingFeedbackListener != null ) 854 mappingFeedbackListener.errorWhileMapping( s ); 855 else 856 System.err.println( s ); 857 } 858 859 void endClassMapping() { 860 if ( mappingFeedbackListener != null ) 861 mappingFeedbackListener.endClassMapping(); 862 else 863 System.err.println( "End ClassPath Mapping" ); 864 } 865 866 public static interface MappingFeedback 867 { 868 public void startClassMapping(); 869 870 876 877 880 public void classMapping( String msg ); 881 882 public void errorWhileMapping( String msg ); 883 884 public void endClassMapping(); 885 } 886 887 } 888 | Popular Tags |