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