View Javadoc

1   /*
2   Copyright (C) 2000 Chr. Clemens Lee <clemens@kclee.com>.
3   
4   This file is part of JavaNCSS
5   (http://www.kclee.com/clemens/java/javancss/).
6   
7   JavaNCSS is free software; you can redistribute it and/or modify it
8   under the terms of the GNU General Public License as published by the
9   Free Software Foundation; either version 2, or (at your option) any
10  later version.
11  
12  JavaNCSS is distributed in the hope that it will be useful, but WITHOUT
13  ANY WARRANTY; without even the implied warranty of MERCHANTABILITY or
14  FITNESS FOR A PARTICULAR PURPOSE.  See the GNU General Public License
15  for more details.
16  
17  You should have received a copy of the GNU General Public License
18  along with JavaNCSS; see the file COPYING.  If not, write to
19  the Free Software Foundation, Inc., 59 Temple Place - Suite 330,
20  Boston, MA 02111-1307, USA.  */
21  
22  package javancss;
23  
24  import java.awt.event.WindowAdapter;
25  import java.awt.event.WindowEvent;
26  import java.io.File;
27  import java.io.FileInputStream;
28  import java.io.FileNotFoundException;
29  import java.io.FileOutputStream;
30  import java.io.InputStream;
31  import java.io.IOException;
32  import java.io.InputStreamReader;
33  import java.io.OutputStream;
34  import java.io.OutputStreamWriter;
35  import java.io.PrintWriter;
36  import java.io.Reader;
37  import java.io.UnsupportedEncodingException;
38  import java.util.ArrayList;
39  import java.util.Collections;
40  import java.util.HashMap;
41  import java.util.HashSet;
42  import java.util.Iterator;
43  import java.util.List;
44  import java.util.Map;
45  import java.util.Set;
46  
47  import ccl.util.Exitable;
48  import ccl.util.FileUtil;
49  import ccl.util.Init;
50  import ccl.util.Util;
51  
52  import javancss.parser.JavaParser;
53  import javancss.parser.JavaParserInterface;
54  import javancss.parser.JavaParserTokenManager;
55  import javancss.parser.ParseException;
56  import javancss.parser.TokenMgrError;
57  import javancss.parser.debug.JavaParserDebug;
58  import javancss.parser.java15.JavaParser15;
59  import javancss.parser.java15.debug.JavaParser15Debug;
60  import javancss.test.JavancssTest;
61  
62  /**
63   * While the Java parser class might be the heart of JavaNCSS,
64   * this class is the brain. This class controls input and output and
65   * invokes the Java parser.
66   *
67   * @author    Chr. Clemens Lee <clemens@kclee.com>
68   *            , recursive feature by Pääkö Hannu
69   *            , additional javadoc metrics by Emilio Gongora <emilio@sms.nl>
70   *            , and Guillermo Rodriguez <guille@sms.nl>.
71   * @version   $Id: Javancss.java 157 2009-05-24 06:59:58Z hboutemy $
72   */
73  public class Javancss implements Exitable
74  {
75      private static final String S_INIT__FILE_CONTENT =
76          "[Init]\n" +
77          "Author=Chr. Clemens Lee\n" +
78          "\n" +
79          "[Help]\n"+
80          "; Please do not edit the Help section\n"+
81          "HelpUsage=@srcfiles.txt | *.java | <stdin>\n" +
82          "Options=ncss,package,object,function,all,gui,xml,out,recursive,check,encoding,parser15\n" +
83          "ncss=b,o,Counts the program NCSS (default).\n" +
84          "package=b,o,Assembles a statistic on package level.\n" +
85          "object=b,o,Counts the object NCSS.\n" +
86          "function=b,o,Counts the function NCSS.\n" +
87          "all=b,o,The same as '-function -object -package'.\n" +
88          "gui=b,o,Opens a gui to present the '-all' output in tabbed panels.\n" +
89          "xml=b,o,Output in xml format.\n" +
90          "out=s,o,Output file name. By default output goes to standard out.\n"+
91          "recursive=b,o,Recurse to subdirs.\n" +
92          "check=b,o,Triggers a javancss self test.\n" +
93          "encoding=s,o,Encoding used while reading source files (default: platform encoding).\n" +
94          "parser15=b,o,Use new experimental Java 1.5 parser.\n" +
95          "\n" +
96          "[Colors]\n" +
97          "UseSystemColors=true\n";
98  
99      private boolean _bExit = false;
100 
101     private List/*<File>*/ _vJavaSourceFiles = null;
102     private String encoding = null;
103 
104     private String _sErrorMessage = null;
105     private Throwable _thrwError = null;
106 
107     private JavaParserInterface _pJavaParser = null;
108     private int _ncss = 0;
109     private int _loc = 0;
110     private List/*<FunctionMetric>*/ _vFunctionMetrics = new ArrayList();
111     private List/*<ObjectMetric>*/ _vObjectMetrics = new ArrayList();
112     private List/*<PackageMetric>*/ _vPackageMetrics = null;
113     private List _vImports = null;
114     private Map/*<String,PackageMetric>*/ _htPackages = null;
115     private Object[] _aoPackage = null;
116 
117     /**
118      * Just used for parseImports.
119      */
120     private File _sJavaSourceFile = null;
121 
122     private Reader createSourceReader( File sSourceFile_ )
123     {
124         try
125         {
126             return newReader( sSourceFile_ );
127         }
128         catch ( IOException pIOException )
129         {
130             if ( Util.isEmpty( _sErrorMessage ) )
131             {
132                 _sErrorMessage = "";
133             }
134             else
135             {
136                 _sErrorMessage += "\n";
137             }
138             _sErrorMessage += "File not found: " + sSourceFile_.getAbsolutePath();
139             _thrwError = pIOException;
140 
141             return null;
142         }
143     }
144 
145     private void _measureSource( File sSourceFile_ ) throws IOException, Exception, Error
146     {
147         Reader reader = null;
148 
149         // opens the file
150         try
151         {
152             reader = newReader( sSourceFile_ );
153         }
154         catch ( IOException pIOException )
155         {
156             if ( Util.isEmpty( _sErrorMessage ) )
157             {
158                 _sErrorMessage = "";
159             }
160             else
161             {
162                 _sErrorMessage += "\n";
163             }
164             _sErrorMessage += "File not found: " + sSourceFile_.getAbsolutePath();
165             _thrwError = pIOException;
166 
167             throw pIOException;
168         }
169 
170         String sTempErrorMessage = _sErrorMessage;
171         try
172         {
173             // the same method but with a Reader
174             _measureSource( reader );
175         }
176         catch ( Exception pParseException )
177         {
178             if ( sTempErrorMessage == null )
179             {
180                 sTempErrorMessage = "";
181             }
182             sTempErrorMessage += "ParseException in " + sSourceFile_.getAbsolutePath() +
183                    "\nLast useful checkpoint: \"" + _pJavaParser.getLastFunction() + "\"\n";
184             sTempErrorMessage += pParseException.getMessage() + "\n";
185 
186             _sErrorMessage = sTempErrorMessage;
187             _thrwError = pParseException;
188 
189             throw pParseException;
190         }
191         catch ( Error pTokenMgrError )
192         {
193             if ( sTempErrorMessage == null )
194             {
195                 sTempErrorMessage = "";
196             }
197             sTempErrorMessage += "TokenMgrError in " + sSourceFile_.getAbsolutePath() +
198                    "\n" + pTokenMgrError.getMessage() + "\n";
199             _sErrorMessage = sTempErrorMessage;
200             _thrwError = pTokenMgrError;
201 
202             throw pTokenMgrError;
203         }
204     }
205 
206     private void _measureSource( Reader reader ) throws IOException, Exception, Error
207     {
208       Util.debug( "_measureSource(Reader).ENTER" );
209       //Util.debug( "_measureSource(Reader).parser15: -->" + (_pInit.getOptions().get( "parser15" ) + "<--" );
210       //Util.panicIf( _pInit == null );
211       //Util.panicIf( _pInit.getOptions() == null );
212       Util.debug( "_measureSource(Reader).ENTER2" );
213       try
214       {
215         // create a parser object
216         if ( Util.isDebug() == false )
217         {
218           if ( _pInit == null || _pInit.getOptions() == null || _pInit.getOptions().get( "parser15" ) == null ) {
219             Util.debug( "creating JavaParser" );
220             _pJavaParser = (JavaParserInterface)(new JavaParser( reader ));
221           } else {
222             Util.debug( "creating JavaParser15" );
223             _pJavaParser = (JavaParserInterface)(new JavaParser15( reader ));
224           }
225         } else {
226           if ( _pInit == null || _pInit.getOptions() == null || _pInit.getOptions().get( "parser15" ) == null ) {
227             Util.debug( "creating JavaParserDebug" );
228             Util.println( "creating JavaParserDebug" );
229             _pJavaParser = (JavaParserInterface)(new JavaParserDebug( reader ));
230           } else {
231             Util.debug( "creating JavaParser15Debug" );
232             _pJavaParser = (JavaParserInterface)(new JavaParser15Debug( reader ));
233           }
234         }
235 
236             // execute the parser
237             _pJavaParser.parse();
238             Util.debug( "Javancss._measureSource(DataInputStream).SUCCESSFULLY_PARSED" );
239 
240             _ncss += _pJavaParser.getNcss(); // increment the ncss
241             _loc += _pJavaParser.getLOC(); // and loc
242             // add new data to global vector
243             _vFunctionMetrics.addAll( _pJavaParser.getFunction() );
244             _vObjectMetrics.addAll( _pJavaParser.getObject() );
245             Map htNewPackages = _pJavaParser.getPackage();
246 
247             /* List vNewPackages = new Vector(); */
248             for ( Iterator ePackages = htNewPackages.entrySet().iterator(); ePackages.hasNext(); )
249             {
250                 String sPackage = (String) ( (Map.Entry) ePackages.next() ).getKey();
251 
252                 PackageMetric pckmNext = (PackageMetric) htNewPackages.get( sPackage );
253                 pckmNext.name = sPackage;
254 
255                 PackageMetric pckmPrevious = (PackageMetric) _htPackages.get( sPackage );
256                 pckmNext.add( pckmPrevious );
257 
258                 _htPackages.put( sPackage, pckmNext );
259             }
260         }
261         catch ( Exception pParseException )
262         {
263             if ( _sErrorMessage == null )
264             {
265                 _sErrorMessage = "";
266             }
267             _sErrorMessage += "ParseException in STDIN";
268             if ( _pJavaParser != null )
269             {
270                 _sErrorMessage += "\nLast useful checkpoint: \"" + _pJavaParser.getLastFunction() + "\"\n";
271             }
272             _sErrorMessage += pParseException.getMessage() + "\n";
273             _thrwError = pParseException;
274 
275             throw pParseException;
276         }
277         catch ( Error pTokenMgrError )
278         {
279             if ( _sErrorMessage == null )
280             {
281                 _sErrorMessage = "";
282             }
283             _sErrorMessage += "TokenMgrError in STDIN\n";
284             _sErrorMessage += pTokenMgrError.getMessage() + "\n";
285             _thrwError = pTokenMgrError;
286 
287             throw pTokenMgrError;
288         }
289     }
290 
291     private void _measureFiles( List/*<File>*/ vJavaSourceFiles_ ) throws IOException, ParseException, TokenMgrError
292     {
293         // for each file
294         for ( Iterator e = vJavaSourceFiles_.iterator(); e.hasNext(); )
295         {
296             File file = (File) e.next();
297 
298             try
299             {
300                 _measureSource( file );
301             }
302             catch ( Throwable pThrowable )
303             {
304                 // hmm, do nothing? Use getLastError() or so to check for details.
305             }
306         }
307     }
308 
309     /**
310      * If arguments were provided, they are used, otherwise
311      * the input stream is used.
312      */
313     private void _measureRoot( Reader reader ) throws IOException, Exception, Error
314     {
315         _htPackages = new HashMap();
316 
317         // either there are argument files, or stdin is used
318         if ( _vJavaSourceFiles == null )
319         {
320             _measureSource( reader );
321         }
322         else
323         {
324             // the collection of files get measured
325             _measureFiles( _vJavaSourceFiles );
326         }
327 
328         _vPackageMetrics = new ArrayList();
329         for ( Iterator ePackages = _htPackages.keySet().iterator(); ePackages.hasNext(); )
330         {
331             String sPackage = (String) ePackages.next();
332 
333             PackageMetric pckmNext = (PackageMetric) _htPackages.get( sPackage );
334             _vPackageMetrics.add( pckmNext );
335         }
336         Collections.sort( _vPackageMetrics );
337     }
338 
339     public List getImports() {
340         return _vImports;
341     }
342 
343     /**
344      * Return info about package statement.
345      * First element has name of package,
346      * then begin of line, etc.
347      */
348     public Object[] getPackage() {
349         return _aoPackage;
350     }
351 
352     /**
353      * The same as getFunctionMetrics?!
354      */
355     public List/*<FunctionMetric>*/ getFunctions() {
356         return _vFunctionMetrics;
357     }
358 
359     public String printObjectNcss() {
360         return getFormatter().printObjectNcss();
361     }
362 
363     public String printFunctionNcss() {
364         return getFormatter().printFunctionNcss();
365     }
366 
367     public String printPackageNcss() {
368         return getFormatter().printPackageNcss();
369     }
370 
371     public String printJavaNcss() {
372         return getFormatter().printJavaNcss();
373     }
374 
375     public Javancss( List/*<File>*/ vJavaSourceFiles_ )
376     {
377         _vJavaSourceFiles = vJavaSourceFiles_;
378         try {
379             _measureRoot(newReader(System.in));
380         } catch(Exception e) {
381             e.printStackTrace();
382         } catch(TokenMgrError pError) {
383             pError.printStackTrace();
384         }
385     }
386 
387     public Javancss( File sJavaSourceFile_ )
388     {
389         Util.debug( "Javancss.<init>(String).sJavaSourceFile_: " + sJavaSourceFile_ );
390         _sErrorMessage = null;
391         _vJavaSourceFiles = new ArrayList();
392         _vJavaSourceFiles.add(sJavaSourceFile_);
393         try {
394             _measureRoot(newReader(System.in));
395         } catch(Exception e) {
396             Util.debug( "Javancss.<init>(String).e: " + e );
397             e.printStackTrace();
398         } catch(TokenMgrError pError) {
399             Util.debug( "Javancss.<init>(String).pError: " + pError );
400             pError.printStackTrace();
401         }
402     }
403 
404     /**
405      * Only way to create object that does not immediately
406      * start to parse.
407      */
408     public Javancss() {
409         super();
410 
411         _sErrorMessage = null;
412         _thrwError = null;
413     }
414 
415     public boolean parseImports() {
416         if ( _sJavaSourceFile == null ) {
417             Util.debug( "Javancss.parseImports().NO_FILE" );
418 
419             return true;
420         }
421         Reader reader = createSourceReader( _sJavaSourceFile );
422         if ( reader == null ) {
423             Util.debug( "Javancss.parseImports().NO_DIS" );
424 
425             return true;
426         }
427 
428         try {
429             Util.debug( "Javancss.parseImports().START_PARSING" );
430             if ( Util.isDebug() == false ) {
431               _pJavaParser = (JavaParserInterface)(new JavaParser(reader));
432             } else {
433               _pJavaParser = (JavaParserInterface)(new JavaParserDebug(reader));
434             }
435             _pJavaParser.parseImportUnit();
436             _vImports = _pJavaParser.getImports();
437             _aoPackage = _pJavaParser.getPackageObjects();
438             Util.debug( "Javancss.parseImports().END_PARSING" );
439         } catch(Exception pParseException) {
440             Util.debug( "Javancss.parseImports().PARSE_EXCEPTION" );
441             if (_sErrorMessage == null) {
442                 _sErrorMessage = "";
443             }
444             _sErrorMessage += "ParseException in STDIN";
445             if (_pJavaParser != null) {
446                 _sErrorMessage += "\nLast useful checkpoint: \"" + _pJavaParser.getLastFunction() + "\"\n";
447             }
448             _sErrorMessage += pParseException.getMessage() + "\n";
449             _thrwError = pParseException;
450 
451             return true;
452         } catch(Error pTokenMgrError) {
453             Util.debug( "Javancss.parseImports().TOKEN_ERROR" );
454             if (_sErrorMessage == null) {
455                 _sErrorMessage = "";
456             }
457             _sErrorMessage += "TokenMgrError in STDIN\n";
458             _sErrorMessage += pTokenMgrError.getMessage() + "\n";
459             _thrwError = pTokenMgrError;
460 
461             return true;
462         }
463 
464         return false;
465     }
466 
467     public void setSourceFile( File javaSourceFile_ ) {
468         _sJavaSourceFile = javaSourceFile_;
469         _vJavaSourceFiles = new ArrayList();
470         _vJavaSourceFiles.add(javaSourceFile_);
471     }
472 
473     public Javancss(Reader reader) {
474         try {
475             _measureRoot(reader);
476         } catch(Exception e) {
477         } catch(TokenMgrError pError) {
478         }
479     }
480 
481     /**
482      * recursively adds *.java files
483      * @param dir the base directory to search
484      * @param v the list of file to add found files to
485      */
486     private static void _addJavaFiles( File dir, List v/*<File>*/ )
487     {
488         File[] files = dir.listFiles();
489         if( files == null || files.length == 0 )
490         {
491             return;
492         }
493 
494         for( int i = 0; i < files.length; i++ )
495         {
496             File newFile = files[i];
497             if( newFile.isDirectory() )
498             {
499                 //Recurse!!!
500                 _addJavaFiles( newFile, v );
501             }
502             else
503             {
504                 if( newFile.getName().endsWith( ".java" ) )
505                 {
506                     v.add( newFile );
507                 }
508             }
509         }
510     }
511 
512     private List/*<File>*/ findFiles( List/*<String>*/ filenames, boolean recursive ) throws IOException
513     {
514         if ( Util.isDebug() )
515         {
516             Util.debug( "filenames: " + Util.toString( filenames ) );
517         }
518         if ( filenames.size() == 0 )
519         {
520             if ( recursive )
521             {
522                 // If no files then add current directory!
523                 filenames.add( "." );
524             }
525             else
526             {
527                 return null;
528             }
529         }
530 
531         Set _processedAtFiles = new HashSet();
532         List newFiles = new ArrayList();
533         for ( Iterator iter = filenames.iterator(); iter.hasNext(); )
534         {
535             String filename = (String)iter.next();
536 
537             // if the file specifies other files...
538             if ( filename.startsWith( "@" ) )
539             {
540                 filename = filename.substring( 1 );
541                 if ( filename.length() > 1 )
542                 {
543                     filename = FileUtil.normalizeFileName( filename );
544                     if ( _processedAtFiles.add( filename ) )
545                     {
546                         String sJavaSourceFileNames = null;
547                         try
548                         {
549                             sJavaSourceFileNames = FileUtil.readFile( filename );
550                         }
551                         catch( IOException pIOException )
552                         {
553                             _sErrorMessage = "File Read Error: " + filename;
554                             _thrwError = pIOException;
555                             throw pIOException;
556                         }
557                         List vTheseJavaSourceFiles = Util.stringToLines( sJavaSourceFileNames );
558                         for ( Iterator iterator = vTheseJavaSourceFiles.iterator(); iterator.hasNext(); )
559                         {
560                             newFiles.add( new File( (String)iterator.next() ) );
561                         }
562                     }
563                 }
564             }
565             else
566             {
567                 filename = FileUtil.normalizeFileName( filename );
568                 File file = new File( filename );
569                 if ( file.isDirectory() )
570                 {
571                     _addJavaFiles( file, newFiles );
572                 }
573                 else
574                 {
575                     newFiles.add( file );
576                 }
577             }
578         }
579 
580         if ( Util.isDebug() )
581         {
582             Util.debug( "resolved filenames: " + Util.toString( newFiles ) );
583         }
584 
585         return newFiles;
586     }
587 
588     private Init _pInit = null;
589 
590     /**
591      * @deprecated use Javancss(String[]) instead, since the sRcsHeader_ parameter is not useful
592      */
593     public Javancss(String[] asArgs_, String sRcsHeader_) throws IOException {
594         this(asArgs_);
595     }
596 
597     /**
598      * This is the constructor used in the main routine in
599      * javancss.Main.
600      * Other constructors might be helpful to use Javancss out
601      * of other programs.
602      */
603     public Javancss(String[] asArgs_) throws IOException {
604         _pInit = new Init(this, asArgs_, Main.S_RCS_HEADER, S_INIT__FILE_CONTENT);
605         if (_bExit) {
606             return;
607         }
608         Map htOptions = _pInit.getOptions();
609 
610         setEncoding( (String) htOptions.get( "encoding" ) );
611 
612         if ( htOptions.get( "check" ) != null ) {
613             new JavancssTest().main( new File( _pInit.getApplicationPath() ) );
614             return;
615         }
616 
617         // the arguments (the files) to be processed
618         _vJavaSourceFiles = findFiles( _pInit.getArguments(), htOptions.get( "recursive" ) != null );
619 
620         if ( htOptions.get( "gui" ) != null )
621         {
622             final JavancssFrame pJavancssFrame = new JavancssFrame(_pInit);
623             /*final Thread pThread = Thread.currentThread();*/
624             pJavancssFrame.addWindowListener(new WindowAdapter() {
625                     public void windowClosing(WindowEvent e_) {
626                         Util.debug("JavancssAll.run().WindowAdapter.windowClosing().1");
627                         pJavancssFrame.setVisible(false);
628                         pJavancssFrame.dispose();
629                     }
630                 });
631             pJavancssFrame.setVisible(true);
632 
633             try {
634                 _measureRoot(newReader(System.in));
635             } catch(Throwable pThrowable) {
636                 // shouldn't we print something here?
637             }
638 
639             pJavancssFrame.showJavancss(this);
640             pJavancssFrame.setSelectedTab(JavancssFrame.S_PACKAGES);
641             pJavancssFrame.run();
642 
643             return;
644         }
645 
646         // this initiates the measurement
647         try
648         {
649             _measureRoot( newReader( System.in ) );
650         }
651         catch(Throwable pThrowable)
652         {
653         }
654         if ( getLastErrorMessage() != null )
655         {
656             Util.printlnErr( getLastErrorMessage() + "\n" );
657             if ( getNcss() <= 0 )
658             {
659                 return;
660             }
661         }
662 
663         boolean bNoNCSS = false;
664 
665         String sOutputFile = (String)htOptions.get( "out" );
666         OutputStream out = System.out;
667         if (sOutputFile != null)
668         {
669             try
670             {
671                 out = new FileOutputStream( FileUtil.normalizeFileName( sOutputFile ) );
672             } catch ( Exception exception ) {
673                 Util.printlnErr( "Error opening output file '"
674                                  + sOutputFile
675                                  + "': " + exception.getMessage() );
676 
677                 out = System.out;
678                 sOutputFile = null;
679             }
680         }
681         // TODO: encoding configuration support for result output
682         PrintWriter pw = useXML() ? new PrintWriter(new OutputStreamWriter(out, "UTF-8")) : new PrintWriter(out);
683 
684         if ( useXML() )
685         {
686             pw.print( XmlFormatter.printStart() );
687         }
688 
689         if (htOptions.get( "package" ) != null ||
690             htOptions.get( "all" ) != null)
691         {
692             pw.print( printPackageNcss() );
693             bNoNCSS = true;
694         }
695         if (htOptions.get( "object" ) != null ||
696             htOptions.get( "all" ) != null)
697         {
698             if ( bNoNCSS )
699             {
700                 pw.println();
701             }
702             pw.print( printObjectNcss() );
703             bNoNCSS = true;
704         }
705         if (htOptions.get( "function" ) != null ||
706             htOptions.get( "all" ) != null)
707         {
708             if ( bNoNCSS )
709             {
710                 pw.println();
711             }
712             pw.print( printFunctionNcss() );
713             bNoNCSS = true;
714         }
715         if (!bNoNCSS) {
716             pw.print( printJavaNcss() );
717         }
718 
719         if ( useXML() )
720         {
721             if ( !bNoNCSS )
722             {
723                 pw.print( printJavaNcss() );
724             }
725             pw.println( "</javancss>" );
726         }
727 
728         if ( sOutputFile != null )
729         {
730             pw.close();
731         } else
732         {
733             // stdout is used: don't close but ensure everything is flushed
734             pw.flush();
735         }
736     }
737 
738     public int getNcss() {
739         return _ncss;
740     }
741 
742     public int getLOC() {
743         return _loc;
744     }
745 
746     // added by SMS
747     public int getJvdc() {
748         return _pJavaParser.getJvdc();
749     }
750 
751     /**
752      * JDCL stands for javadoc comment lines (while jvdc stands
753      * for number of javadoc comments).
754      */
755     public int getJdcl() {
756         return JavaParserTokenManager._iFormalComments;
757     }
758 
759     public int getSl() {
760         return JavaParserTokenManager._iSingleComments;
761     }
762 
763     public int getMl() {
764         return JavaParserTokenManager._iMultiComments;
765     }
766     //
767 
768     public List getFunctionMetrics() {
769         return(_vFunctionMetrics);
770     }
771 
772     public List/*<ObjectMetric>*/ getObjectMetrics() {
773         return(_vObjectMetrics);
774     }
775 
776     /**
777      * Returns list of packages in the form
778      * PackageMetric objects.
779      */
780     public List getPackageMetrics() {
781         return(_vPackageMetrics);
782     }
783 
784     public String getLastErrorMessage() {
785         if (_sErrorMessage == null) {
786             return null;
787         }
788         return _sErrorMessage;
789     }
790 
791     public Throwable getLastError() {
792         return _thrwError;
793     }
794 
795     public void setExit() {
796         _bExit = true;
797     }
798 
799     private boolean _bXML = false;
800 
801     public void setXML( boolean bXML )
802     {
803         _bXML = bXML;
804     }
805 
806     public boolean useXML()
807     {
808         return _bXML
809                || (_pInit != null && _pInit.getOptions().get( "xml" ) != null );
810     }
811 
812     public Formatter getFormatter()
813     {
814         if ( useXML() )
815         {
816             return new XmlFormatter( this );
817         }
818 
819         return new AsciiFormatter( this );
820     }
821 
822     public String getEncoding()
823     {
824         return encoding;
825     }
826 
827     public void setEncoding( String encoding )
828     {
829         this.encoding = encoding;
830     }
831 
832     private Reader newReader( InputStream stream ) throws UnsupportedEncodingException
833     {
834         return ( encoding == null ) ? new InputStreamReader( stream ) : new InputStreamReader( stream, encoding );
835     }
836 
837     private Reader newReader( File file ) throws FileNotFoundException, UnsupportedEncodingException
838     {
839         return newReader( new FileInputStream( file ) );
840     }
841 }