Clover coverage report - XJavaDoc - 1.1
Coverage timestamp: Mon Oct 4 2004 23:49:51 BST
file stats: LOC: 969   Methods: 40
NCLOC: 534   Classes: 3
30 day Evaluation Version distributed via the Maven Jar Repository. Clover is not free. You have 30 days to evaluate it. Please visit http://www.thecortex.net/clover to obtain a licensed version of Clover
 
 Source file Conditionals Statements Methods TOTAL
XJavaDoc.java 57.8% 65.8% 62.5% 63.3%
coverage coverage
 1   
 /*
 2   
  * Copyright (c) 2001-2003 The XDoclet team
 3   
  * All rights reserved.
 4   
  */
 5   
 package xjavadoc;
 6   
 
 7   
 import xjavadoc.filesystem.AbstractFile;
 8   
 import xjavadoc.tags.TagIntrospector;
 9   
 
 10   
 import java.io.PrintStream;
 11   
 import java.util.*;
 12   
 
 13   
 import org.apache.commons.collections.Predicate;
 14   
 import org.apache.commons.collections.CollectionUtils;
 15   
 
 16   
 /**
 17   
  * This class represents the entry-point for xjavadoc classes. Come here to get
 18   
  * classes and packages.
 19   
  *
 20   
  * @author    Aslak Hellesøy
 21   
  * @created   3. januar 2002
 22   
  */
 23   
 public final class XJavaDoc
 24   
 {
 25   
     /**
 26   
      * Indicates whether this XJavaDoc was built with or without unicode support
 27   
      */
 28   
     public final static String IS_UNICODE = "@IS_UNICODE@";
 29   
     /**
 30   
      * messgage level for reporting unqualified classes when there are no imported
 31   
      * packages
 32   
      */
 33   
     public final static int NO_IMPORTED_PACKAGES = 0;
 34   
     /**
 35   
      * messgage level for reporting unqualified classes when there are one or more
 36   
      * imported packages
 37   
      */
 38   
     public final static int ONE_OR_MORE_IMPORTED_PACKAGES = 1;
 39   
 
 40   
     private final static List PRIMITIVES = Collections.unmodifiableList( Arrays.asList( new String[]
 41   
         {"void", "java.lang.Void.TYPE",
 42   
         "byte", "java.lang.Byte.TYPE",
 43   
         "short", "java.lang.Short.TYPE",
 44   
         "int", "java.lang.Integer.TYPE",
 45   
         "long", "java.lang.Long.TYPE",
 46   
         "char", "java.lang.Character.TYPE",
 47   
         "float", "java.lang.Float.TYPE",
 48   
         "double", "java.lang.Double.TYPE",
 49   
         "boolean", "java.lang.Boolean.TYPE"
 50   
         } ) );
 51   
 
 52   
     private static HashMap _primitiveClasses = new HashMap();
 53   
     private final Map  _binaryClasses = new HashMap();
 54   
     private final Map  _unknownClasses = new HashMap();
 55   
     private final Map  _packages = new HashMap();
 56   
     private final Set  _sourceSets = new HashSet();
 57   
 
 58   
     /**
 59   
      * This map contains all the classes that were passed in the source sets,
 60   
      * excluding all inner classes.
 61   
      */
 62   
     private final Map  _sourceSetSourceClasses = new HashMap();
 63   
 
 64   
     /**
 65   
      * This map contains the same classes as _sourceSetSourceClasses, but it is
 66   
      * also populated with additional classes that may be accessed that were not in
 67   
      * the source sets. This can be superclasses, classes referenced in methods,
 68   
      * import statements etc.
 69   
      */
 70   
     private final Map  _allSourceClasses = new HashMap();
 71   
 
 72   
     private final Set  _sourceSetClassNames = new TreeSet();
 73   
 
 74   
     private final Map  _properties = new HashMap();
 75   
 
 76   
     private final Map  _abstractFileClasses = new HashMap();
 77   
 
 78   
     private final XTagFactory _tagFactory = new XTagFactory();
 79   
 
 80   
     /**
 81   
      * This map contains all the classes that were passed in the source sets,
 82   
      * including all inner classes.
 83   
      */
 84   
     private Collection _sourceSetSourceClassesWithInnerClasses = new ArrayList();
 85   
 
 86   
     /**
 87   
      * Remember when we're born. We hate sources that are born after us and we
 88   
      * pretend they don't exist, because if we don't we'll have very unpredictable
 89   
      * behaviour. Well, since we have editor plugin and this is singleton object,
 90   
      * we have to relax our policy on this. Or we will have to restart editor every
 91   
      * time we like to tag the same class again...
 92   
      */
 93   
     private long       _birthday;
 94   
 
 95   
     /**
 96   
      * info, error and warning messages related to parsing and class qualification
 97   
      */
 98   
     private List       _logMessages = new LinkedList();
 99   
 
 100   
     /**
 101   
      * sticky parameter for useNodeParser. _useNodeParser = true -> slower parsing,
 102   
      * but modifiable javaodcs.
 103   
      */
 104   
     private boolean    _useNodeParser = false;
 105   
 
 106   
     /** charset for source file */
 107   
     private String _encoding = null;
 108   
     
 109   
     /** charset for generated file */
 110   
     private String _docEncoding = null;
 111   
     
 112  66
     public XJavaDoc()
 113   
     {
 114  66
         _birthday = System.currentTimeMillis();
 115  66
         for( int i = 0; i < PRIMITIVES.size(); i += 2 )
 116   
         {
 117  594
             addPrimitive( ( String ) PRIMITIVES.get( i ), ( String ) PRIMITIVES.get( i + 1 ) );
 118   
         }
 119   
     }
 120   
 
 121   
     /**
 122   
      * Dump to sytem out the status of XJavadoc.
 123   
      */
 124  0
     public static void printMemoryStatus()
 125   
     {
 126  0
         System.out.println( "ParameterImpl instances:   " + ParameterImpl.instanceCount );
 127  0
         System.out.println( "MethodImpl instances:      " + MethodImpl.instanceCount );
 128  0
         System.out.println( "ConstructorImpl instances: " + ConstructorImpl.instanceCount );
 129  0
         System.out.println( "SimpleNode instances:      " + SimpleNode.instanceCount );
 130  0
         System.out.println( "SourceClass instances:     " + SourceClass.instanceCount );
 131  0
         System.out.println( "XDoc instances:            " + XDoc.instanceCount );
 132  0
         System.out.println( "DefaultXTag instances:     " + DefaultXTag.instanceCount );
 133  0
         System.out.println( "BinaryClass instances:     " + BinaryClass.instanceCount );
 134  0
         System.out.println( "UnknownClass instances:    " + UnknownClass.instanceCount );
 135   
 
 136  0
         System.out.println( "Total memory:    " + ( Runtime.getRuntime().totalMemory() / ( 1024 * 1024 ) ) );
 137  0
         System.out.println( "Free memory:    " + Runtime.getRuntime().freeMemory() / ( 1024 * 1024 ) );
 138   
     }
 139   
 
 140   
     /**
 141   
      * Replaces <code>${xxx}</code> style constructions in the given value with the
 142   
      * string value of the corresponding data types. NOTE: This method was taken
 143   
      * directly from Ant's source code (org.apache.tools.ant.ProjectHelper) and
 144   
      * modified slightly to use a Map instead of a HashMap.
 145   
      *
 146   
      * @param value  The string to be scanned for property references. May be
 147   
      *      <code>null</code> , in which case this method returns immediately with
 148   
      *      no effect.
 149   
      * @param keys   Mapping (String to String) of property names to their values.
 150   
      *      Must not be <code>null</code>.
 151   
      * @return       the original string with the properties replaced, or <code>null</code>
 152   
      *      if the original string is <code>null</code>.
 153   
      */
 154  371
     public static String replaceProperties( String value, Map keys )
 155   
     {
 156  371
         if( value == null )
 157   
         {
 158  0
             return null;
 159   
         }
 160   
 
 161  371
         ArrayList fragments = new ArrayList();
 162  371
         ArrayList propertyRefs = new ArrayList();
 163   
 
 164  371
         parsePropertyString( value, fragments, propertyRefs );
 165   
 
 166  371
         StringBuffer sbuf = new StringBuffer();
 167  371
         Iterator i = fragments.iterator();
 168  371
         Iterator j = propertyRefs.iterator();
 169   
 
 170  371
         while( i.hasNext() )
 171   
         {
 172  387
             String fragment = ( String ) i.next();
 173   
 
 174  387
             if( fragment == null )
 175   
             {
 176  9
                 String propertyName = ( String ) j.next();
 177   
 
 178  9
                 fragment = ( keys.containsKey( propertyName ) ) ? ( String ) keys.get( propertyName )
 179   
                      : "${" + propertyName + '}';
 180   
             }
 181  387
             sbuf.append( fragment );
 182   
         }
 183   
 
 184  371
         return sbuf.toString();
 185   
     }
 186   
 
 187   
     /**
 188   
      * Parses a string containing <code>${xxx}</code> style property references
 189   
      * into two lists. The first list is a collection of text fragments, while the
 190   
      * other is a set of string property names. <code>null</code> entries in the
 191   
      * first list indicate a property reference from the second list. NOTE: This
 192   
      * method was taken directly from Ant's source code
 193   
      * ({@link org.apache.tools.ant.ProjectHelper}) with the BuildException throwing
 194   
      * removed.
 195   
      *
 196   
      * @param value         Text to parse. Must not be <code>null</code>.
 197   
      * @param fragments     List to add text fragments to. Must not be <code>null</code>.
 198   
      * @param propertyRefs  List to add property names to. Must not be <code>null</code>.
 199   
      */
 200  371
     public static void parsePropertyString( String value, List fragments, List propertyRefs )
 201   
     {
 202  371
         int prev = 0;
 203  371
         int pos;
 204   
 
 205  ?
         while( ( pos = value.indexOf( '$', prev ) ) >= 0 )
 206   
         {
 207   
 
 208  9
             if( pos > 0 )
 209   
             {
 210  9
                 String fragment = value.substring( prev, pos );
 211   
 
 212  9
                 fragments.add( fragment );
 213   
             }
 214   
 
 215  9
             if( pos == ( value.length() - 1 ) )
 216   
             {
 217  0
                 fragments.add( "$" );
 218  0
                 prev = pos + 1;
 219   
             }
 220  9
             else if( value.charAt( pos + 1 ) != '{' )
 221   
             {
 222  0
                 fragments.add( value.substring( pos, pos + 1 ) );
 223  0
                 prev = pos + 1;
 224   
             }
 225   
             else
 226   
             {
 227  9
                 int endName = value.indexOf( '}', pos );
 228   
 
 229  9
                 if( endName < 0 )
 230   
                 {
 231   
                     // In Ant this is a BuildException condition as its an
 232   
                     // incomplete property reference. Here we'll leave it
 233   
                     // in the output string
 234  0
                     String fragment = value.substring( pos );
 235   
 
 236  0
                     fragments.add( fragment );
 237  0
                     continue;
 238   
                 }
 239   
 
 240  9
                 String propertyName = value.substring( pos + 2, endName );
 241   
 
 242  9
                 fragments.add( null );
 243  9
                 propertyRefs.add( propertyName );
 244  9
                 prev = endName + 1;
 245   
             }
 246   
         }
 247   
 
 248  371
         if( prev < value.length() )
 249   
         {
 250  369
             String fragment = value.substring( prev );
 251   
 
 252  369
             fragments.add( fragment );
 253   
         }
 254   
     }
 255   
 
 256   
     /**
 257   
      * Gets the Primitive attribute of the XJavaDoc class
 258   
      *
 259   
      * @param name  Describe what the parameter does
 260   
      * @return      The Primitive value
 261   
      */
 262  2275
     static Primitive getPrimitive( String name )
 263   
     {
 264  2275
         return ( Primitive ) _primitiveClasses.get( name );
 265   
     }
 266   
 
 267   
     /**
 268   
      * Gets the file the pe is contained in. Note: calling this method with a
 269   
      * XProgramElement not from source (but from a binary or unknown class) will
 270   
      * result in a ClassCastException, so don't do that. This method is only used
 271   
      * for diagnostics in error reporting.
 272   
      *
 273   
      * @param pe  the program element we want the source for.
 274   
      * @return    the file the program element is contained in.
 275   
      */
 276  0
     static AbstractFile getSourceFileFor( XProgramElement pe )
 277   
     {
 278  0
         SourceClass containingClass = null;
 279   
 
 280  0
         if( !( pe instanceof SourceClass ) )
 281   
         {
 282   
             // pe is a field, method or constructor. get the surrounding class
 283  0
             containingClass = ( SourceClass ) pe.getContainingClass();
 284   
         }
 285   
         else
 286   
         {
 287  0
             containingClass = ( SourceClass ) pe;
 288   
         }
 289   
         // in case the class is an inner class, loop until we have the outermost.
 290  0
         while( containingClass.getContainingClass() != null )
 291   
         {
 292  0
             containingClass = ( SourceClass ) containingClass.getContainingClass();
 293   
         }
 294  0
         return containingClass.getFile();
 295   
     }
 296   
 
 297   
     /**
 298   
      * Describe the method
 299   
      *
 300   
      * @param name  Describe the method parameter
 301   
      * @param type  The feature to be added to the Primitive attribute
 302   
      */
 303  594
     private final void addPrimitive( String name, String type )
 304   
     {
 305  594
         _primitiveClasses.put( name, new Primitive( this, name, type ) );
 306   
     }
 307   
 
 308  0
     public Collection getSourceClasses( Predicate predicate )
 309   
     {
 310  0
         return CollectionUtils.select( getSourceClasses(), predicate );
 311   
     }
 312   
 
 313   
     /**
 314   
      * Returns all classes in the registered source sets, including inner classes
 315   
      *
 316   
      * @return   A Collection of XClass
 317   
      */
 318  2
     public Collection getSourceClasses()
 319   
     {
 320  2
         if( _sourceSetSourceClassesWithInnerClasses.isEmpty() )
 321   
         {
 322   
             // Add the regular classes
 323  2
             _sourceSetSourceClassesWithInnerClasses.addAll( getOuterSourceClasses() );
 324   
 
 325   
             // Add inner classes
 326  2
             for( Iterator outers = getOuterSourceClasses().iterator(); outers.hasNext();  )
 327   
             {
 328  16
                 addInnerClassRecursive( (XClass) outers.next(), _sourceSetSourceClassesWithInnerClasses );
 329   
             }
 330   
         }
 331  2
         return Collections.unmodifiableCollection( _sourceSetSourceClassesWithInnerClasses );
 332   
     }
 333   
 
 334   
     /**
 335   
      * Returns the packages of the specified classes during parsing.
 336   
      *
 337   
      * @return   Describe the return value
 338   
      */
 339  1
     public Collection getSourcePackages()
 340   
     {
 341  1
         Set packages = new TreeSet();
 342  1
         Collection classes = getSourceClasses();
 343   
 
 344  1
         for( Iterator i = classes.iterator(); i.hasNext();  )
 345   
         {
 346  21
             packages.add( ((XClass)i.next()).getContainingPackage() );
 347   
         }
 348   
 
 349  1
         return Collections.unmodifiableCollection( packages );
 350   
     }
 351   
 
 352  0
     public Map getPropertyMap()
 353   
     {
 354  0
         return Collections.unmodifiableMap( _properties );
 355   
     }
 356   
 
 357   
     /**
 358   
      * Get the XClass corresponding to the qualifiedName. This can be a class from
 359   
      * source, a precompiled class or a primitive. UnknownClass is never returned
 360   
      * from this method, unless it has been previously instantiated. <b>IMPORTANT:
 361   
      * </b> If the Java source can be located, an instance of SourceClass will be
 362   
      * returned. -Even if that file was not among the files in the fileset or
 363   
      * sourceset. <b>IMPORTANT: </b> If qualifiedName represents an inner class, an
 364   
      * UnknownClass will be returned unless the enclousing "outer" class has been
 365   
      * resolved first.
 366   
      *
 367   
      * @param qualifiedName  Fully qualified class name
 368   
      * @return               The XClass value
 369   
      */
 370  1689
     public XClass getXClass( String qualifiedName )
 371   
     {
 372  1689
         if( qualifiedName.equals( "" ) )
 373   
         {
 374  0
             throw new IllegalStateException( "Classname can't be empty String" );
 375   
         }
 376   
 
 377  1689
         XClass result = null;
 378  1689
         Primitive primitive;
 379  1689
         SourceClass sourceClass;
 380  1689
         BinaryClass binaryClass;
 381  1689
         UnknownClass unknownClass;
 382   
 
 383   
         // first, check all caches
 384  ?
         if( ( primitive = getPrimitive( qualifiedName ) ) != null )
 385   
         {
 386  8
             result = primitive;
 387   
         }
 388  ?
         else if( ( sourceClass = ( SourceClass ) _allSourceClasses.get( qualifiedName ) ) != null )
 389   
         {
 390  142
             result = sourceClass;
 391   
         }
 392  ?
         else if( ( binaryClass = ( BinaryClass ) _binaryClasses.get( qualifiedName ) ) != null )
 393   
         {
 394  509
             result = binaryClass;
 395   
         }
 396  ?
         else if( ( unknownClass = ( UnknownClass ) _unknownClasses.get( qualifiedName ) ) != null )
 397   
         {
 398  0
             result = unknownClass;
 399   
         }
 400   
         else
 401   
         {
 402   
             // Let's try to read the class from source
 403  1030
             if( sourceExists( qualifiedName ) )
 404   
             {
 405   
                 // The source exists. Let's parse it.
 406  336
                 sourceClass = scanAndPut( qualifiedName );
 407  336
                 result = sourceClass;
 408   
             }
 409   
             else
 410   
             {
 411   
                 // Couldn't find the class among the sources.
 412   
                 // Try a BinaryClass
 413  694
                 Class clazz = getClass( qualifiedName );
 414   
 
 415  694
                 if( clazz != null )
 416   
                 {
 417  684
                     binaryClass = new BinaryClass( this, clazz );
 418  684
                     _binaryClasses.put( qualifiedName, binaryClass );
 419  684
                     result = binaryClass;
 420   
                 }
 421   
                 else
 422   
                 {
 423   
                     // Binary didn't exist either. Return an UnknownClass
 424  10
                     result = new UnknownClass( this, qualifiedName );
 425  10
                     _unknownClasses.put( qualifiedName, result );
 426   
                 }
 427   
             }
 428   
         }
 429  1689
         return result;
 430   
     }
 431   
 
 432   
     /**
 433   
      * Returns the package. The package must be one of the packages of the sources.
 434   
      * Other packages, such as java.lang are not available.
 435   
      *
 436   
      * @param packageName
 437   
      * @return             an XPackage, or null if the packageName is not among the
 438   
      *      sources.
 439   
      */
 440  1
     public XPackage getSourcePackage( String packageName )
 441   
     {
 442   
         // This is not optimal, but this method is primarily used for testing.
 443  3
         for( Iterator i = getSourcePackages().iterator(); i.hasNext();  )
 444   
         {
 445  3
             XPackage p = ( XPackage ) i.next();
 446   
 
 447  3
             if( p.getName().equals( packageName ) )
 448   
             {
 449  1
                 return p;
 450   
             }
 451   
         }
 452  0
         return null;
 453   
     }
 454   
 
 455   
     /**
 456   
      * This method can be called prior to parsing so that all classes are parsed
 457   
      * with AST (to make it possible to write the source back to disk)
 458   
      *
 459   
      * @param useNodeParser
 460   
      */
 461  4
     public void setUseNodeParser( boolean useNodeParser )
 462   
     {
 463  4
         _useNodeParser = useNodeParser;
 464   
     }
 465   
 
 466  25
     public void setPropertyMap( Map properties )
 467   
     {
 468  25
         _properties.putAll( properties );
 469   
     }
 470   
 
 471   
     /**
 472   
      * Resets the caches.
 473   
      *
 474   
      * @param resetTimeStamp  true if timestamps should be reset too.
 475   
      */
 476  60
     public void reset( boolean resetTimeStamp )
 477   
     {
 478  60
         for( Iterator iterator =  _packages.values().iterator(); iterator.hasNext();  )
 479   
         {
 480  0
             XPackage xPackage = (XPackage) iterator.next();
 481   
 
 482  0
             for( Iterator i = xPackage.getClasses().iterator(); i.hasNext();  )
 483   
             {
 484  0
                 AbstractClass clazz = ( AbstractClass ) i.next();
 485   
 
 486  0
                 clazz.reset();
 487   
             }
 488   
         }
 489  60
         _binaryClasses.clear();
 490  60
         _unknownClasses.clear();
 491  60
         _packages.clear();
 492  60
         _sourceSets.clear();
 493  60
         _sourceSetSourceClasses.clear();
 494  60
         _sourceSetClassNames.clear();
 495  60
         _allSourceClasses.clear();
 496  60
         _sourceSetSourceClassesWithInnerClasses.clear();
 497   
 
 498  60
         _logMessages.clear();
 499  60
         _properties.clear();
 500  60
         _abstractFileClasses.clear();
 501   
 
 502   
         //_primitiveClasses = null;
 503   
 
 504   
         //AbstractProgramElement.NULL_XDOC = null;
 505   
 
 506   
         // if we start new life, we can as well get new birth certificate,
 507   
         // so classes saved in previous life can be loaded again without
 508   
         // hating them :)
 509  60
         if( resetTimeStamp )
 510   
         {
 511  60
             _birthday = System.currentTimeMillis();
 512   
         }
 513   
     }
 514   
 
 515   
     /**
 516   
      * Prints the log messages encountered during parsing
 517   
      *
 518   
      * @param out
 519   
      * @param level
 520   
      */
 521  0
     public void printLogMessages( PrintStream out, int level )
 522   
     {
 523  0
         boolean printedHeader = false;
 524   
 
 525  0
         for( Iterator i = _logMessages.iterator(); i.hasNext();  )
 526   
         {
 527  0
             LogMessage m = ( LogMessage ) i.next();
 528   
 
 529  0
             if( m._level == level )
 530   
             {
 531  0
                 if( !printedHeader )
 532   
                 {
 533  0
                     if( level == ONE_OR_MORE_IMPORTED_PACKAGES )
 534   
                     {
 535   
                         // Could be an inner class too!!!
 536  0
                         out.println( "WARNING: Some classes refer to other classes that were not found among the sources or on the classpath." );
 537  0
                         out.println( "         (Perhaps the referred class doesn't exist? Hasn't been generated yet?)" );
 538  0
                         out.println( "         The referring classes do not import any fully qualified classes matching these classes." );
 539  0
                         out.println( "         Since at least one package is imported, it is impossible for xjavadoc to figure out" );
 540  0
                         out.println( "         what package the referred classes belong to. The classes are:" );
 541   
                     }
 542   
                     else
 543   
                     {
 544  0
                         out.println( "INFO:    Some classes refer to other classes that were not found among the sources or on the classpath." );
 545  0
                         out.println( "         (Perhaps the referred class doesn't exist? Hasn't been generated yet?)" );
 546  0
                         out.println( "         The referring classes do not import any fully qualified classes matching these classes." );
 547  0
                         out.println( "         However, since no packages are imported, xjavadoc has assumed that the referred classes" );
 548  0
                         out.println( "         belong to the same package as the referring class. The classes are:" );
 549   
                     }
 550  0
                     printedHeader = true;
 551   
                 }
 552  0
                 out.println( m._sourceClass.getFile().getPath() + " --> " + m._unqualifiedClassName + " qualified to " + m._unknownClass.getQualifiedName() );
 553   
             }
 554   
         }
 555   
     }
 556   
 
 557   
     /**
 558   
      * Adds a new set of java sources to be parsed.
 559   
      *
 560   
      * @param sourceSet  a set of java sources.
 561   
      */
 562  60
     public void addSourceSet( SourceSet sourceSet )
 563   
     {
 564  60
         _sourceSets.add( sourceSet );
 565  60
         for( int j = 0; j < sourceSet.getSize(); j++ )
 566   
         {
 567  250
             String qualifiedName = sourceSet.getQualifiedName( j );
 568   
 
 569  250
             if( _sourceSetClassNames.contains( qualifiedName ) )
 570   
             {
 571  0
                 String msg = "The class \"" + qualifiedName + "\" occurs in more than one fileset. That's illegal.";
 572   
 
 573  0
                 System.err.println( msg );
 574   
             }
 575  250
             _sourceSetClassNames.add( qualifiedName );
 576   
         }
 577   
     }
 578   
 
 579  0
     public void addAbstractFile( String qualifiedName, AbstractFile file )
 580   
     {
 581  0
         _abstractFileClasses.put( qualifiedName, file );
 582   
 
 583  0
         if( _sourceSetClassNames.contains( qualifiedName ) )
 584   
         {
 585  0
             String msg = "The class \"" + qualifiedName + "\" occurs in more than one fileset. That's illegal.";
 586   
 
 587  0
             System.err.println( msg );
 588   
         }
 589  0
         _sourceSetClassNames.add( qualifiedName );
 590   
     }
 591   
 
 592   
     /**
 593   
      * Describe what the method does
 594   
      *
 595   
      * @param className                qualified name of class
 596   
      * @param tagName                  tag name
 597   
      * @param parameterName            parameter name
 598   
      * @param parameterValue           new parameter value
 599   
      * @param tagIndex                 index of tag (??)
 600   
      * @param methodNameWithSignature  method name followed by signature. no
 601   
      *      spaces. Ex:<br>
 602   
      *      <code>doIt(java.lang.String,int)</code>
 603   
      * @return                         the class corresponding to the className
 604   
      * @exception XJavaDocException    If the tag for some reason couldn't be
 605   
      *      updated
 606   
      */
 607  2
     public XClass updateMethodTag(
 608   
         String className,
 609   
         String methodNameWithSignature,
 610   
         String tagName,
 611   
         String parameterName,
 612   
         String parameterValue,
 613   
         int tagIndex
 614   
          ) throws XJavaDocException
 615   
     {
 616  2
         XClass clazz = getXClass( className );
 617  2
         XMethod method = clazz.getMethod( methodNameWithSignature );
 618   
 
 619  2
         XDoc doc = method.getDoc();
 620   
 
 621  2
         doc.updateTagValue( tagName, parameterName, parameterValue, tagIndex );
 622   
 
 623  2
         return clazz;
 624   
     }
 625   
 
 626   
     /**
 627   
      * Describe what the method does
 628   
      *
 629   
      * @param className              Describe what the parameter does
 630   
      * @param tagName                Describe what the parameter does
 631   
      * @param parameterName          Describe what the parameter does
 632   
      * @param parameterValue         Describe what the parameter does
 633   
      * @param tagIndex               Describe what the parameter does
 634   
      * @return                       Describe the return value
 635   
      * @exception XJavaDocException  Describe the exception
 636   
      */
 637  1
     public XClass updateClassTag(
 638   
         String className,
 639   
         String tagName,
 640   
         String parameterName,
 641   
         String parameterValue,
 642   
         int tagIndex
 643   
          ) throws XJavaDocException
 644   
     {
 645  1
         XClass clazz = getXClass( className );
 646  1
         XDoc doc = clazz.getDoc();
 647   
 
 648  1
         doc.updateTagValue( tagName, parameterName, parameterValue, tagIndex );
 649  1
         return clazz;
 650   
     }
 651   
 
 652  371
     public String dereferenceProperties( String value )
 653   
     {
 654   
 
 655  371
         return replaceProperties( value, _properties );
 656   
     }
 657   
 
 658   
     /**
 659   
      * @param qualifiedClassName
 660   
      * @return                    true if the class exists, either as source or
 661   
      *      binary
 662   
      */
 663  1410
     final boolean classExists( final String qualifiedClassName )
 664   
     {
 665   
         // See if we have the source
 666  1410
         if( sourceExists( qualifiedClassName ) )
 667   
         {
 668  224
             return true;
 669   
         }
 670   
         // See if we kand find the class (binary)
 671  1186
         else if( getClass( qualifiedClassName ) != null )
 672   
         {
 673  171
             return true;
 674   
         }
 675   
         else
 676   
         {
 677  1015
             return false;
 678   
         }
 679   
     }
 680   
 
 681  0
     void logMessage( SourceClass clazz, UnknownClass unknownClass, String unqualifiedClassName, int level )
 682   
     {
 683  0
         _logMessages.add( new LogMessage( clazz, unknownClass, unqualifiedClassName, level ) );
 684   
     }
 685   
 
 686   
     /**
 687   
      * Describe the method
 688   
      *
 689   
      * @param packageName  Describe the method parameter
 690   
      * @return             Describe the return value
 691   
      */
 692  1778
     XPackage addPackageMaybe( String packageName )
 693   
     {
 694  1778
         XPackage result = ( XPackage ) _packages.get( packageName );
 695   
 
 696  1778
         if( result == null )
 697   
         {
 698   
             // The package doesn't exist yet. Add it then
 699  412
             result = new XPackage( packageName );
 700  412
             _packages.put( packageName, result );
 701   
         }
 702  1778
         return result;
 703   
     }
 704   
 
 705   
     /**
 706   
      * Adds a source class to the cache. This method is also called from JavaParser
 707   
      * when parsing inner classes.
 708   
      *
 709   
      * @param sourceClass  Describe the method parameter
 710   
      */
 711  779
     void addSourceClass( SourceClass sourceClass )
 712   
     {
 713  779
         _allSourceClasses.put( sourceClass.getQualifiedName(), sourceClass );
 714   
 
 715   
         // Also add it to _sourceSetSourceClasses if it was among the source sets
 716   
         // or if it is an "extra" class (this is due to XJD-8)
 717  779
         if( _sourceSetClassNames.contains( sourceClass.getQualifiedName() ) || sourceClass.isExtraClass() )
 718   
         {
 719  130
             _sourceSetSourceClasses.put( sourceClass.getQualifiedName(), sourceClass );
 720   
         }
 721   
     }
 722   
 
 723   
     /**
 724   
      * Returns the Class with the given name, or null if unknown.
 725   
      *
 726   
      * @param qualifiedName  Describe what the parameter does
 727   
      * @return               The Class value
 728   
      */
 729  1880
     private final Class getClass( String qualifiedName )
 730   
     {
 731  1880
         try
 732   
         {
 733  1880
             return Class.forName( qualifiedName, false, getClass().getClassLoader() );
 734   
         }
 735   
         catch( Throwable e )
 736   
         {
 737   
             // e can be LinkageError, ClassNotFoundException or ExceptionInInitializerError
 738   
             // We don't care what we get. If the forName fails, we don't have a class to return
 739  1025
             return null;
 740   
         }
 741   
     }
 742   
 
 743   
     /**
 744   
      * Returns all classes in the registered source sets
 745   
      *
 746   
      * @return   A Collection of XClass
 747   
      */
 748  4
     private Collection getOuterSourceClasses()
 749   
     {
 750  4
         if( _sourceSetSourceClasses.isEmpty() )
 751   
         {
 752  2
             for( Iterator i = _sourceSetClassNames.iterator(); i.hasNext();  )
 753   
             {
 754  20
                 String qualifiedName = ( String ) i.next();
 755   
 
 756   
                 /*
 757   
                  * This will result in the class being added to
 758   
                  * _sourceSetSourceClasses AND _allSourceClasses
 759   
                  */
 760  20
                 getXClass( qualifiedName );
 761   
             }
 762   
 
 763  2
             for( Iterator iterator = _abstractFileClasses.keySet().iterator(); iterator.hasNext();  )
 764   
             {
 765  0
                 String fqcn = ( String ) iterator.next();
 766   
 
 767  0
                 getXClass( fqcn );
 768   
             }
 769   
         }
 770   
 
 771   
         //a new collection should be created, becuase we might be looping over classes and generatePerClass for each
 772   
         //one, now if a new class is discovered and registered (maybe we are analyzing a new class, maybe a template
 773   
         //is now interested in superclass of a class, maybe ...), we'll get a ConcurrentModificationException. To prevent
 774   
         //it we need to looping over a new collection (initiated from XJavaDoc mostly).
 775  4
         Collection new_collection = new ArrayList( _sourceSetSourceClasses.values() );
 776   
 
 777  4
         return Collections.unmodifiableCollection( new_collection );
 778   
     }
 779   
 
 780   
     /**
 781   
      * Gets the SourceFile attribute of the XJavaDoc object
 782   
      *
 783   
      * @param qualifiedName  Describe what the parameter does
 784   
      * @return               The SourceFile value
 785   
      */
 786  2776
     private AbstractFile getSourceFile( String qualifiedName )
 787   
     {
 788   
         // loop over all SourceSets. If a source is found more than once -> bang!
 789  2776
         AbstractFile found = null;
 790   
 
 791  2776
         for( Iterator i = _sourceSets.iterator(); i.hasNext();  )
 792   
         {
 793  2737
             SourceSet sourceSet = ( SourceSet ) i.next();
 794  2737
             AbstractFile javaFile = sourceSet.getSourceFile( qualifiedName );
 795   
 
 796  2737
             if( javaFile != null )
 797   
             {
 798   
                 // isn't this an impossible situation?  Have a look at addSourceSet - we check
 799   
                 // there to ensure that no classes are added twice.....
 800   
 //                if( found != null && !found.getAbsolutePath().equals( javaFile.getAbsolutePath() ) )
 801   
 //                {
 802   
 //                    throw new IllegalStateException( "Ambiguous sources for " + qualifiedName + " : " + found.getAbsolutePath() + " or " + javaFile.getAbsolutePath() );
 803   
 //                }
 804  896
                 found = javaFile;
 805   
             }
 806   
         }
 807  2776
         return found;
 808   
     }
 809   
 
 810   
     /**
 811   
      * Recursively adds inner classes to a collection
 812   
      *
 813   
      * @param outer  The feature to be added to the InnerClassRecursive attribute
 814   
      * @param c      The feature to be added to the InnerClassRecursive attribute
 815   
      */
 816  42
     private void addInnerClassRecursive( XClass outer, Collection c )
 817   
     {
 818  42
         for( Iterator inners = outer.getInnerClasses().iterator(); inners.hasNext();  )
 819   
         {
 820  26
             XClass inner = (XClass) inners.next();
 821   
 
 822  26
             c.add( inner );
 823  26
             addInnerClassRecursive( inner, c );
 824   
         }
 825   
     }
 826   
 
 827   
     /**
 828   
      * Checks is the source exists
 829   
      *
 830   
      * @param qualifiedName  the class to check for
 831   
      * @return               true if source exists.
 832   
      */
 833  2440
     private boolean sourceExists( String qualifiedName )
 834   
     {
 835   
         /*
 836   
          * When used with XDoclet, some classes might be resolved by xjavadoc
 837   
          * after they are generated by XDoclet.
 838   
          * (An example is e.g. a primary key
 839   
          * class that doesn't exist before XDoclet is run, and which might be
 840   
          * referenced by some of the methods in the @tagged classes).
 841   
          * We will pretend that any classes that didn't exist before xjavadoc started
 842   
          * scanning sources (generated classes) don't exist. This is to avoid modifying
 843   
          * collections that are being iterated over in parallel, as this will throw
 844   
          * ConcurrentModificationException. (Aslak)
 845   
          */
 846  2440
         AbstractFile sourceFile = getSourceFile( qualifiedName );
 847   
 
 848  2440
         if( sourceFile != null )
 849   
         {
 850  560
             if( sourceFile.lastModified() > _birthday )
 851   
             {
 852   
                 // The source appeared after xjavadoc was reset. Pretend it doesn't exist.
 853  0
                 System.out.println( "XJavaDoc Ignoring class " + qualifiedName + " in " + sourceFile.getPath() + ". It was generated (" + new Date( sourceFile.lastModified() ) + ") after XJavaDoc's timestamp was reset (" + new Date( _birthday ) + ")" );
 854  0
                 return false;
 855   
             }
 856   
         }
 857   
 
 858  2440
         boolean sourceFileExists = sourceFile != null;
 859   
 
 860  2440
         if( !sourceFileExists )
 861   
         {
 862  1880
             sourceFileExists = _abstractFileClasses.containsKey( qualifiedName );
 863   
         }
 864   
 
 865  2440
         return sourceFileExists;
 866   
     }
 867   
 
 868   
     /**
 869   
      * Scan's a class and puts it in the cache.
 870   
      *
 871   
      * @param qualifiedName  Describe what the parameter does
 872   
      * @return               Describe the return value
 873   
      */
 874  336
     private SourceClass scanAndPut( String qualifiedName )
 875   
     {
 876  336
         AbstractFile sourceFile = getSourceFile( qualifiedName );
 877   
 
 878  336
         sourceFile = sourceFile != null ? sourceFile : ( AbstractFile ) _abstractFileClasses.get( qualifiedName );
 879   
 
 880  336
         if( sourceFile == null )
 881   
         {
 882  0
             throw new IllegalStateException( "No source found for " + qualifiedName );
 883   
         }
 884   
 
 885  336
         SourceClass sourceClass = new SourceClass( this, sourceFile, _useNodeParser, _tagFactory ,_encoding);
 886   
 
 887   
         // now that the entire file is parsed, validate the tags.
 888  336
         if( _tagFactory.isValidating() )
 889   
         {
 890  0
             sourceClass.validateTags();
 891   
         }
 892   
 
 893   
 //        addSourceClass( sourceClass );
 894   
 
 895  336
         return sourceClass;
 896   
     }
 897   
 
 898  0
     public XTagFactory getTagFactory() {
 899  0
         return _tagFactory;
 900   
     }
 901   
 
 902   
     /**
 903   
      * Registers tags.
 904   
      *
 905   
      * @param classpath where tags are found.
 906   
      */
 907  0
     public void registerTags( String classpath ) {
 908  0
         new TagIntrospector().registerTags( classpath, getTagFactory() );
 909   
     }
 910   
 
 911   
     public final static class NoInnerClassesPredicate implements Predicate
 912   
     {
 913  0
         public boolean evaluate( Object o )
 914   
         {
 915  0
             XClass clazz = ( XClass ) o;
 916   
 
 917  0
             return !clazz.isInner();
 918   
         }
 919   
     }
 920   
 
 921   
     class LogMessage
 922   
     {
 923   
         public final SourceClass _sourceClass;
 924   
         public final UnknownClass _unknownClass;
 925   
         public final String _unqualifiedClassName;
 926   
         public final int  _level;
 927  0
         LogMessage( SourceClass sourceClass, UnknownClass unknownClass, String unqualifiedClassName, int level )
 928   
         {
 929  0
             _sourceClass = sourceClass;
 930  0
             _unknownClass = unknownClass;
 931  0
             _unqualifiedClassName = unqualifiedClassName;
 932  0
             _level = level;
 933   
         }
 934   
     }
 935   
 
 936   
     /**
 937   
      * Getter for source file charset.
 938   
      * @return encoding
 939   
      */
 940  0
     public String getEncoding() {
 941  0
         return _encoding;
 942   
     }
 943   
 
 944   
     /**
 945   
      * Setter for source file charset.
 946   
      * @param string encoding
 947   
      */
 948  0
     public void setEncoding(String encoding) {
 949  0
         _encoding = encoding;
 950   
     }
 951   
 
 952   
     /**
 953   
      * Getter for generated file charset.
 954   
      * @return encoding
 955   
      */
 956  0
     public String getDocEncoding() {
 957  0
         return _docEncoding;
 958   
     }
 959   
 
 960   
     /**
 961   
      * Setter for generated file charset.
 962   
      * @param string encoding 
 963   
      */
 964  0
     public void setDocEncoding(String docencoding) {
 965  0
         _docEncoding = docencoding;
 966   
     }
 967   
 
 968   
 }
 969