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    
041    package net.sourceforge.cobertura.javancss;
042    
043    import java.awt.event.WindowAdapter;
044    import java.awt.event.WindowEvent;
045    import java.io.File;
046    import java.io.FileInputStream;
047    import java.io.FileNotFoundException;
048    import java.io.FileOutputStream;
049    import java.io.InputStream;
050    import java.io.IOException;
051    import java.io.InputStreamReader;
052    import java.io.OutputStream;
053    import java.io.OutputStreamWriter;
054    import java.io.PrintWriter;
055    import java.io.Reader;
056    import java.io.UnsupportedEncodingException;
057    import java.util.ArrayList;
058    import java.util.Collections;
059    import java.util.HashMap;
060    import java.util.HashSet;
061    import java.util.Iterator;
062    import java.util.List;
063    import java.util.Map;
064    import java.util.Set;
065    
066    import net.sourceforge.cobertura.javancss.ccl.Exitable;
067    import net.sourceforge.cobertura.javancss.ccl.FileUtil;
068    import net.sourceforge.cobertura.javancss.ccl.Init;
069    import net.sourceforge.cobertura.javancss.ccl.Util;
070    
071    import net.sourceforge.cobertura.javancss.parser.JavaParser;
072    import net.sourceforge.cobertura.javancss.parser.JavaParserInterface;
073    import net.sourceforge.cobertura.javancss.parser.JavaParserTokenManager;
074    import net.sourceforge.cobertura.javancss.parser.ParseException;
075    import net.sourceforge.cobertura.javancss.parser.TokenMgrError;
076    import net.sourceforge.cobertura.javancss.parser.debug.JavaParserDebug;
077    import net.sourceforge.cobertura.javancss.parser.java15.JavaParser15;
078    import net.sourceforge.cobertura.javancss.parser.java15.debug.JavaParser15Debug;
079    import 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     */
092    public 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    }