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 	 * Prints this class to a stream
293 	 *
294 	 * @param out  Describe what the parameter does
295 	 */
296 	public void print( Writer out )
297 	{
298 		updateDoc();
299 		if( !isWriteable() )
300 		{
301 			// parsed with simple parser
302 			throw new UnsupportedOperationException( "Can't save classes that are parsed with simpleparser" );
303 		}
304 		NodePrinter.print( _compilationUnit, out );
305 	}
306 
307 	/***
308 	 * Saves the class at root dir rootDir. The actual java file is derived from
309 	 * tha package name. If no root dir is specified, save where it was loaded from
310 	 *
311 	 * @param rootDir       the root directory.
312 	 * @return              the relative fileName to which the file was saved.
313 	 * @throws IOException  if the file couldn't be saved
314 	 */
315 	public String save( File rootDir ) throws IOException
316 	{
317 		if( !isWriteable() )
318 		{
319 			throw new UnsupportedOperationException( "Can't save classes that aren't parsed in AST mode (do getXJavaDoc().setUseNodeParser(true) before parsing starts!)" );
320 		}
321 		if( getContainingClass() != null )
322 		{
323 			// inner class. can't save these.
324 			throw new UnsupportedOperationException( "Can't save inner classes" );
325 		}
326 		else if( rootDir != null )
327 		{
328 			String fileName = getFileName( getQualifiedName() );
329 			File javaFile = new File( rootDir, fileName );
330 
331 			javaFile.getParentFile().mkdirs();
332 			FileWriter fwtr = new FileWriter( javaFile );
333 			print( fwtr );
334 			fwtr.flush();
335 			fwtr.close(); 
336 			return fileName;
337 		}
338 		else
339 		{
340 			// no root dir specified, save in place
341 			Writer outputStream = _sourceFile.getWriter();
342 
343 			print( new PrintWriter( outputStream ) );
344 			outputStream.flush();
345 			outputStream.close();
346 			return _sourceFile.toString();
347 		}
348 	}
349 
350 	/***
351 	 * Returns fully qualified name of a class. 1: check for "." 2: if "." it's
352 	 * already qualified 3: if no ".", must try with all imported packages or
353 	 * classes
354 	 *
355 	 * @param unqualifiedClassName  Describe what the parameter does
356 	 * @return                      Describe the return value
357 	 */
358 	public XClass qualify( final String unqualifiedClassName )
359 	{
360 		XClass result = null;
361 
362 		result = ( XClass ) _qualifiedClasses.get( unqualifiedClassName );
363 		if( result == null )
364 		{
365 
366 			if( getContainingClass() == null )
367 			{
368 				// If there are dots, consider it to be qualified or a reference to an inner class in one
369 				// of the imported classes.
370 				if( unqualifiedClassName.indexOf( '.' ) != -1 )
371 				{
372 					result = unqualifiedNameInImportedClassesInnerClasses( unqualifiedClassName );
373 					if( result == null )
374 					{
375 						// It wasn't a ref to an imported inner class. Consider it already qualified
376 						result = getXJavaDoc().getXClass( unqualifiedClassName );
377 					}
378 				}
379 				else
380 				{
381 					// There are no dots in the class name. It's a primitive or unqualified.
382 					Primitive primitive;
383 
384 					if( ( primitive = XJavaDoc.getPrimitive( unqualifiedClassName ) ) != null )
385 					{
386 						result = primitive;
387 					}
388 					else
389 					{
390 						String qualifiedName;
391 
392 						if( ( qualifiedName = unqualifiedNameInTheSameClassAsAnInnerClass( unqualifiedClassName ) ) != null )
393 						{
394 							result = getXJavaDoc().getXClass( qualifiedName );
395 						}
396 						else if( ( qualifiedName = unqualifiedNameInInnerClasses( unqualifiedClassName ) ) != null )
397 						{
398 							result = getXJavaDoc().getXClass( qualifiedName );
399 						}
400 						else if( ( qualifiedName = unqualifiedNameInJavaDotLang( unqualifiedClassName ) ) != null )
401 						{
402 							result = getXJavaDoc().getXClass( qualifiedName );
403 						}
404 						else if( ( qualifiedName = unqualifiedNameInImportedClasses( unqualifiedClassName ) ) != null )
405 						{
406 							result = getXJavaDoc().getXClass( qualifiedName );
407 						}
408 						else if( ( qualifiedName = unqualifiedNameInImportedPackages( unqualifiedClassName ) ) != null )
409 						{
410 							result = getXJavaDoc().getXClass( qualifiedName );
411 						}
412 						else if( ( qualifiedName = unqualifiedNameInTheSamePackage( unqualifiedClassName ) ) != null )
413 						{
414 							result = getXJavaDoc().getXClass( qualifiedName );
415 						}
416                         else if( ( qualifiedName = unqualifiedNameInInnerClassesOfSuperClass( unqualifiedClassName ) ) != null )
417                         {
418                             result = getXJavaDoc().getXClass( qualifiedName );
419                         }
420                         else if( ( qualifiedName = unqualifiedNameInInnerInterface( unqualifiedClassName ) ) != null )
421                         {
422                             result = getXJavaDoc().getXClass( qualifiedName );
423                         }
424 						else
425 						{
426 							String unknownClassName;
427 
428 							if( getContainingPackage().getName().equals( "" ) )
429 							{
430 								unknownClassName = unqualifiedClassName;
431 							}
432 							else
433 							{
434 								unknownClassName = getContainingPackage().getName() + "." + unqualifiedClassName;
435 							}
436 
437 							UnknownClass unknownClass = new UnknownClass( getXJavaDoc(), unknownClassName );
438 
439 							/*
440 							 * We couldn't qualify the class. If there are no package import statements,
441 							 * we'll assume the class belongs to the same package as ourself.
442 							 */
443 							if( !hasImportedPackages() )
444 							{
445 								// No import foo.bar.* statements. Just add an informative message that we guessed
446 								getXJavaDoc().logMessage( this, unknownClass, unqualifiedClassName, XJavaDoc.NO_IMPORTED_PACKAGES );
447 							}
448 							else
449 							{
450 
451 								// We can't decide. Add a warning that will be displayed in the end.
452 								getXJavaDoc().logMessage( this, unknownClass, unqualifiedClassName, XJavaDoc.ONE_OR_MORE_IMPORTED_PACKAGES );
453 							}
454 							result = unknownClass;
455 						}
456 					}
457 				}
458 			}
459 			else
460 			{
461 				result = getContainingAbstractClass().qualify( unqualifiedClassName );
462 			}
463 			_qualifiedClasses.put( unqualifiedClassName, result );
464 		}
465 
466 		return result;
467 	}
468 
469 	public void reset()
470 	{
471 		super.reset();
472 
473 		_compilationUnit = null;
474 		_in = null;
475 		_sourceFile = null;
476 		_qualifiedClasses.clear();
477 	}
478 
479 	private final String unqualifiedNameInImportedClasses( final String unqualifiedClassName )
480 	{
481 		if( !hasImportedClasses() )
482 		{
483 			return null;
484 		}
485 
486 		final String suffix = "." + unqualifiedClassName;
487 		String candidate = null;
488 
489 		for( Iterator i = getImportedClasses().iterator(); i.hasNext();  )
490 		{
491 			XClass clazz = ( XClass ) i.next();
492 			String qualifiedClassName = clazz.getQualifiedName();
493 
494 			if( qualifiedClassName.endsWith( suffix ) )
495 			{
496 				// perform sanity check for ambiguous imports
497 				if( candidate != null && !candidate.equals( qualifiedClassName ) )
498 				{
499 					// ambiguous class import
500 					throw new IllegalStateException( "In class " + getQualifiedName() + ": Ambiguous class:" + unqualifiedClassName + ". Is it " + candidate + " or " + qualifiedClassName + "?" );
501 				}
502 				else
503 				{
504 					candidate = qualifiedClassName;
505 				}
506 			}
507 		}
508 		return candidate;
509 	}
510 
511 	private final XClass unqualifiedNameInImportedClassesInnerClasses( final String unqualifiedClassName )
512 	{
513 		if( !hasImportedClasses() )
514 		{
515 			return null;
516 		}
517 
518 		XClass candidate = null;
519 
520 		for( Iterator i = getImportedClasses().iterator(); i.hasNext();  )
521 		{
522 			XClass clazz = ( XClass ) i.next();
523 
524 			// See if it's among the inner classes.
525 			for( Iterator inners = clazz.getInnerClasses().iterator(); inners.hasNext();  )
526 			{
527 				XClass inner = ( XClass ) inners.next();
528 				boolean isAccessible = inner.isPublic();
529 
530 				if( inner.getName().equals( unqualifiedClassName ) && isAccessible )
531 				{
532 					if( candidate != null )
533 					{
534 						// ambiguous class import
535 						throw new IllegalStateException( "In class " + getQualifiedName() + ": Ambiguous class:" + unqualifiedClassName + ". Is it " + candidate.getQualifiedName() + " or " + inner.getQualifiedName() + "?" );
536 					}
537 					else
538 					{
539 						candidate = inner;
540 					}
541 				}
542 			}
543 		}
544 		return candidate;
545 	}
546 
547 	/***
548 	 * Describe what the method does
549 	 *
550 	 * @param unqualifiedClassName  Describe what the parameter does
551 	 * @return                      Describe the return value
552 	 */
553 	private final String unqualifiedNameInInnerClasses( final String unqualifiedClassName )
554 	{
555 		if( !hasInnerClasses() )
556 		{
557 			return null;
558 		}
559 
560 		final String innerClassName = getQualifiedName() + '.' + unqualifiedClassName;
561 
562 		String candidate = null;
563 
564 		for( Iterator i = getInnerClasses().iterator(); i.hasNext();  )
565 		{
566 			XClass innerClass = ( XClass ) i.next();
567 			String qualifiedClassName = innerClass.getQualifiedName();
568 
569 			if( innerClassName.equals( qualifiedClassName ) )
570 			{
571 				candidate = qualifiedClassName;
572 				break;
573 			}
574 		}
575 		return candidate;
576 	}
577 
578 	/***
579 	 * Resolves Inner interfaces that exist in current class.
580     *
581     * This catches inner classes as well because isInterface()
582     * does not indicate if it's an interface.
583 	 *
584 	 * @param unqualifiedClassName  Name of the class to resolve
585 	 * @return                      The qualified name of the inner class.
586 	 */
587 	private final String unqualifiedNameInInnerInterface( final String unqualifiedClassName )
588 	{
589         String qualifiedClassName = getQualifiedName() + '$' + unqualifiedClassName;
590         if (getXJavaDoc().classExists(qualifiedClassName)) {
591 //            // The isInterface() method is not implemented for source classes.
592 //            if (XJavaDoc.getInstance().getXClass(qualifiedClassName).isInterface()) {
593 //                return getQualifiedName() + '.' + unqualifiedClassName;
594 //            }
595             return getQualifiedName() + '.' + unqualifiedClassName;
596         }
597 		return null;
598 	}
599 
600 	/***
601 	 * Resolves Inner classes that exist in the super class hierarchy.
602 	 *
603 	 * @param unqualifiedClassName  Name of the class to resolve
604 	 * @return                      The qualified name of the inner class.
605 	 */
606 	private final String unqualifiedNameInInnerClassesOfSuperClass( final String unqualifiedClassName )
607 	{
608         XClass clazz = getXJavaDoc().getXClass(getQualifiedName());
609         XClass superClazz = clazz.getSuperclass();
610         while (superClazz != null && ! superClazz.getQualifiedName().equals("java.lang.Object")) {
611 		    String innerClassName = superClazz.getQualifiedName() + '.' + unqualifiedClassName;
612             for( Iterator i = superClazz.getInnerClasses().iterator(); i.hasNext();  )
613             {
614                 XClass innerClass = ( XClass ) i.next();
615                 String qualifiedClassName = innerClass.getQualifiedName();
616                 if( innerClassName.equals( qualifiedClassName ) )
617                 {
618                     return qualifiedClassName;
619                 }
620             }
621             superClazz = superClazz.getSuperclass();
622         }
623 		return null;
624 	}
625 
626 	/***
627 	 * Describe what the method does
628 	 *
629 	 * @param unqualifiedClassName  Describe what the parameter does
630 	 * @return                      Describe the return value
631 	 */
632 	private final String unqualifiedNameInImportedPackages( final String unqualifiedClassName )
633 	{
634 		if( !hasImportedPackages() )
635 		{
636 			return null;
637 		}
638 
639 		final String suffix = "." + unqualifiedClassName;
640 		String candidate = null;
641 
642 		for( Iterator i = getImportedPackages().iterator(); i.hasNext();  )
643 		{
644 			String importedPackageName = ( ( XPackage ) i.next() ).getName();
645 			String qualifiedClassName = importedPackageName + suffix;
646 
647 			if( getXJavaDoc().classExists( qualifiedClassName ) )
648 			{
649 				if( candidate != null && !candidate.equals( qualifiedClassName ) )
650 				{
651 					// ambiguous class import
652 					throw new IllegalStateException( "In class " + getQualifiedName() + ": Ambiguous class:" + unqualifiedClassName + ". Is it " + candidate + " or " + qualifiedClassName + "?" );
653 				}
654 				else
655 				{
656 					candidate = qualifiedClassName;
657 				}
658 			}
659 		}
660 		return candidate;
661 	}
662 
663 	/***
664 	 * Returns the fully qualified class name if it's found in java.lang, otherwise
665 	 * null.
666 	 *
667 	 * @param unqualifiedClassName
668 	 * @return fully qualified class name, or null
669 	 */
670 	private final String unqualifiedNameInJavaDotLang( final String unqualifiedClassName )
671 	{
672 		String qualifiedClassName = "java.lang." + unqualifiedClassName;
673 
674 		if( getXJavaDoc().classExists( qualifiedClassName ) )
675 		{
676 			return qualifiedClassName;
677 		}
678 		else
679 		{
680 			return null;
681 		}
682 	}
683 
684 	/***
685 	 * Describe what the method does
686 	 *
687 	 * @param unqualifiedClassName  Describe what the parameter does
688 	 * @return                      Describe the return value
689 	 */
690 	private final String unqualifiedNameInTheSamePackage( final String unqualifiedClassName )
691 	{
692 		String qualifiedClassName;
693 
694 		if( getContainingPackage().getName().equals( "" ) )
695 		{
696 			qualifiedClassName = unqualifiedClassName;
697 		}
698 		else
699 		{
700 			qualifiedClassName = getContainingPackage().getName() + '.' + unqualifiedClassName;
701 		}
702 
703 		if( getXJavaDoc().classExists( qualifiedClassName ) )
704 		{
705 			return qualifiedClassName;
706 		}
707 		else
708 		{
709 			return null;
710 		}
711 
712 	}
713 
714 	private final String unqualifiedNameInTheSameClassAsAnInnerClass( final String unqualifiedClassName )
715 	{
716 		//containing class=com.p.A, inner-reference=B ->com.p.A.B
717 		String qualifiedClassName = getQualifiedName() + '.' + unqualifiedClassName;
718 
719 		if( getXJavaDoc().classExists( qualifiedClassName ) )
720 			return qualifiedClassName;
721 
722 		//containing class=com.p.A, inner-reference=A.B ->com.p.A.B
723 		if( getContainingPackage().getName().equals( "" ) )
724 		{
725 			qualifiedClassName = unqualifiedClassName;
726 		}
727 		else
728 		{
729 			qualifiedClassName = getContainingPackage().getName() + '.' + unqualifiedClassName;
730 		}
731 
732 		if( getXJavaDoc().classExists( qualifiedClassName ) )
733 			return qualifiedClassName;
734 
735 		return null;
736 	}
737 
738 	/***
739 	 * Describe what the method does
740 	 *
741 	 * @param useNodeParser  Describe what the parameter does
742 	 */
743 	private void parse( boolean useNodeParser )
744 	{
745 		try
746 		{
747 			if( useNodeParser )
748 			{
749 				// We need a pool of parsers, because parsing one file
750 				// might kick away the parsing of another etc.
751 //				_nodeParser.populate( this );
752 				new NodeParser( getXJavaDoc(), getTagFactory() ).populate( this );
753 			}
754 			else
755 			{
756 //				_simpleParser.populate( this );
757 				new SimpleParser( getXJavaDoc(), getTagFactory() ).populate( this );
758 			}
759 		}
760 		catch( ParseException e )
761 		{
762 			// Source code is bad. Not according to grammar. User's fault.
763 			String cls = _sourceFile != null ? _sourceFile.toString() : getQualifiedName();
764 
765 			System.err.println( "Error parsing " + cls + ':' + e.getMessage() );
766 		}
767 		catch( TokenMgrError e )
768 		{
769 			String cls = _sourceFile != null ? _sourceFile.toString() : getQualifiedName();
770 
771 			System.err.println( "Error parsing " + cls + ':' + e.getMessage() );
772 		}
773 	}
774 }