001/*
002 * Cobertura - http://cobertura.sourceforge.net/
003 *
004 * This file was taken from JavaNCSS
005 * http://www.kclee.com/clemens/java/javancss/
006 * Copyright (C) 2000 Chr. Clemens Lee <clemens a.t kclee d.o.t com>
007 *
008 * Cobertura is free software; you can redistribute it and/or modify
009 * it under the terms of the GNU General Public License as published
010 * by the Free Software Foundation; either version 2 of the License,
011 * or (at your option) any later version.
012 *
013 * Cobertura is distributed in the hope that it will be useful, but
014 * WITHOUT ANY WARRANTY; without even the implied warranty of
015 * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU
016 * General Public License for more details.
017 *
018 * You should have received a copy of the GNU General Public License
019 * along with Cobertura; if not, write to the Free Software
020 * Foundation, Inc., 59 Temple Place, Suite 330, Boston, MA 02111-1307
021 * USA
022 */
023
024
025/*
026 *
027 * WARNING   WARNING   WARNING   WARNING   WARNING   WARNING   WARNING   WARNING   WARNING  
028 *
029 * WARNING TO COBERTURA DEVELOPERS
030 *
031 * DO NOT MODIFY THIS FILE!
032 *
033 * MODIFY THE FILES UNDER THE JAVANCSS DIRECTORY LOCATED AT THE ROOT OF THE COBERTURA PROJECT.
034 *
035 * FOLLOW THE PROCEDURE FOR MERGING THE LATEST JAVANCSS INTO COBERTURA LOCATED AT
036 * javancss/coberturaREADME.txt
037 *
038 * WARNING   WARNING   WARNING   WARNING   WARNING   WARNING   WARNING   WARNING   WARNING   
039 */
040
041package net.sourceforge.cobertura.javancss;
042
043import java.awt.event.WindowAdapter;
044import java.awt.event.WindowEvent;
045import java.io.File;
046import java.io.FileInputStream;
047import java.io.FileNotFoundException;
048import java.io.FileOutputStream;
049import java.io.InputStream;
050import java.io.IOException;
051import java.io.InputStreamReader;
052import java.io.OutputStream;
053import java.io.OutputStreamWriter;
054import java.io.PrintWriter;
055import java.io.Reader;
056import java.io.UnsupportedEncodingException;
057import java.util.ArrayList;
058import java.util.Collections;
059import java.util.HashMap;
060import java.util.HashSet;
061import java.util.Iterator;
062import java.util.List;
063import java.util.Map;
064import java.util.Set;
065
066import net.sourceforge.cobertura.javancss.ccl.Exitable;
067import net.sourceforge.cobertura.javancss.ccl.FileUtil;
068import net.sourceforge.cobertura.javancss.ccl.Init;
069import net.sourceforge.cobertura.javancss.ccl.Util;
070
071import net.sourceforge.cobertura.javancss.parser.JavaParser;
072import net.sourceforge.cobertura.javancss.parser.JavaParserInterface;
073import net.sourceforge.cobertura.javancss.parser.JavaParserTokenManager;
074import net.sourceforge.cobertura.javancss.parser.ParseException;
075import net.sourceforge.cobertura.javancss.parser.TokenMgrError;
076import net.sourceforge.cobertura.javancss.parser.debug.JavaParserDebug;
077import net.sourceforge.cobertura.javancss.parser.java15.JavaParser15;
078import net.sourceforge.cobertura.javancss.parser.java15.debug.JavaParser15Debug;
079import net.sourceforge.cobertura.javancss.test.JavancssTest;
080
081/**
082 * While the Java parser class might be the heart of JavaNCSS,
083 * this class is the brain. This class controls input and output and
084 * invokes the Java parser.
085 *
086 * @author    Chr. Clemens Lee <clemens@kclee.com>
087 *            , recursive feature by P??k? Hannu
088 *            , additional javadoc metrics by Emilio Gongora <emilio@sms.nl>
089 *            , and Guillermo Rodriguez <guille@sms.nl>.
090 * @version   $Id: Javancss.java 676 2009-09-04 13:42:13Z lewijw $
091 */
092public class Javancss implements Exitable
093{
094    private static final String S_INIT__FILE_CONTENT =
095        "[Init]\n" +
096        "Author=Chr. Clemens Lee\n" +
097        "\n" +
098        "[Help]\n"+
099        "; Please do not edit the Help section\n"+
100        "HelpUsage=@srcfiles.txt | *.java | <stdin>\n" +
101        "Options=ncss,package,object,function,all,gui,xml,out,recursive,check,encoding,parser15\n" +
102        "ncss=b,o,Counts the program NCSS (default).\n" +
103        "package=b,o,Assembles a statistic on package level.\n" +
104        "object=b,o,Counts the object NCSS.\n" +
105        "function=b,o,Counts the function NCSS.\n" +
106        "all=b,o,The same as '-function -object -package'.\n" +
107        "gui=b,o,Opens a gui to present the '-all' output in tabbed panels.\n" +
108        "xml=b,o,Output in xml format.\n" +
109        "out=s,o,Output file name. By default output goes to standard out.\n"+
110        "recursive=b,o,Recurse to subdirs.\n" +
111        "check=b,o,Triggers a javancss self test.\n" +
112        "encoding=s,o,Encoding used while reading source files (default: platform encoding).\n" +
113        "parser15=b,o,Use new experimental Java 1.5 parser.\n" +
114        "\n" +
115        "[Colors]\n" +
116        "UseSystemColors=true\n";
117
118    private boolean _bExit = false;
119
120    private List/*<File>*/ _vJavaSourceFiles = null;
121    private String encoding = null;
122
123    private String _sErrorMessage = null;
124    private Throwable _thrwError = null;
125
126    private JavaParserInterface _pJavaParser = null;
127    private int _ncss = 0;
128    private int _loc = 0;
129    private List/*<FunctionMetric>*/ _vFunctionMetrics = new ArrayList();
130    private List/*<ObjectMetric>*/ _vObjectMetrics = new ArrayList();
131    private List/*<PackageMetric>*/ _vPackageMetrics = null;
132    private List _vImports = null;
133    private Map/*<String,PackageMetric>*/ _htPackages = null;
134    private Object[] _aoPackage = null;
135
136    /**
137     * Just used for parseImports.
138     */
139    private File _sJavaSourceFile = null;
140
141    private Reader createSourceReader( File sSourceFile_ )
142    {
143        try
144        {
145            return newReader( sSourceFile_ );
146        }
147        catch ( IOException pIOException )
148        {
149            if ( Util.isEmpty( _sErrorMessage ) )
150            {
151                _sErrorMessage = "";
152            }
153            else
154            {
155                _sErrorMessage += "\n";
156            }
157            _sErrorMessage += "File not found: " + sSourceFile_.getAbsolutePath();
158            _thrwError = pIOException;
159
160            return null;
161        }
162    }
163
164    private void _measureSource( File sSourceFile_ ) throws IOException, Exception, Error
165    {
166        Reader reader = null;
167
168        // opens the file
169        try
170        {
171            reader = newReader( sSourceFile_ );
172        }
173        catch ( IOException pIOException )
174        {
175            if ( Util.isEmpty( _sErrorMessage ) )
176            {
177                _sErrorMessage = "";
178            }
179            else
180            {
181                _sErrorMessage += "\n";
182            }
183            _sErrorMessage += "File not found: " + sSourceFile_.getAbsolutePath();
184            _thrwError = pIOException;
185
186            throw pIOException;
187        }
188
189        String sTempErrorMessage = _sErrorMessage;
190        try
191        {
192            // the same method but with a Reader
193            _measureSource( reader );
194        }
195        catch ( Exception pParseException )
196        {
197            if ( sTempErrorMessage == null )
198            {
199                sTempErrorMessage = "";
200            }
201            sTempErrorMessage += "ParseException in " + sSourceFile_.getAbsolutePath() +
202                   "\nLast useful checkpoint: \"" + _pJavaParser.getLastFunction() + "\"\n";
203            sTempErrorMessage += pParseException.getMessage() + "\n";
204
205            _sErrorMessage = sTempErrorMessage;
206            _thrwError = pParseException;
207
208            throw pParseException;
209        }
210        catch ( Error pTokenMgrError )
211        {
212            if ( sTempErrorMessage == null )
213            {
214                sTempErrorMessage = "";
215            }
216            sTempErrorMessage += "TokenMgrError in " + sSourceFile_.getAbsolutePath() +
217                   "\n" + pTokenMgrError.getMessage() + "\n";
218            _sErrorMessage = sTempErrorMessage;
219            _thrwError = pTokenMgrError;
220
221            throw pTokenMgrError;
222        }
223    }
224
225    private void _measureSource( Reader reader ) throws IOException, Exception, Error
226    {
227      Util.debug( "_measureSource(Reader).ENTER" );
228      //Util.debug( "_measureSource(Reader).parser15: -->" + (_pInit.getOptions().get( "parser15" ) + "<--" );
229      //Util.panicIf( _pInit == null );
230      //Util.panicIf( _pInit.getOptions() == null );
231      Util.debug( "_measureSource(Reader).ENTER2" );
232      try
233      {
234        // create a parser object
235        if ( Util.isDebug() == false )
236        {
237          if ( _pInit == null || _pInit.getOptions() == null || _pInit.getOptions().get( "parser15" ) == null ) {
238            Util.debug( "creating JavaParser" );
239            _pJavaParser = (JavaParserInterface)(new JavaParser( reader ));
240          } else {
241            Util.debug( "creating JavaParser15" );
242            _pJavaParser = (JavaParserInterface)(new JavaParser15( reader ));
243          }
244        } else {
245          if ( _pInit == null || _pInit.getOptions() == null || _pInit.getOptions().get( "parser15" ) == null ) {
246            Util.debug( "creating JavaParserDebug" );
247            Util.println( "creating JavaParserDebug" );
248            _pJavaParser = (JavaParserInterface)(new JavaParserDebug( reader ));
249          } else {
250            Util.debug( "creating JavaParser15Debug" );
251            _pJavaParser = (JavaParserInterface)(new JavaParser15Debug( reader ));
252          }
253        }
254
255            // execute the parser
256            _pJavaParser.parse();
257            Util.debug( "Javancss._measureSource(DataInputStream).SUCCESSFULLY_PARSED" );
258
259            _ncss += _pJavaParser.getNcss(); // increment the ncss
260            _loc += _pJavaParser.getLOC(); // and loc
261            // add new data to global vector
262            _vFunctionMetrics.addAll( _pJavaParser.getFunction() );
263            _vObjectMetrics.addAll( _pJavaParser.getObject() );
264            Map htNewPackages = _pJavaParser.getPackage();
265
266            /* List vNewPackages = new Vector(); */
267            for ( Iterator ePackages = htNewPackages.entrySet().iterator(); ePackages.hasNext(); )
268            {
269                String sPackage = (String) ( (Map.Entry) ePackages.next() ).getKey();
270
271                PackageMetric pckmNext = (PackageMetric) htNewPackages.get( sPackage );
272                pckmNext.name = sPackage;
273
274                PackageMetric pckmPrevious = (PackageMetric) _htPackages.get( sPackage );
275                pckmNext.add( pckmPrevious );
276
277                _htPackages.put( sPackage, pckmNext );
278            }
279        }
280        catch ( Exception pParseException )
281        {
282            if ( _sErrorMessage == null )
283            {
284                _sErrorMessage = "";
285            }
286            _sErrorMessage += "ParseException in STDIN";
287            if ( _pJavaParser != null )
288            {
289                _sErrorMessage += "\nLast useful checkpoint: \"" + _pJavaParser.getLastFunction() + "\"\n";
290            }
291            _sErrorMessage += pParseException.getMessage() + "\n";
292            _thrwError = pParseException;
293
294            throw pParseException;
295        }
296        catch ( Error pTokenMgrError )
297        {
298            if ( _sErrorMessage == null )
299            {
300                _sErrorMessage = "";
301            }
302            _sErrorMessage += "TokenMgrError in STDIN\n";
303            _sErrorMessage += pTokenMgrError.getMessage() + "\n";
304            _thrwError = pTokenMgrError;
305
306            throw pTokenMgrError;
307        }
308    }
309
310    private void _measureFiles( List/*<File>*/ vJavaSourceFiles_ ) throws IOException, ParseException, TokenMgrError
311    {
312        // for each file
313        for ( Iterator e = vJavaSourceFiles_.iterator(); e.hasNext(); )
314        {
315            File file = (File) e.next();
316
317            try
318            {
319                _measureSource( file );
320            }
321            catch ( Throwable pThrowable )
322            {
323                // hmm, do nothing? Use getLastError() or so to check for details.
324            }
325        }
326    }
327
328    /**
329     * If arguments were provided, they are used, otherwise
330     * the input stream is used.
331     */
332    private void _measureRoot( Reader reader ) throws IOException, Exception, Error
333    {
334        _htPackages = new HashMap();
335
336        // either there are argument files, or stdin is used
337        if ( _vJavaSourceFiles == null )
338        {
339            _measureSource( reader );
340        }
341        else
342        {
343            // the collection of files get measured
344            _measureFiles( _vJavaSourceFiles );
345        }
346
347        _vPackageMetrics = new ArrayList();
348        for ( Iterator ePackages = _htPackages.keySet().iterator(); ePackages.hasNext(); )
349        {
350            String sPackage = (String) ePackages.next();
351
352            PackageMetric pckmNext = (PackageMetric) _htPackages.get( sPackage );
353            _vPackageMetrics.add( pckmNext );
354        }
355    }
356
357    public List getImports() {
358        return _vImports;
359    }
360
361    /**
362     * Return info about package statement.
363     * First element has name of package,
364     * then begin of line, etc.
365     */
366    public Object[] getPackage() {
367        return _aoPackage;
368    }
369
370    /**
371     * The same as getFunctionMetrics?!
372     */
373    public List/*<FunctionMetric>*/ getFunctions() {
374        return _vFunctionMetrics;
375    }
376
377    public Javancss( List/*<File>*/ vJavaSourceFiles_ )
378    {
379        _vJavaSourceFiles = vJavaSourceFiles_;
380        try {
381            _measureRoot(newReader(System.in));
382        } catch(Exception e) {
383            e.printStackTrace();
384        } catch(TokenMgrError pError) {
385            pError.printStackTrace();
386        }
387    }
388
389    public Javancss( File sJavaSourceFile_ )
390    {
391        Util.debug( "Javancss.<init>(String).sJavaSourceFile_: " + sJavaSourceFile_ );
392        _sErrorMessage = null;
393        _vJavaSourceFiles = new ArrayList();
394        _vJavaSourceFiles.add(sJavaSourceFile_);
395        try {
396            _measureRoot(newReader(System.in));
397        } catch(Exception e) {
398                Util.debug( "Javancss.<init>(String).e: " + e );
399            e.printStackTrace();
400        } catch(TokenMgrError pError) {
401                Util.debug( "Javancss.<init>(String).pError: " + pError );
402            pError.printStackTrace();
403        }
404    }
405
406    /*
407     * cobertura:  add this next constructor so any input stream can be used.
408     * 
409     * It should be a copy of the Javancss(String) constructor, but just
410     * make sure _vJavaSourceFiles is null.   _measureRoot will
411     * use the input stream if it is null.
412     */
413    public Javancss(InputStream isJavaSource_) {
414        Util.debug( "Javancss.<init>(InputStream).sJavaSourceFile_: " + isJavaSource_ );
415        _sErrorMessage = null;
416        _vJavaSourceFiles = null;
417
418        try {
419            _measureRoot(newReader(isJavaSource_));
420        } catch(Exception e) {
421                Util.debug( "Javancss.<init>(InputStream).e: " + e );
422            e.printStackTrace();
423        } catch(TokenMgrError pError) {
424                Util.debug( "Javancss.<init>(InputStream).pError: " + pError );
425            pError.printStackTrace();
426        }
427    }
428
429    /**
430     * Only way to create object that does not immediately
431     * start to parse.
432     */
433    public Javancss() {
434        super();
435
436        _sErrorMessage = null;
437        _thrwError = null;
438    }
439
440    public boolean parseImports() {
441        if ( _sJavaSourceFile == null ) {
442                Util.debug( "Javancss.parseImports().NO_FILE" );
443
444            return true;
445        }
446        Reader reader = createSourceReader( _sJavaSourceFile );
447        if ( reader == null ) {
448                Util.debug( "Javancss.parseImports().NO_DIS" );
449
450            return true;
451        }
452
453        try {
454            Util.debug( "Javancss.parseImports().START_PARSING" );
455            if ( Util.isDebug() == false ) {
456              _pJavaParser = (JavaParserInterface)(new JavaParser(reader));
457            } else {
458              _pJavaParser = (JavaParserInterface)(new JavaParserDebug(reader));
459            }
460            _pJavaParser.parseImportUnit();
461            _vImports = _pJavaParser.getImports();
462            _aoPackage = _pJavaParser.getPackageObjects();
463            Util.debug( "Javancss.parseImports().END_PARSING" );
464        } catch(Exception pParseException) {
465                Util.debug( "Javancss.parseImports().PARSE_EXCEPTION" );
466            if (_sErrorMessage == null) {
467                _sErrorMessage = "";
468            }
469            _sErrorMessage += "ParseException in STDIN";
470            if (_pJavaParser != null) {
471                _sErrorMessage += "\nLast useful checkpoint: \"" + _pJavaParser.getLastFunction() + "\"\n";
472            }
473            _sErrorMessage += pParseException.getMessage() + "\n";
474            _thrwError = pParseException;
475
476            return true;
477        } catch(Error pTokenMgrError) {
478                Util.debug( "Javancss.parseImports().TOKEN_ERROR" );
479            if (_sErrorMessage == null) {
480                _sErrorMessage = "";
481            }
482            _sErrorMessage += "TokenMgrError in STDIN\n";
483            _sErrorMessage += pTokenMgrError.getMessage() + "\n";
484            _thrwError = pTokenMgrError;
485
486            return true;
487        }
488
489        return false;
490    }
491
492    public void setSourceFile( File javaSourceFile_ ) {
493        _sJavaSourceFile = javaSourceFile_;
494        _vJavaSourceFiles = new ArrayList();
495        _vJavaSourceFiles.add(javaSourceFile_);
496    }
497    private Init _pInit = null;
498    public int getNcss() {
499        return _ncss;
500    }
501
502    public int getLOC() {
503        return _loc;
504    }
505
506    // added by SMS
507    public int getJvdc() {
508        return _pJavaParser.getJvdc();
509    }
510
511    /**
512     * JDCL stands for javadoc comment lines (while jvdc stands
513     * for number of javadoc comments).
514     */
515    public int getJdcl() {
516        return JavaParserTokenManager._iFormalComments;
517    }
518
519    public int getSl() {
520        return JavaParserTokenManager._iSingleComments;
521    }
522
523    public int getMl() {
524        return JavaParserTokenManager._iMultiComments;
525    }
526    //
527
528    public List getFunctionMetrics() {
529        return(_vFunctionMetrics);
530    }
531
532    public List/*<ObjectMetric>*/ getObjectMetrics() {
533        return(_vObjectMetrics);
534    }
535
536    /**
537     * Returns list of packages in the form
538     * PackageMetric objects.
539     */
540    public List getPackageMetrics() {
541        return(_vPackageMetrics);
542    }
543
544    public String getLastErrorMessage() {
545        if (_sErrorMessage == null) {
546            return null;
547        }
548        return _sErrorMessage;
549    }
550
551    public Throwable getLastError() {
552        return _thrwError;
553    }
554
555    public void setExit() {
556        _bExit = true;
557    }
558
559
560    public String getEncoding()
561    {
562        return encoding;
563    }
564
565    public void setEncoding( String encoding )
566    {
567        this.encoding = encoding;
568    }
569
570    private Reader newReader( InputStream stream ) throws UnsupportedEncodingException
571    {
572        return ( encoding == null ) ? new InputStreamReader( stream ) : new InputStreamReader( stream, encoding );
573    }
574
575    private Reader newReader( File file ) throws FileNotFoundException, UnsupportedEncodingException
576    {
577        return newReader( new FileInputStream( file ) );
578    }
579}