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 Paako Hannu
088 *            , additional javadoc metrics by Emilio Gongora <emilio@sms.nl>
089 *            , and Guillermo Rodriguez <guille@sms.nl>.
090 * @version   $Id: Javancss.java 198 2009-09-07 21:43:19Z hboutemy $
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            boolean parser15 = _pInit != null && _pInit.getOptions() != null && _pInit.getOptions().get( "parser15" ) != null;
236            if ( Util.isDebug() )
237            {
238                if ( parser15 )
239                {
240                    Util.debug( "creating JavaParser15Debug" );
241                    _pJavaParser = new JavaParser15Debug( reader );
242                }
243                else
244                {
245                    Util.debug( "creating JavaParserDebug" );
246                    _pJavaParser = new JavaParserDebug( reader );
247                }
248            }
249            else
250            {
251                if ( parser15 )
252                {
253                    Util.debug( "creating JavaParser15" );
254                    _pJavaParser = new JavaParser15( reader );
255                }
256                else
257                {
258                    Util.debug( "creating JavaParser" );
259                    _pJavaParser = new JavaParser( reader );
260                }
261            }
262
263            // execute the parser
264            _pJavaParser.parse();
265            Util.debug( "Javancss._measureSource(DataInputStream).SUCCESSFULLY_PARSED" );
266
267            _ncss += _pJavaParser.getNcss(); // increment the ncss
268            _loc += _pJavaParser.getLOC(); // and loc
269            // add new data to global vector
270            _vFunctionMetrics.addAll( _pJavaParser.getFunction() );
271            _vObjectMetrics.addAll( _pJavaParser.getObject() );
272            Map htNewPackages = _pJavaParser.getPackage();
273
274            /* List vNewPackages = new Vector(); */
275            for ( Iterator ePackages = htNewPackages.entrySet().iterator(); ePackages.hasNext(); )
276            {
277                String sPackage = (String) ( (Map.Entry) ePackages.next() ).getKey();
278
279                PackageMetric pckmNext = (PackageMetric) htNewPackages.get( sPackage );
280                pckmNext.name = sPackage;
281
282                PackageMetric pckmPrevious = (PackageMetric) _htPackages.get( sPackage );
283                pckmNext.add( pckmPrevious );
284
285                _htPackages.put( sPackage, pckmNext );
286            }
287        }
288        catch ( Exception pParseException )
289        {
290            if ( _sErrorMessage == null )
291            {
292                _sErrorMessage = "";
293            }
294            _sErrorMessage += "ParseException in STDIN";
295            if ( _pJavaParser != null )
296            {
297                _sErrorMessage += "\nLast useful checkpoint: \"" + _pJavaParser.getLastFunction() + "\"\n";
298            }
299            _sErrorMessage += pParseException.getMessage() + "\n";
300            _thrwError = pParseException;
301
302            throw pParseException;
303        }
304        catch ( Error pTokenMgrError )
305        {
306            if ( _sErrorMessage == null )
307            {
308                _sErrorMessage = "";
309            }
310            _sErrorMessage += "TokenMgrError in STDIN\n";
311            _sErrorMessage += pTokenMgrError.getMessage() + "\n";
312            _thrwError = pTokenMgrError;
313
314            throw pTokenMgrError;
315        }
316    }
317
318    private void _measureFiles( List/*<File>*/ vJavaSourceFiles_ ) throws IOException, ParseException, TokenMgrError
319    {
320        // for each file
321        for ( Iterator e = vJavaSourceFiles_.iterator(); e.hasNext(); )
322        {
323            File file = (File) e.next();
324
325            try
326            {
327                _measureSource( file );
328            }
329            catch ( Throwable pThrowable )
330            {
331                // hmm, do nothing? Use getLastError() or so to check for details.
332            }
333        }
334    }
335
336    /**
337     * If arguments were provided, they are used, otherwise
338     * the input stream is used.
339     */
340    private void _measureRoot( Reader reader ) throws IOException, Exception, Error
341    {
342        _htPackages = new HashMap();
343
344        // either there are argument files, or stdin is used
345        if ( _vJavaSourceFiles == null )
346        {
347            _measureSource( reader );
348        }
349        else
350        {
351            // the collection of files get measured
352            _measureFiles( _vJavaSourceFiles );
353        }
354
355        _vPackageMetrics = new ArrayList();
356        for ( Iterator ePackages = _htPackages.keySet().iterator(); ePackages.hasNext(); )
357        {
358            String sPackage = (String) ePackages.next();
359
360            PackageMetric pckmNext = (PackageMetric) _htPackages.get( sPackage );
361            _vPackageMetrics.add( pckmNext );
362        }
363    }
364
365    public List getImports() {
366        return _vImports;
367    }
368
369    /**
370     * Return info about package statement.
371     * First element has name of package,
372     * then begin of line, etc.
373     */
374    public Object[] getPackage() {
375        return _aoPackage;
376    }
377
378    /**
379     * The same as getFunctionMetrics?!
380     */
381    public List/*<FunctionMetric>*/ getFunctions() {
382        return _vFunctionMetrics;
383    }
384
385    public Javancss( List/*<File>*/ vJavaSourceFiles_ )
386    {
387        _vJavaSourceFiles = vJavaSourceFiles_;
388        try {
389            _measureRoot(newReader(System.in));
390        } catch(Exception e) {
391            e.printStackTrace();
392        } catch(TokenMgrError pError) {
393            pError.printStackTrace();
394        }
395    }
396
397    public Javancss( File sJavaSourceFile_ )
398    {
399        Util.debug( "Javancss.<init>(String).sJavaSourceFile_: " + sJavaSourceFile_ );
400        _sErrorMessage = null;
401        _vJavaSourceFiles = new ArrayList();
402        _vJavaSourceFiles.add(sJavaSourceFile_);
403        try {
404            _measureRoot(newReader(System.in));
405        } catch(Exception e) {
406                Util.debug( "Javancss.<init>(String).e: " + e );
407            e.printStackTrace();
408        } catch(TokenMgrError pError) {
409                Util.debug( "Javancss.<init>(String).pError: " + pError );
410            pError.printStackTrace();
411        }
412    }
413
414    /*
415     * cobertura:  add this next constructor so any input stream can be used.
416     * 
417     * It should (more or less) be a copy of the Javancss(File) constructor, but just
418     * make sure _vJavaSourceFiles is null.   _measureRoot will
419     * use the input stream if it is null.
420     */
421    public Javancss(InputStream isJavaSource_) {
422        Util.debug( "Javancss.<init>(InputStream).sJavaSourceFile_: " + isJavaSource_ );
423        _sErrorMessage = null;
424        _vJavaSourceFiles = null;
425
426        try {
427            _measureRoot(newReader(isJavaSource_));
428        } catch(Exception e) {
429                Util.debug( "Javancss.<init>(InputStream).e: " + e );
430            e.printStackTrace();
431        } catch(TokenMgrError pError) {
432                Util.debug( "Javancss.<init>(InputStream).pError: " + pError );
433            pError.printStackTrace();
434        }
435    }
436
437    /**
438     * Only way to create object that does not immediately
439     * start to parse.
440     */
441    public Javancss() {
442        super();
443
444        _sErrorMessage = null;
445        _thrwError = null;
446    }
447
448    public boolean parseImports() {
449        if ( _sJavaSourceFile == null ) {
450                Util.debug( "Javancss.parseImports().NO_FILE" );
451
452            return true;
453        }
454        Reader reader = createSourceReader( _sJavaSourceFile );
455        if ( reader == null ) {
456                Util.debug( "Javancss.parseImports().NO_DIS" );
457
458            return true;
459        }
460
461        try {
462            Util.debug( "Javancss.parseImports().START_PARSING" );
463            if ( Util.isDebug() == false ) {
464              _pJavaParser = (JavaParserInterface)(new JavaParser(reader));
465            } else {
466              _pJavaParser = (JavaParserInterface)(new JavaParserDebug(reader));
467            }
468            _pJavaParser.parseImportUnit();
469            _vImports = _pJavaParser.getImports();
470            _aoPackage = _pJavaParser.getPackageObjects();
471            Util.debug( "Javancss.parseImports().END_PARSING" );
472        } catch(Exception pParseException) {
473                Util.debug( "Javancss.parseImports().PARSE_EXCEPTION" );
474            if (_sErrorMessage == null) {
475                _sErrorMessage = "";
476            }
477            _sErrorMessage += "ParseException in STDIN";
478            if (_pJavaParser != null) {
479                _sErrorMessage += "\nLast useful checkpoint: \"" + _pJavaParser.getLastFunction() + "\"\n";
480            }
481            _sErrorMessage += pParseException.getMessage() + "\n";
482            _thrwError = pParseException;
483
484            return true;
485        } catch(Error pTokenMgrError) {
486                Util.debug( "Javancss.parseImports().TOKEN_ERROR" );
487            if (_sErrorMessage == null) {
488                _sErrorMessage = "";
489            }
490            _sErrorMessage += "TokenMgrError in STDIN\n";
491            _sErrorMessage += pTokenMgrError.getMessage() + "\n";
492            _thrwError = pTokenMgrError;
493
494            return true;
495        }
496
497        return false;
498    }
499
500    public void setSourceFile( File javaSourceFile_ ) {
501        _sJavaSourceFile = javaSourceFile_;
502        _vJavaSourceFiles = new ArrayList();
503        _vJavaSourceFiles.add(javaSourceFile_);
504    }
505    private Init _pInit = null;
506    public int getNcss() {
507        return _ncss;
508    }
509
510    public int getLOC() {
511        return _loc;
512    }
513
514    // added by SMS
515    public int getJvdc() {
516        return _pJavaParser.getJvdc();
517    }
518
519    /**
520     * JDCL stands for javadoc comment lines (while jvdc stands
521     * for number of javadoc comments).
522     */
523    public int getJdcl() {
524        return JavaParserTokenManager._iFormalComments;
525    }
526
527    public int getSl() {
528        return JavaParserTokenManager._iSingleComments;
529    }
530
531    public int getMl() {
532        return JavaParserTokenManager._iMultiComments;
533    }
534    //
535
536    public List getFunctionMetrics() {
537        return(_vFunctionMetrics);
538    }
539
540    public List/*<ObjectMetric>*/ getObjectMetrics() {
541        return(_vObjectMetrics);
542    }
543
544    /**
545     * Returns list of packages in the form
546     * PackageMetric objects.
547     */
548    public List getPackageMetrics() {
549        return(_vPackageMetrics);
550    }
551
552    public String getLastErrorMessage() {
553        if (_sErrorMessage == null) {
554            return null;
555        }
556        return _sErrorMessage;
557    }
558
559    public Throwable getLastError() {
560        return _thrwError;
561    }
562
563    public void setExit() {
564        _bExit = true;
565    }
566
567
568    public String getEncoding()
569    {
570        return encoding;
571    }
572
573    public void setEncoding( String encoding )
574    {
575        this.encoding = encoding;
576    }
577
578    private Reader newReader( InputStream stream ) throws UnsupportedEncodingException
579    {
580        return ( encoding == null ) ? new InputStreamReader( stream ) : new InputStreamReader( stream, encoding );
581    }
582
583    private Reader newReader( File file ) throws FileNotFoundException, UnsupportedEncodingException
584    {
585        return newReader( new FileInputStream( file ) );
586    }
587}