View Javadoc

    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 	public XJavaDoc()
    113 	{
    114 		_birthday = System.currentTimeMillis();
    115         for( int i = 0; i < PRIMITIVES.size(); i += 2 )
    116         {
    117             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 	public static void printMemoryStatus()
    125 	{
    126 		System.out.println( "ParameterImpl instances:   " + ParameterImpl.instanceCount );
    127 		System.out.println( "MethodImpl instances:      " + MethodImpl.instanceCount );
    128 		System.out.println( "ConstructorImpl instances: " + ConstructorImpl.instanceCount );
    129 		System.out.println( "SimpleNode instances:      " + SimpleNode.instanceCount );
    130 		System.out.println( "SourceClass instances:     " + SourceClass.instanceCount );
    131 		System.out.println( "XDoc instances:            " + XDoc.instanceCount );
    132 		System.out.println( "DefaultXTag instances:     " + DefaultXTag.instanceCount );
    133 		System.out.println( "BinaryClass instances:     " + BinaryClass.instanceCount );
    134 		System.out.println( "UnknownClass instances:    " + UnknownClass.instanceCount );
    135
    136 		System.out.println( "Total memory:    " + ( Runtime.getRuntime().totalMemory() / ( 1024 * 1024 ) ) );
    137 		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 	public static String replaceProperties( String value, Map keys )
    155 	{
    156 		if( value == null )
    157 		{
    158 			return null;
    159 		}
    160
    161 		ArrayList fragments = new ArrayList();
    162 		ArrayList propertyRefs = new ArrayList();
    163
    164 		parsePropertyString( value, fragments, propertyRefs );
    165
    166 		StringBuffer sbuf = new StringBuffer();
    167 		Iterator i = fragments.iterator();
    168 		Iterator j = propertyRefs.iterator();
    169
    170 		while( i.hasNext() )
    171 		{
    172 			String fragment = ( String ) i.next();
    173
    174 			if( fragment == null )
    175 			{
    176 				String propertyName = ( String ) j.next();
    177
    178 				fragment = ( keys.containsKey( propertyName ) ) ? ( String ) keys.get( propertyName )
    179 					 : "${" + propertyName + '}';
    180 			}
    181 			sbuf.append( fragment );
    182 		}
    183
    184 		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 	public static void parsePropertyString( String value, List fragments, List propertyRefs )
    201 	{
    202 		int prev = 0;
    203 		int pos;
    204
    205 		while( ( pos = value.indexOf( '$', prev ) ) >= 0 )
    206 		{
    207
    208 			if( pos > 0 )
    209 			{
    210 				String fragment = value.substring( prev, pos );
    211
    212 				fragments.add( fragment );
    213 			}
    214
    215 			if( pos == ( value.length() - 1 ) )
    216 			{
    217 				fragments.add( "$" );
    218 				prev = pos + 1;
    219 			}
    220 			else if( value.charAt( pos + 1 ) != '{' )
    221 			{
    222 				fragments.add( value.substring( pos, pos + 1 ) );
    223 				prev = pos + 1;
    224 			}
    225 			else
    226 			{
    227 				int endName = value.indexOf( '}', pos );
    228
    229 				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 					String fragment = value.substring( pos );
    235
    236 					fragments.add( fragment );
    237 					continue;
    238 				}
    239
    240 				String propertyName = value.substring( pos + 2, endName );
    241
    242 				fragments.add( null );
    243 				propertyRefs.add( propertyName );
    244 				prev = endName + 1;
    245 			}
    246 		}
    247
    248 		if( prev < value.length() )
    249 		{
    250 			String fragment = value.substring( prev );
    251
    252 			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 	static Primitive getPrimitive( String name )
    263 	{
    264 		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 	static AbstractFile getSourceFileFor( XProgramElement pe )
    277 	{
    278 		SourceClass containingClass = null;
    279
    280 		if( !( pe instanceof SourceClass ) )
    281 		{
    282 			// pe is a field, method or constructor. get the surrounding class
    283 			containingClass = ( SourceClass ) pe.getContainingClass();
    284 		}
    285 		else
    286 		{
    287 			containingClass = ( SourceClass ) pe;
    288 		}
    289 		// in case the class is an inner class, loop until we have the outermost.
    290 		while( containingClass.getContainingClass() != null )
    291 		{
    292 			containingClass = ( SourceClass ) containingClass.getContainingClass();
    293 		}
    294 		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 	private final void addPrimitive( String name, String type )
    304 	{
    305 		_primitiveClasses.put( name, new Primitive( this, name, type ) );
    306 	}
    307
    308 	public Collection getSourceClasses( Predicate predicate )
    309 	{
    310 		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 	public Collection getSourceClasses()
    319 	{
    320 		if( _sourceSetSourceClassesWithInnerClasses.isEmpty() )
    321 		{
    322 			// Add the regular classes
    323 			_sourceSetSourceClassesWithInnerClasses.addAll( getOuterSourceClasses() );
    324
    325 			// Add inner classes
    326 			for( Iterator outers = getOuterSourceClasses().iterator(); outers.hasNext();  )
    327 			{
    328 				addInnerClassRecursive( (XClass) outers.next(), _sourceSetSourceClassesWithInnerClasses );
    329 			}
    330 		}
    331 		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 	public Collection getSourcePackages()
    340 	{
    341 		Set packages = new TreeSet();
    342 		Collection classes = getSourceClasses();
    343
    344 		for( Iterator i = classes.iterator(); i.hasNext();  )
    345 		{
    346 			packages.add( ((XClass)i.next()).getContainingPackage() );
    347 		}
    348
    349 		returng> Collections.unmodifiableCollection( packages );
    350 	}
    351
    352 	public Map getPropertyMap()
    353 	{
    354 		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 	public XClass getXClass( String qualifiedName )
    371 	{
    372 		if( qualifiedName.equals( "" ) )
    373 		{
    374 			throw new IllegalStateException( "Classname can't be empty String" );
    375 		}
    376
    377 		XClass result = null;
    378 		Primitive primitive;
    379 		SourceClass sourceClass;
    380 		BinaryClass binaryClass;
    381 		UnknownClass unknownClass;
    382
    383 		// first, check all caches
    384 		if( ( primitive = getPrimitive( qualifiedName ) ) != null )
    385 		{
    386 			result = primitive;
    387 		}
    388 		else if( ( sourceClass = ( SourceClass ) _allSourceClasses.get( qualifiedName ) ) != null )
    389 		{
    390 			result = sourceClass;
    391 		}
    392 		else if( ( binaryClass = ( BinaryClass ) _binaryClasses.get( qualifiedName ) ) != null )
    393 		{
    394 			result = binaryClass;
    395 		}
    396 		else if( ( unknownClass = ( UnknownClass ) _unknownClasses.get( qualifiedName ) ) != null )
    397 		{
    398 			result = unknownClass;
    399 		}
    400 		else
    401 		{
    402 			// Let's try to read the class from source
    403 			if( sourceExists( qualifiedName ) )
    404 			{
    405 				// The source exists. Let's parse it.
    406 				sourceClass = scanAndPut( qualifiedName );
    407 				result = sourceClass;
    408 			}
    409 			else
    410 			{
    411 				// Couldn't find the class among the sources.
    412 				// Try a BinaryClass
    413 				Class clazz = getClass( qualifiedName );
    414
    415 				if( clazz != null )
    416 				{
    417 					binaryClass = new BinaryClass( this, clazz );
    418 					_binaryClasses.put( qualifiedName, binaryClass );
    419 					result = binaryClass;
    420 				}
    421 				else
    422 				{
    423 					// Binary didn't exist either. Return an UnknownClass
    424 					result = new UnknownClass( this, qualifiedName );
    425 					_unknownClasses.put( qualifiedName, result );
    426 				}
    427 			}
    428 		}
    429 		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 	publicXPackage getSourcePackage( String packageName )/package-summary.html">> XPackage getSourcePackage( String packageName )
    441 	{
    442 		// This is not optimal, but this method is primarily used for testing.
    443 		for( Iterator i = getSourcePackages().iterator(); i.hasNext();  )
    444 		{
    445 			XPackage p = ( XPackage ) i.next();
    446
    447 			ifg>( p.getName().equals( packageName ) )
    448 			{
    449 				return p;
    450 			}
    451 		}
    452 		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 	public void setUseNodeParser( boolean useNodeParser )
    462 	{
    463 		_useNodeParser = useNodeParser;
    464 	}
    465
    466 	public void setPropertyMap( Map properties )
    467 	{
    468 		_properties.putAll( properties );
    469 	}
    470
    471 	/***
    472 	 * Resets the caches.
    473 	 *
    474 	 * @param resetTimeStamp  true if timestamps should be reset too.
    475 	 */
    476 	public void reset( boolean resetTimeStamp )
    477 	{
    478 		for( Iterator iterator =  _packages.values().iterator(); iterator.hasNext();  )
    479 		{
    480 			XPackage xPackage = (XPackage) iterator.next();
    481
    482 			for( Iterator i = xPackage.getClasses().iterator(); i.hasNext();  )
    483 			{
    484 				AbstractClass clazz = ( AbstractClass ) i.next();
    485
    486 				clazz.reset();
    487 			}
    488 		}
    489 		_binaryClasses.clear();
    490 		_unknownClasses.clear();
    491 		_packages.clear();
    492 		_sourceSets.clear();
    493 		_sourceSetSourceClasses.clear();
    494 		_sourceSetClassNames.clear();
    495 		_allSourceClasses.clear();
    496 		_sourceSetSourceClassesWithInnerClasses.clear();
    497
    498 		_logMessages.clear();
    499 		_properties.clear();
    500 		_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 		if( resetTimeStamp )
    510 		{
    511 			_birthday = System.currentTimeMillis();
    512 		}
    513 	}
    514
    515 	/***
    516 	 * Prints the log messages encountered during parsing
    517 	 *
    518 	 * @param out
    519 	 * @param level
    520 	 */
    521 	public void printLogMessages( PrintStream out, int level )
    522 	{
    523 		boolean printedHeader = false;
    524
    525 		for( Iterator i = _logMessages.iterator(); i.hasNext();  )
    526 		{
    527 			LogMessage m = ( LogMessage ) i.next();
    528
    529 			if( m._level == level )
    530 			{
    531 				if( !printedHeader )
    532 				{
    533 					if( level == ONE_OR_MORE_IMPORTED_PACKAGES )
    534 					{
    535 						// Could be an inner class too!!!
    536 						out.println( "WARNING: Some classes refer to other classes that were not found among the sources or on the classpath." );
    537 						out.println( "         (Perhaps the referred class doesn't exist? Hasn't been generated yet?)" );
    538 						out.println( "         The referring classes do not import any fully qualified classes matching these classes." );
    539 						out.println( "         Since at least one package is imported, it is impossible for xjavadoc to figure out" );
    540 						out.println( "         what package the referred classes belong to. The classes are:" );
    541 					}
    542 					else
    543 					{
    544 						out.println( "INFO:    Some classes refer to other classes that were not found among the sources or on the classpath." );
    545 						out.println( "         (Perhaps the referred class doesn't exist? Hasn't been generated yet?)" );
    546 						out.println( "         The referring classes do not import any fully qualified classes matching these classes." );
    547 						out.println( "         However, since no packages are imported, xjavadoc has assumed that the referred classes" );
    548 						out.println( "         belong to the same package as the referring class. The classes are:" );
    549 					}
    550 					printedHeader = true;
    551 				}
    552 				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 	public void addSourceSet( SourceSet sourceSet )
    563 	{
    564 		_sourceSets.add( sourceSet );
    565 		for( int j = 0; j < sourceSet.getSize(); j++ )
    566 		{
    567 			String qualifiedName = sourceSet.getQualifiedName( j );
    568
    569 			if( _sourceSetClassNames.contains( qualifiedName ) )
    570 			{
    571 				String msg = "The class \"" + qualifiedName + "\" occurs in more than one fileset. That's illegal.";
    572
    573 				System.err.println( msg );
    574 			}
    575 			_sourceSetClassNames.add( qualifiedName );
    576 		}
    577 	}
    578
    579 	public void addAbstractFile( String qualifiedName, AbstractFile file )
    580 	{
    581 		_abstractFileClasses.put( qualifiedName, file );
    582
    583 		if( _sourceSetClassNames.contains( qualifiedName ) )
    584 		{
    585 			String msg = "The class \"" + qualifiedName + "\" occurs in more than one fileset. That's illegal.";
    586
    587 			System.err.println( msg );
    588 		}
    589 		_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 	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 		XClass clazz = getXClass( className );
    617 		XMethod method = clazz.getMethod( methodNameWithSignature );
    618
    619 		XDoc doc = method.getDoc();
    620
    621 		doc.updateTagValue( tagName, parameterName, parameterValue, tagIndex );
    622
    623 		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 	public XClass updateClassTag(
    638 		String className,
    639 		String tagName,
    640 		String parameterName,
    641 		String parameterValue,
    642 		int tagIndex
    643 		 ) throws XJavaDocException
    644 	{
    645 		XClass clazz = getXClass( className );
    646 		XDoc doc = clazz.getDoc();
    647
    648 		doc.updateTagValue( tagName, parameterName, parameterValue, tagIndex );
    649 		return clazz;
    650 	}
    651
    652 	public String dereferenceProperties( String value )
    653 	{
    654
    655 		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 	final boolean classExists( final String qualifiedClassName )
    664 	{
    665 		// See if we have the source
    666 		if( sourceExists( qualifiedClassName ) )
    667 		{
    668 			return true;
    669 		}
    670 		// See if we kand find the class (binary)
    671 		else if( getClass( qualifiedClassName ) != null )
    672 		{
    673 			return true;
    674 		}
    675 		else
    676 		{
    677 			return false;
    678 		}
    679 	}
    680
    681 	void logMessage( SourceClass clazz, UnknownClass unknownClass, String unqualifiedClassName, int level )
    682 	{
    683 		_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 	XPackage addPackageMaybe( String packageName )
    693 	{
    694 		XPackage result = ( XPackage ) _packages.get( packageName );
    695
    696 		if( result == null )
    697 		{
    698 			// The package doesn't exist yet. Add it then
    699 			resXPackage( packageName )/package-summary.html">ult = new XPackage( packageName );
    700 			_packages.put( packageName, result );
    701 		}
    702 		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 	void addSourceClass( SourceClass sourceClass )
    712 	{
    713 		_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 		if( _sourceSetClassNames.contains( sourceClass.getQualifiedName() ) || sourceClass.isExtraClass() )
    718 		{
    719 			_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 	private final Class getClass( String qualifiedName )
    730 	{
    731 		try
    732 		{
    733 			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 			return null;
    740 		}
    741 	}
    742
    743 	/***
    744 	 * Returns all classes in the registered source sets
    745 	 *
    746 	 * @return   A Collection of XClass
    747 	 */
    748 	private Collection getOuterSourceClasses()
    749 	{
    750 		if( _sourceSetSourceClasses.isEmpty() )
    751 		{
    752 			for( Iterator i = _sourceSetClassNames.iterator(); i.hasNext();  )
    753 			{
    754 				String qualifiedName = ( String ) i.next();
    755
    756 				/*
    757 				 * This will result in the class being added to
    758 				 * _sourceSetSourceClasses AND _allSourceClasses
    759 				 */
    760 				getXClass( qualifiedName );
    761 			}
    762
    763 			for( Iterator iterator = _abstractFileClasses.keySet().iterator(); iterator.hasNext();  )
    764 			{
    765 				String fqcn = ( String ) iterator.next();
    766
    767 				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 		Collection new_collection = new ArrayList( _sourceSetSourceClasses.values() );
    776
    777 		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 	private AbstractFile getSourceFile( String qualifiedName )
    787 	{
    788 		// loop over all SourceSets. If a source is found more than once -> bang!
    789 		AbstractFile found = null;
    790
    791 		for( Iterator i = _sourceSets.iterator(); i.hasNext();  )
    792 		{
    793 			SourceSet sourceSet = ( SourceSet ) i.next();
    794 			AbstractFile javaFile = sourceSet.getSourceFile( qualifiedName );
    795
    796 			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 				found = javaFile;
    805 			}
    806 		}
    807 		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 	private void addInnerClassRecursive( XClass outer, Collection c )
    817 	{
    818 		for( Iterator inners = outer.getInnerClasses().iterator(); inners.hasNext();  )
    819 		{
    820 			XClass inner = (XClass) inners.next();
    821
    822 			c.add( inner );
    823 			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 	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 		AbstractFile sourceFile = getSourceFile( qualifiedName );
    847
    848 		if( sourceFile != null )
    849 		{
    850 			if( sourceFile.lastModified() > _birthday )
    851 			{
    852 				// The source appeared after xjavadoc was reset. Pretend it doesn't exist.
    853 				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 				return false;
    855 			}
    856 		}
    857
    858 		boolean sourceFileExists = sourceFile != null;
    859
    860 		if( !sourceFileExists )
    861 		{
    862 			sourceFileExists = _abstractFileClasses.containsKey( qualifiedName );
    863 		}
    864
    865 		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 	private SourceClass scanAndPut( String qualifiedName )
    875 	{
    876 		AbstractFile sourceFile = getSourceFile( qualifiedName );
    877
    878 		sourceFile = sourceFile != null ? sourceFile : ( AbstractFile ) _abstractFileClasses.get( qualifiedName );
    879
    880 		if( sourceFile == null )
    881 		{
    882 			throw new IllegalStateException( "No source found for " + qualifiedName );
    883 		}
    884
    885 		SourceClass sourceClass = new SourceClass( this, sourceFile, _useNodeParser, _tagFactory ,_encoding);
    886
    887 		// now that the entire file is parsed, validate the tags.
    888 		if( _tagFactory.isValidating() )
    889 		{
    890 			sourceClass.validateTags();
    891 		}
    892
    893 //		addSourceClass( sourceClass );
    894
    895 		return sourceClass;
    896 	}
    897
    898     public XTagFactory getTagFactory() {
    899         return _tagFactory;
    900     }
    901
    902     /***
    903      * Registers tags.
    904      *
    905      * @param classpath where tags are found.
    906      */
    907     public void registerTags( String classpath ) {
    908         new TagIntrospector().registerTags( classpath, getTagFactory() );
    909     }
    910
    911 	public final static class NoInnerClassesPredicate implements Predicate
    912 	{
    913 		public boolean evaluate( Object o )
    914 		{
    915 			XClass clazz = ( XClass ) o;
    916
    917 			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 		LogMessage( SourceClass sourceClass, UnknownClass unknownClass, String unqualifiedClassName, int level )
    928 		{
    929 			_sourceClass = sourceClass;
    930 			_unknownClass = unknownClass;
    931 			_unqualifiedClassName = unqualifiedClassName;
    932 			_level = level;
    933 		}
    934 	}
    935
    936 	/***
    937 	 * Getter for source file charset.
    938 	 * @return encoding
    939 	 */
    940 	public String getEncoding() {
    941 		return _encoding;
    942 	}
    943
    944 	/***
    945 	 * Setter for source file charset.
    946 	 * @param string encoding
    947 	 */
    948 	public void setEncoding(String encoding) {
    949 		_encoding = encoding;
    950 	}
    951
    952 	/***
    953 	 * Getter for generated file charset.
    954 	 * @return encoding
    955 	 */
    956 	public String getDocEncoding() {
    957 		return _docEncoding;
    958 	}
    959
    960 	/***
    961 	 * Setter for generated file charset.
    962 	 * @param string encoding
    963 	 */
    964 	public void setDocEncoding(String docencoding) {
    965 		_docEncoding = docencoding;
    966 	}
    967
    968 }