001 /*
002 Copyright (C) 2000 Chr. Clemens Lee <clemens@kclee.com>.
003
004 This file is part of JavaNCSS
005 (http://www.kclee.com/clemens/java/javancss/).
006
007 JavaNCSS is free software; you can redistribute it and/or modify it
008 under the terms of the GNU General Public License as published by the
009 Free Software Foundation; either version 2, or (at your option) any
010 later version.
011
012 JavaNCSS is distributed in the hope that it will be useful, but WITHOUT
013 ANY WARRANTY; without even the implied warranty of MERCHANTABILITY or
014 FITNESS FOR A PARTICULAR PURPOSE. See the GNU General Public License
015 for more details.
016
017 You should have received a copy of the GNU General Public License
018 along with JavaNCSS; see the file COPYING. If not, write to
019 the Free Software Foundation, Inc., 59 Temple Place - Suite 330,
020 Boston, MA 02111-1307, USA. */
021
022 package javancss;
023
024 import java.awt.event.WindowAdapter;
025 import java.awt.event.WindowEvent;
026 import java.io.File;
027 import java.io.FileInputStream;
028 import java.io.FileNotFoundException;
029 import java.io.FileOutputStream;
030 import java.io.InputStream;
031 import java.io.IOException;
032 import java.io.InputStreamReader;
033 import java.io.OutputStream;
034 import java.io.OutputStreamWriter;
035 import java.io.PrintWriter;
036 import java.io.Reader;
037 import java.io.UnsupportedEncodingException;
038 import java.util.ArrayList;
039 import java.util.Collections;
040 import java.util.HashMap;
041 import java.util.HashSet;
042 import java.util.Iterator;
043 import java.util.List;
044 import java.util.Map;
045 import java.util.Set;
046
047 import ccl.util.Exitable;
048 import ccl.util.FileUtil;
049 import ccl.util.Init;
050 import ccl.util.Util;
051
052 import javancss.parser.JavaParser;
053 import javancss.parser.JavaParserInterface;
054 import javancss.parser.JavaParserTokenManager;
055 import javancss.parser.ParseException;
056 import javancss.parser.TokenMgrError;
057 import javancss.parser.debug.JavaParserDebug;
058 import javancss.parser.java15.JavaParser15;
059 import javancss.parser.java15.debug.JavaParser15Debug;
060 import javancss.test.JavancssTest;
061
062 /**
063 * While the Java parser class might be the heart of JavaNCSS,
064 * this class is the brain. This class controls input and output and
065 * invokes the Java parser.
066 *
067 * @author Chr. Clemens Lee <clemens@kclee.com>
068 * , recursive feature by P??k? Hannu
069 * , additional javadoc metrics by Emilio Gongora <emilio@sms.nl>
070 * , and Guillermo Rodriguez <guille@sms.nl>.
071 * @version $Id: Javancss.java 157 2009-05-24 06:59:58Z hboutemy $
072 */
073 public class Javancss implements Exitable
074 {
075 private static final String S_INIT__FILE_CONTENT =
076 "[Init]\n" +
077 "Author=Chr. Clemens Lee\n" +
078 "\n" +
079 "[Help]\n"+
080 "; Please do not edit the Help section\n"+
081 "HelpUsage=@srcfiles.txt | *.java | <stdin>\n" +
082 "Options=ncss,package,object,function,all,gui,xml,out,recursive,check,encoding,parser15\n" +
083 "ncss=b,o,Counts the program NCSS (default).\n" +
084 "package=b,o,Assembles a statistic on package level.\n" +
085 "object=b,o,Counts the object NCSS.\n" +
086 "function=b,o,Counts the function NCSS.\n" +
087 "all=b,o,The same as '-function -object -package'.\n" +
088 "gui=b,o,Opens a gui to present the '-all' output in tabbed panels.\n" +
089 "xml=b,o,Output in xml format.\n" +
090 "out=s,o,Output file name. By default output goes to standard out.\n"+
091 "recursive=b,o,Recurse to subdirs.\n" +
092 "check=b,o,Triggers a javancss self test.\n" +
093 "encoding=s,o,Encoding used while reading source files (default: platform encoding).\n" +
094 "parser15=b,o,Use new experimental Java 1.5 parser.\n" +
095 "\n" +
096 "[Colors]\n" +
097 "UseSystemColors=true\n";
098
099 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 }