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.filesystem.XJavadocFile;
9   import xjavadoc.filesystem.ReaderFile;
10  
11  import java.util.*;
12  import java.io.*;
13  
14  /***
15   * This class represents a class for which the source code is available
16   *XJavaDocFil
17   * @author    Aslak Hellesøy
18   * @created   3. januar 2002
19   */
20  public final class SourceClass extends AbstractClass
21  {
22  	public static int  instanceCount = 0;
23  
24  	private final Map  _qualifiedClasses = new HashMap();
25  
26  	private final boolean _isExtraClass;
27  
28  	private final List _tagsForValidation = new ArrayList();
29  
30  	/***
31  	 * The root node of the AST
32  	 */
33  	private SimpleNode _compilationUnit;
34  
35  	private Reader     _in = null;
36  
37  	/***
38  	 * Keep a ref to the file in case of warning reporting
39  	 */
40  	private AbstractFile _sourceFile;
41  
42  	//private JavaParser _parser;
43  
44  	/***
45  	 * doe we nees saving?
46  	 */
47  	private boolean    _dirty;
48  
49  	/***
50  	 * Constructor to use for inner classes.
51  	 *
52  	 * @param containingClass  The containing class;
53  	 */
54  	public SourceClass( SourceClass containingClass, XTagFactory tagFactory )
55  	{
56  		super( containingClass, tagFactory );
57  		setContainingPackage( containingClass.getContainingPackage().getName() );
58  		_isExtraClass = false;
59  	}
60  
61  	/***
62  	 * Constructor to use for "extra" classes, that is, secondary classes that
63  	 * figure in the same source.
64  	 *
65  	 * @param mainClass  The containing class. Or rather the "main" class in the
66  	 *      source.
67  	 * @param dummy
68  	 */
69  	public SourceClass( SourceClass mainClass, int dummy, XTagFactory tagFactory )
70  	{
71  		super( mainClass.getXJavaDoc(), tagFactory );
72  		setContainingPackage( mainClass.getContainingPackage().getName() );
73  		_isExtraClass = true;
74  		_sourceFile = mainClass.getFile();
75  	}
76  
77  	/***
78  	 * Constructor to use for outer classes
79  	 *
80  	 * @param sourceFile  The file containing the source
81  	 */
82  	public SourceClass( XJavaDoc xJavaDoc, File sourceFile, XTagFactory tagFactory )
83  	{
84  		this( xJavaDoc, new XJavadocFile( sourceFile ), false, tagFactory, null );
85  	}
86  
87  	/***
88  	 * Constructor to use for outer classes
89  	 *
90  	 * @param sourceFile  The file containing the source
91  	 */
92  	public SourceClass( XJavaDoc xJavaDoc, Reader sourceFile, XTagFactory tagFactory )
93  	{
94  		this( xJavaDoc, new ReaderFile( sourceFile ), false, tagFactory, null);
95  	}
96  
97  	/***
98  	 * @param sourceFile
99  	 * @param useNodeParser
100 	 */
101 	public SourceClass( XJavaDoc xJavaDoc, File sourceFile, boolean useNodeParser, XTagFactory tagFactory )
102 	{
103 		this( xJavaDoc, new XJavadocFile( sourceFile ), useNodeParser, tagFactory ,null );
104 	}
105 
106 	/***
107 	 * Constructor to use for outer classes
108 	 *
109 	 * @param sourceFile     The file containing the source
110 	 * @param useNodeParser
111 	 */
112 	public SourceClass( XJavaDoc xJavaDoc, AbstractFile sourceFile, boolean useNodeParser, XTagFactory tagFactory ,String encoding)
113 	{
114 		super( xJavaDoc, tagFactory );
115 		if( sourceFile == null )
116 		{
117 			throw new IllegalArgumentException( "sourceFile can't be null for outer classes!" );
118 		}
119 		_sourceFile = sourceFile;
120 
121 		try
122 		{
123 			_in = sourceFile.getReader(encoding);
124 			parse( useNodeParser );
125 		}
126 		catch( IOException e )
127 		{
128 			// That's tough. Shouldn't happen
129 			if(encoding==null)
130 			{
131 			throw new IllegalStateException( "Couldn't find " + sourceFile );
132 		}
133 			else
134 			{
135 				throw new IllegalStateException( "Invalid Encoding '"+encoding+"' or couldn't find '" + sourceFile +"'");
136 			}
137 		}
138 
139 		instanceCount++;
140 		_dirty = false;
141 		_isExtraClass = false;
142 	}
143 
144 	/***
145 	 * Describe what the method does
146 	 *
147 	 * @param qualifiedName  Describe what the parameter does
148 	 * @return               Describe the return value
149 	 */
150 	public static String getFileName( String qualifiedName )
151 	{
152 		return qualifiedName.replace( '.', File.separatorChar ) + ".java";
153 	}
154 
155 	public boolean isExtraClass()
156 	{
157 		return _isExtraClass;
158 	}
159 
160 	/***
161 	 * Returns "1", "2", etc., depending on how many inner classes we have.
162 	 *
163 	 * @return String containing number of next anonymous inner class
164 	 */
165 	public String getNextAnonymousClassName()
166 	{
167 		return String.valueOf( getInnerClasses().size() + 1 );
168 	}
169 
170 	/***
171 	 * Gets the OuterClass attribute of the SourceClass object
172 	 *
173 	 * @return   The OuterClass value
174 	 */
175 	private boolean isOuterClass()
176 	{
177 		return _sourceFile != null;
178 	}
179 
180 	/***
181 	 * Gets the Writeable attribute of the SourceClass object
182 	 *
183 	 * @return   The Writeable value
184 	 */
185 	public boolean isWriteable()
186 	{
187 		return _compilationUnit != null;
188 	}
189 
190 	public SimpleNode getCompilationUnit()
191 	{
192 		return _compilationUnit;
193 	}
194 
195 	/***
196 	 * Returns a reader for the source code.
197 	 *
198 	 * @return   a reader for the source code.
199 	 */
200 	public Reader getReader()
201 	{
202 		return _in;
203 	}
204 
205 	public AbstractFile getFile()
206 	{
207 		return _sourceFile;
208 	}
209 
210 	public boolean isPrimitive()
211 	{
212 		return false;
213 	}
214 
215 	/***
216 	 * say this class is dirty and needs saving propagate to outer class ( if any )
217 	 */
218 	public void setDirty()
219 	{
220 		if( isInner() )
221 		{
222 			getContainingClass().setDirty();
223 		}
224 		else
225 		{
226 			_dirty = true;
227 		}
228 	}
229 	/***
230 	 * Called by JavaParser at the end of the parsing
231 	 *
232 	 * @param compilationUnit  The new CompilationUnit value
233 	 */
234 	public void setCompilationUnit( SimpleNode compilationUnit )
235 	{
236 		_compilationUnit = compilationUnit;
237 	}
238 
239 	/***
240 	 * Called by XJavaDoc after the entire source is parsed, but only if validation
241 	 * is on.
242 	 *
243 	 * @throws TagValidationException
244 	 */
245 	public void validateTags() throws TagValidationException
246 	{
247 		// Validate the tags on the class level and on our members.
248 		for( Iterator i = _tagsForValidation.iterator(); i.hasNext();  )
249 		{
250 			XTag tag = ( XTag ) i.next();
251 
252 			tag.validate();
253 		}
254 
255 		// then validate tags in all our inner classes.
256 		for( Iterator i = getInnerClasses().iterator(); i.hasNext();  )
257 		{
258 			SourceClass inner = ( SourceClass ) i.next();
259 
260 			inner.validateTags();
261 		}
262 	}
263 
264 	public void addTagForValidation( DefaultXTag tag )
265 	{
266 		_tagsForValidation.add( tag );
267 	}
268 
269 	public boolean saveNeeded()
270 	{
271 		return isWriteable() && _dirty;
272 	}
273 
274 	/***
275 	 * Describe what the method does
276 	 *
277 	 * @return   Describe the return value
278 	 */
279 	public long lastModified()
280 	{
281 		if( isOuterClass() )
282 		{
283 			return _sourceFile.lastModified();
284 		}
285 		else
286 		{
287 			return getContainingClass().lastModified();
288 		}
289 	}
290 
291 	/***
292 	 * @param out
293 	 * @deprecated   use the Writer method
294 	 */
295 	public void print( OutputStream out )
296 	{
297 		print( new OutputStreamWriter( out ) );
298 	}
299 
300 	/***
301 	 * Prints this class to a stream
302 	 *
303 	 * @param out  Describe what the parameter does
304 	 */
305 	public void print( Writer out )
306 	{
307 		updateDoc();
308 		if( !isWriteable() )
309 		{
310 			// parsed with simple parser
311 			throw new UnsupportedOperationException( "Can't save classes that are parsed with simpleparser" );
312 		}
313 		NodePrinter.print( _compilationUnit, out );
314 	}
315 
316 	/***
317 	 * Saves the class at root dir rootDir. The actual java file is derived from
318 	 * tha package name. If no root dir is specified, save where it was loaded from
319 	 *
320 	 * @param rootDir       the root directory.
321 	 * @return              the relative fileName to which the file was saved.
322 	 * @throws IOException  if the file couldn't be saved
323 	 */
324 	public String save( File rootDir ) throws IOException
325 	{
326 		if( !isWriteable() )
327 		{
328 			throw new UnsupportedOperationException( "Can't save classes that aren't parsed in AST mode (do getXJavaDoc().setUseNodeParser(true) before parsing starts!)" );
329 		}
330 		if( getContainingClass() != null )
331 		{
332 			// inner class. can't save these.
333 			throw new UnsupportedOperationException( "Can't save inner classes" );
334 		}
335 		else if( rootDir != null )
336 		{
337 			String fileName = getFileName( getQualifiedName() );
338 			File javaFile = new File( rootDir, fileName );
339 
340 			javaFile.getParentFile().mkdirs();
341 			print( new FileWriter( javaFile ) );
342 			return fileName;
343 		}
344 		else
345 		{
346 			// no root dir specified, save in place
347 			Writer outputStream = _sourceFile.getWriter();
348 
349 			print( new PrintWriter( outputStream ) );
350 			outputStream.flush();
351 			outputStream.close();
352 			return _sourceFile.toString();
353 		}
354 	}
355 
356 	/***
357 	 * Returns fully qualified name of a class. 1: check for "." 2: if "." it's
358 	 * already qualified 3: if no ".", must try with all imported packages or
359 	 * classes
360 	 *
361 	 * @param unqualifiedClassName  Describe what the parameter does
362 	 * @return                      Describe the return value
363 	 */
364 	public XClass qualify( final String unqualifiedClassName )
365 	{
366 		XClass result = null;
367 
368 		result = ( XClass ) _qualifiedClasses.get( unqualifiedClassName );
369 		if( result == null )
370 		{
371 
372 			if( getContainingClass() == null )
373 			{
374 				// If there are dots, consider it to be qualified or a reference to an inner class in one
375 				// of the imported classes.
376 				if( unqualifiedClassName.indexOf( '.' ) != -1 )
377 				{
378 					result = unqualifiedNameInImportedClassesInnerClasses( unqualifiedClassName );
379 					if( result == null )
380 					{
381 						// It wasn't a ref to an imported inner class. Consider it already qualified
382 						result = getXJavaDoc().getXClass( unqualifiedClassName );
383 					}
384 				}
385 				else
386 				{
387 					// There are no dots in the class name. It's a primitive or unqualified.
388 					Primitive primitive;
389 
390 					if( ( primitive = XJavaDoc.getPrimitive( unqualifiedClassName ) ) != null )
391 					{
392 						result = primitive;
393 					}
394 					else
395 					{
396 						String qualifiedName;
397 
398 						if( ( qualifiedName = unqualifiedNameInTheSameClassAsAnInnerClass( unqualifiedClassName ) ) != null )
399 						{
400 							result = getXJavaDoc().getXClass( qualifiedName );
401 						}
402 						else if( ( qualifiedName = unqualifiedNameInInnerClasses( unqualifiedClassName ) ) != null )
403 						{
404 							result = getXJavaDoc().getXClass( qualifiedName );
405 						}
406 						else if( ( qualifiedName = unqualifiedNameInJavaDotLang( unqualifiedClassName ) ) != null )
407 						{
408 							result = getXJavaDoc().getXClass( qualifiedName );
409 						}
410 						else if( ( qualifiedName = unqualifiedNameInImportedClasses( unqualifiedClassName ) ) != null )
411 						{
412 							result = getXJavaDoc().getXClass( qualifiedName );
413 						}
414 						else if( ( qualifiedName = unqualifiedNameInImportedPackages( unqualifiedClassName ) ) != null )
415 						{
416 							result = getXJavaDoc().getXClass( qualifiedName );
417 						}
418 						else if( ( qualifiedName = unqualifiedNameInTheSamePackage( unqualifiedClassName ) ) != null )
419 						{
420 							result = getXJavaDoc().getXClass( qualifiedName );
421 						}
422                         else if( ( qualifiedName = unqualifiedNameInInnerClassesOfSuperClass( unqualifiedClassName ) ) != null )
423                         {
424                             result = getXJavaDoc().getXClass( qualifiedName );
425                         }
426                         else if( ( qualifiedName = unqualifiedNameInInnerInterface( unqualifiedClassName ) ) != null )
427                         {
428                             result = getXJavaDoc().getXClass( qualifiedName );
429                         }
430 						else
431 						{
432 							String unknownClassName;
433 
434 							if( getContainingPackage().getName().equals( "" ) )
435 							{
436 								unknownClassName = unqualifiedClassName;
437 							}
438 							else
439 							{
440 								unknownClassName = getContainingPackage().getName() + "." + unqualifiedClassName;
441 							}
442 
443 							UnknownClass unknownClass = new UnknownClass( getXJavaDoc(), unknownClassName );
444 
445 							/*
446 							 * We couldn't qualify the class. If there are no package import statements,
447 							 * we'll assume the class belongs to the same package as ourself.
448 							 */
449 							if( !hasImportedPackages() )
450 							{
451 								// No import foo.bar.* statements. Just add an informative message that we guessed
452 								getXJavaDoc().logMessage( this, unknownClass, unqualifiedClassName, XJavaDoc.NO_IMPORTED_PACKAGES );
453 							}
454 							else
455 							{
456 
457 								// We can't decide. Add a warning that will be displayed in the end.
458 								getXJavaDoc().logMessage( this, unknownClass, unqualifiedClassName, XJavaDoc.ONE_OR_MORE_IMPORTED_PACKAGES );
459 							}
460 							result = unknownClass;
461 						}
462 					}
463 				}
464 			}
465 			else
466 			{
467 				result = getContainingAbstractClass().qualify( unqualifiedClassName );
468 			}
469 			_qualifiedClasses.put( unqualifiedClassName, result );
470 		}
471 
472 		return result;
473 	}
474 
475 	public void reset()
476 	{
477 		super.reset();
478 
479 		_compilationUnit = null;
480 		_in = null;
481 		_sourceFile = null;
482 		_qualifiedClasses.clear();
483 	}
484 
485 	private final String unqualifiedNameInImportedClasses( final String unqualifiedClassName )
486 	{
487 		if( !hasImportedClasses() )
488 		{
489 			return null;
490 		}
491 
492 		final String suffix = "." + unqualifiedClassName;
493 		String candidate = null;
494 
495 		for( Iterator i = getImportedClasses().iterator(); i.hasNext();  )
496 		{
497 			XClass clazz = ( XClass ) i.next();
498 			String qualifiedClassName = clazz.getQualifiedName();
499 
500 			if( qualifiedClassName.endsWith( suffix ) )
501 			{
502 				// perform sanity check for ambiguous imports
503 				if( candidate != null && !candidate.equals( qualifiedClassName ) )
504 				{
505 					// ambiguous class import
506 					throw new IllegalStateException( "In class " + getQualifiedName() + ": Ambiguous class:" + unqualifiedClassName + ". Is it " + candidate + " or " + qualifiedClassName + "?" );
507 				}
508 				else
509 				{
510 					candidate = qualifiedClassName;
511 				}
512 			}
513 		}
514 		return candidate;
515 	}
516 
517 	private final XClass unqualifiedNameInImportedClassesInnerClasses( final String unqualifiedClassName )
518 	{
519 		if( !hasImportedClasses() )
520 		{
521 			return null;
522 		}
523 
524 		XClass candidate = null;
525 
526 		for( Iterator i = getImportedClasses().iterator(); i.hasNext();  )
527 		{
528 			XClass clazz = ( XClass ) i.next();
529 
530 			// See if it's among the inner classes.
531 			for( Iterator inners = clazz.getInnerClasses().iterator(); inners.hasNext();  )
532 			{
533 				XClass inner = ( XClass ) inners.next();
534 				boolean isAccessible = inner.isPublic();
535 
536 				if( inner.getName().equals( unqualifiedClassName ) && isAccessible )
537 				{
538 					if( candidate != null )
539 					{
540 						// ambiguous class import
541 						throw new IllegalStateException( "In class " + getQualifiedName() + ": Ambiguous class:" + unqualifiedClassName + ". Is it " + candidate.getQualifiedName() + " or " + inner.getQualifiedName() + "?" );
542 					}
543 					else
544 					{
545 						candidate = inner;
546 					}
547 				}
548 			}
549 		}
550 		return candidate;
551 	}
552 
553 	/***
554 	 * Describe what the method does
555 	 *
556 	 * @param unqualifiedClassName  Describe what the parameter does
557 	 * @return                      Describe the return value
558 	 */
559 	private final String unqualifiedNameInInnerClasses( final String unqualifiedClassName )
560 	{
561 		if( !hasInnerClasses() )
562 		{
563 			return null;
564 		}
565 
566 		final String innerClassName = getQualifiedName() + '.' + unqualifiedClassName;
567 
568 		String candidate = null;
569 
570 		for( Iterator i = getInnerClasses().iterator(); i.hasNext();  )
571 		{
572 			XClass innerClass = ( XClass ) i.next();
573 			String qualifiedClassName = innerClass.getQualifiedName();
574 
575 			if( innerClassName.equals( qualifiedClassName ) )
576 			{
577 				candidate = qualifiedClassName;
578 				break;
579 			}
580 		}
581 		return candidate;
582 	}
583 
584 	/***
585 	 * Resolves Inner interfaces that exist in current class.
586     *
587     * This catches inner classes as well because isInterface()
588     * does not indicate if it's an interface.
589 	 *
590 	 * @param unqualifiedClassName  Name of the class to resolve
591 	 * @return                      The qualified name of the inner class.
592 	 */
593 	private final String unqualifiedNameInInnerInterface( final String unqualifiedClassName )
594 	{
595         String qualifiedClassName = getQualifiedName() + '$' + unqualifiedClassName;
596         if (getXJavaDoc().classExists(qualifiedClassName)) {
597 //            // The isInterface() method is not implemented for source classes.
598 //            if (XJavaDoc.getInstance().getXClass(qualifiedClassName).isInterface()) {
599 //                return getQualifiedName() + '.' + unqualifiedClassName;
600 //            }
601             return getQualifiedName() + '.' + unqualifiedClassName;
602         }
603 		return null;
604 	}
605 
606 	/***
607 	 * Resolves Inner classes that exist in the super class hierarchy.
608 	 *
609 	 * @param unqualifiedClassName  Name of the class to resolve
610 	 * @return                      The qualified name of the inner class.
611 	 */
612 	private final String unqualifiedNameInInnerClassesOfSuperClass( final String unqualifiedClassName )
613 	{
614         XClass clazz = getXJavaDoc().getXClass(getQualifiedName());
615         XClass superClazz = clazz.getSuperclass();
616         while (superClazz != null && ! superClazz.getQualifiedName().equals("java.lang.Object")) {
617 		    String innerClassName = superClazz.getQualifiedName() + '.' + unqualifiedClassName;
618             for( Iterator i = superClazz.getInnerClasses().iterator(); i.hasNext();  )
619             {
620                 XClass innerClass = ( XClass ) i.next();
621                 String qualifiedClassName = innerClass.getQualifiedName();
622                 if( innerClassName.equals( qualifiedClassName ) )
623                 {
624                     return qualifiedClassName;
625                 }
626             }
627             superClazz = superClazz.getSuperclass();
628         }
629 		return null;
630 	}
631 
632 	/***
633 	 * Describe what the method does
634 	 *
635 	 * @param unqualifiedClassName  Describe what the parameter does
636 	 * @return                      Describe the return value
637 	 */
638 	private final String unqualifiedNameInImportedPackages( final String unqualifiedClassName )
639 	{
640 		if( !hasImportedPackages() )
641 		{
642 			return null;
643 		}
644 
645 		final String suffix = "." + unqualifiedClassName;
646 		String candidate = null;
647 
648 		for( Iterator i = getImportedPackages().iterator(); i.hasNext();  )
649 		{
650 			String importedPackageName = ( ( XPackage ) i.next() ).getName();
651 			String qualifiedClassName = importedPackageName + suffix;
652 
653 			if( getXJavaDoc().classExists( qualifiedClassName ) )
654 			{
655 				if( candidate != null && !candidate.equals( qualifiedClassName ) )
656 				{
657 					// ambiguous class import
658 					throw new IllegalStateException( "In class " + getQualifiedName() + ": Ambiguous class:" + unqualifiedClassName + ". Is it " + candidate + " or " + qualifiedClassName + "?" );
659 				}
660 				else
661 				{
662 					candidate = qualifiedClassName;
663 				}
664 			}
665 		}
666 		return candidate;
667 	}
668 
669 	/***
670 	 * Returns the fully qualified class name if it's found in java.lang, otherwise
671 	 * null.
672 	 *
673 	 * @param unqualifiedClassName
674 	 * @return fully qualified class name, or null
675 	 */
676 	private final String unqualifiedNameInJavaDotLang( final String unqualifiedClassName )
677 	{
678 		String qualifiedClassName = "java.lang." + unqualifiedClassName;
679 
680 		if( getXJavaDoc().classExists( qualifiedClassName ) )
681 		{
682 			return qualifiedClassName;
683 		}
684 		else
685 		{
686 			return null;
687 		}
688 	}
689 
690 	/***
691 	 * Describe what the method does
692 	 *
693 	 * @param unqualifiedClassName  Describe what the parameter does
694 	 * @return                      Describe the return value
695 	 */
696 	private final String unqualifiedNameInTheSamePackage( final String unqualifiedClassName )
697 	{
698 		String qualifiedClassName;
699 
700 		if( getContainingPackage().getName().equals( "" ) )
701 		{
702 			qualifiedClassName = unqualifiedClassName;
703 		}
704 		else
705 		{
706 			qualifiedClassName = getContainingPackage().getName() + '.' + unqualifiedClassName;
707 		}
708 
709 		if( getXJavaDoc().classExists( qualifiedClassName ) )
710 		{
711 			return qualifiedClassName;
712 		}
713 		else
714 		{
715 			return null;
716 		}
717 
718 	}
719 
720 	private final String unqualifiedNameInTheSameClassAsAnInnerClass( final String unqualifiedClassName )
721 	{
722 		//containing class=com.p.A, inner-reference=B ->com.p.A.B
723 		String qualifiedClassName = getQualifiedName() + '.' + unqualifiedClassName;
724 
725 		if( getXJavaDoc().classExists( qualifiedClassName ) )
726 			return qualifiedClassName;
727 
728 		//containing class=com.p.A, inner-reference=A.B ->com.p.A.B
729 		if( getContainingPackage().getName().equals( "" ) )
730 		{
731 			qualifiedClassName = unqualifiedClassName;
732 		}
733 		else
734 		{
735 			qualifiedClassName = getContainingPackage().getName() + '.' + unqualifiedClassName;
736 		}
737 
738 		if( getXJavaDoc().classExists( qualifiedClassName ) )
739 			return qualifiedClassName;
740 
741 		return null;
742 	}
743 
744 	/***
745 	 * Describe what the method does
746 	 *
747 	 * @param useNodeParser  Describe what the parameter does
748 	 */
749 	private void parse( boolean useNodeParser )
750 	{
751 		try
752 		{
753 			if( useNodeParser )
754 			{
755 				// We need a pool of parsers, because parsing one file
756 				// might kick away the parsing of another etc.
757 //				_nodeParser.populate( this );
758 				new NodeParser( getXJavaDoc(), getTagFactory() ).populate( this );
759 			}
760 			else
761 			{
762 //				_simpleParser.populate( this );
763 				new SimpleParser( getXJavaDoc(), getTagFactory() ).populate( this );
764 			}
765 		}
766 		catch( ParseException e )
767 		{
768 			// Source code is bad. Not according to grammar. User's fault.
769 			String cls = _sourceFile != null ? _sourceFile.toString() : getQualifiedName();
770 
771 			System.err.println( "Error parsing " + cls + ':' + e.getMessage() );
772 		}
773 		catch( TokenMgrError e )
774 		{
775 			String cls = _sourceFile != null ? _sourceFile.toString() : getQualifiedName();
776 
777 			System.err.println( "Error parsing " + cls + ':' + e.getMessage() );
778 		}
779 	}
780 }