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    package net.sourceforge.cobertura.javancss;
025    
026    import java.io.DataInputStream;
027    import java.io.FileInputStream;
028    import java.io.IOException;
029    import java.io.InputStream;
030    import java.util.Enumeration;
031    import java.util.Hashtable;
032    import java.util.Vector;
033    
034    /**
035     * While the Java parser class might be the heart of JavaNCSS,
036     * this class is the brain. This class controls input and output and
037     * invokes the Java parser.
038     *
039     * @author    Chr. Clemens Lee <clemens@kclee.com>
040     *            , recursive feature by Pääkö Hannu
041     *            , additional javadoc metrics by Emilio Gongora <emilio@sms.nl>
042     *            , and Guillermo Rodriguez <guille@sms.nl>.
043     * @version   $Id: Javancss.java 553 2009-01-08 20:41:52Z lewijw $
044     */
045    public class Javancss implements JavancssConstants
046    {
047        static final int LEN_NR = 3;
048        static final String S_INIT__FILE_CONTENT =
049            "[Init]\n" +
050            "Author=Chr. Clemens Lee\n" +
051            "\n" +
052            "[Help]\n"+
053            "; Please do not edit the Help section\n"+
054            "HelpUsage=@srcfiles.txt | *.java | <stdin>\n" +
055            "Options=ncss,package,object,function,all,gui,xml,out,recursive,check\n" +
056            "ncss=b,o,Counts the program NCSS (default).\n" +
057            "package=b,o,Assembles a statistic on package level.\n" +
058            "object=b,o,Counts the object NCSS.\n" +
059            "function=b,o,Counts the function NCSS.\n" +
060            "all=b,o,The same as '-function -object -package'.\n" +
061            "gui=b,o,Opens a gui to present the '-all' output in tabbed panels.\n" +
062            "xml=b,o,Output in xml format.\n" +
063            "out=s,o,Output file name. By default output goes to standard out.\n"+
064            "recursive=b,o,Recurse to subdirs.\n" +
065            "check=b,o,Triggers a javancss self test.\n" +
066            "\n" +
067            "[Colors]\n" +
068            "UseSystemColors=true\n";
069        
070        private int _ncss = 0;
071        private int _loc = 0;
072        private JavaParser _pJavaParser = null;
073        private Vector _vJavaSourceFiles = new Vector();
074        private String _sErrorMessage = null;
075        private Throwable _thrwError = null;
076        private Vector _vFunctionMetrics = new Vector();
077        private Vector _vObjectMetrics = new Vector();
078        private Vector _vPackageMetrics = null;
079        private Vector _vImports = null;
080        private Hashtable _htPackages = null;
081        private Hashtable _htProcessedAtFiles = new Hashtable();
082        private Object[] _aoPackage = null;
083    
084        /**
085         * Just used for parseImports.
086         */
087        private String _sJavaSourceFileName = null;
088    
089        private DataInputStream createInputStream( String sSourceFileName_ )
090        {
091            DataInputStream disSource = null;
092    
093            try {
094                disSource = new DataInputStream
095                       (new FileInputStream(sSourceFileName_));
096            } catch(IOException pIOException) {
097                if ( Util.isEmpty( _sErrorMessage ) )
098                {
099                    _sErrorMessage = "";
100                }
101                else
102                {
103                    _sErrorMessage += "\n";
104                }
105                _sErrorMessage += "File not found: " + sSourceFileName_;
106                _thrwError = pIOException;
107    
108                return null;
109            }
110    
111            return disSource;
112        }
113    
114        private void _measureSource(String sSourceFileName_)
115            throws IOException,
116                   ParseException,
117                   TokenMgrError
118        {
119            // take user.dir property in account
120            sSourceFileName_ = FileUtil.normalizeFileName( sSourceFileName_ );
121    
122            DataInputStream disSource = null;
123    
124            // opens the file
125            try 
126            {
127                disSource = new DataInputStream
128                       (new FileInputStream(sSourceFileName_));
129            }
130            catch(IOException pIOException) 
131            {
132                if ( Util.isEmpty( _sErrorMessage ) )
133                {
134                    _sErrorMessage = "";
135                }
136                else
137                {
138                    _sErrorMessage += "\n";
139                }
140                _sErrorMessage += "File not found: " + sSourceFileName_;
141                _thrwError = pIOException;
142    
143                throw pIOException;
144            }
145    
146            String sTempErrorMessage = _sErrorMessage;
147            try {
148                // the same method but with a DataInputSream
149                _measureSource(disSource);
150            } catch(ParseException pParseException) {
151                if (sTempErrorMessage == null) {
152                    sTempErrorMessage = "";
153                }
154                sTempErrorMessage += "ParseException in " + sSourceFileName_ + 
155                       "\nLast useful checkpoint: \"" + _pJavaParser.getLastFunction() + "\"\n";
156                sTempErrorMessage += pParseException.getMessage() + "\n";
157                
158                _sErrorMessage = sTempErrorMessage;
159                _thrwError = pParseException;
160                
161                throw pParseException;
162            } catch(TokenMgrError pTokenMgrError) {
163                if (sTempErrorMessage == null) {
164                    sTempErrorMessage = "";
165                }
166                sTempErrorMessage += "TokenMgrError in " + sSourceFileName_ + 
167                       "\n" + pTokenMgrError.getMessage() + "\n";
168                _sErrorMessage = sTempErrorMessage;
169                _thrwError = pTokenMgrError;
170                
171                throw pTokenMgrError;
172            }
173        }
174    
175        private void _measureSource(DataInputStream disSource_)
176            throws IOException,
177                   ParseException,
178                   TokenMgrError
179        {
180            try {
181                // create a parser object
182                _pJavaParser = new JavaParser(disSource_);
183                // execute the parser
184                _pJavaParser.CompilationUnit();
185                _ncss += _pJavaParser.getNcss();       // increment the ncss
186                _loc  += _pJavaParser.getLOC();        // and loc
187                // add new data to global vector
188                _vFunctionMetrics.addAll(_pJavaParser.getFunction());
189                _vObjectMetrics.addAll(_pJavaParser.getObject());
190                Hashtable htNewPackages = _pJavaParser.getPackage();
191                /*Vector vNewPackages = new Vector();*/
192                for(Enumeration ePackages = htNewPackages.keys();
193                    ePackages.hasMoreElements(); )
194                {
195                    String sPackage = (String)ePackages.nextElement();
196                    PackageMetric pckmNext = (PackageMetric)htNewPackages.
197                           get(sPackage);
198                    pckmNext.name = sPackage;
199                    PackageMetric pckmPrevious =
200                           (PackageMetric)_htPackages.get
201                           (sPackage);
202                    pckmNext.add(pckmPrevious);
203                    _htPackages.put(sPackage, pckmNext);
204                }
205            } catch(ParseException pParseException) {
206                if (_sErrorMessage == null) {
207                    _sErrorMessage = "";
208                }
209                _sErrorMessage += "ParseException in STDIN";
210                if (_pJavaParser != null) {
211                    _sErrorMessage += "\nLast useful checkpoint: \"" + _pJavaParser.getLastFunction() + "\"\n";
212                }
213                _sErrorMessage += pParseException.getMessage() + "\n";
214                _thrwError = pParseException;
215                
216                throw pParseException;
217            } catch(TokenMgrError pTokenMgrError) {
218                if (_sErrorMessage == null) {
219                    _sErrorMessage = "";
220                }
221                _sErrorMessage += "TokenMgrError in STDIN\n";
222                _sErrorMessage += pTokenMgrError.getMessage() + "\n";
223                _thrwError = pTokenMgrError;
224                
225                throw pTokenMgrError;
226            }
227        }
228    
229        private void _measureFiles(Vector vJavaSourceFiles_)
230            throws IOException,
231                   ParseException,
232                   TokenMgrError
233        {
234            // for each file
235            for(Enumeration e = vJavaSourceFiles_.elements(); e.hasMoreElements(); ) 
236            {
237                String sJavaFileName = (String)e.nextElement();
238    
239                // if the file specifies other files...
240                if (sJavaFileName.charAt(0) == '@') 
241                {
242                    if (sJavaFileName.length() > 1) 
243                    {
244                        String sFileName = sJavaFileName.substring(1);
245                        sFileName = FileUtil.normalizeFileName( sFileName );
246                        if (_htProcessedAtFiles.get(sFileName) != null) 
247                        {
248                            continue;
249                        }
250                        _htProcessedAtFiles.put( sFileName, sFileName );
251                        String sJavaSourceFileNames = null;
252                        try 
253                        {
254                            sJavaSourceFileNames = FileUtil.readFile(sFileName);
255                        }
256                        catch(IOException pIOException) 
257                        {
258                            _sErrorMessage = "File Read Error: " + sFileName;
259                            _thrwError = pIOException;
260                            
261                            throw pIOException;
262                        }
263                        Vector vTheseJavaSourceFiles =
264                               Util.stringToLines(sJavaSourceFileNames);
265                        _measureFiles(vTheseJavaSourceFiles);
266                    }
267                } 
268                else 
269                {
270                    try 
271                    {
272                        _measureSource( sJavaFileName );
273                    } catch( Throwable pThrowable ) 
274                    {
275                        // hmm, do nothing? Use getLastError() or so to check for details.
276                    }
277                }
278            }
279        }
280    
281        /**
282         * If arguments were provided, they are used, otherwise
283         * the input stream is used.
284         */
285        private void _measureRoot(InputStream pInputStream_)
286            throws IOException,
287                   ParseException,
288                   TokenMgrError
289        {
290            _htPackages = new Hashtable();
291            
292            // either there are argument files, or stdin is used
293            if (_vJavaSourceFiles.size() == 0) {
294                DataInputStream disJava = new java.io.DataInputStream(pInputStream_);
295                _measureSource(disJava);
296            } else {
297                // the collection of files get measured
298                _measureFiles(_vJavaSourceFiles);
299            }
300            
301            _vPackageMetrics = new Vector();
302            for(Enumeration ePackages = _htPackages.keys();
303                ePackages.hasMoreElements(); )
304            {
305                String sPackage = (String)ePackages.nextElement();
306                PackageMetric pckmNext = (PackageMetric)_htPackages.
307                       get(sPackage);
308                _vPackageMetrics.addElement(pckmNext);
309            }
310        }
311    
312        public Vector getImports() {
313            return _vImports;
314        }
315    
316        /**
317         * Return info about package statement.
318         * First element has name of package,
319         * then begin of line, etc.
320         */
321        public Object[] getPackage() {
322            return _aoPackage;
323        }
324    
325        /**
326         * The same as getFunctionMetrics?!
327         */
328        public Vector getFunctions() {
329            return _vFunctionMetrics;
330        }
331    
332        public Javancss(Vector vJavaSourceFiles_) {
333            _vJavaSourceFiles = vJavaSourceFiles_;
334            try {
335                _measureRoot(System.in);
336            } catch(Exception e) {
337            } catch(TokenMgrError pError) {
338            }
339        }
340    
341        public Javancss(String sJavaSourceFile_) {
342            _sErrorMessage = null;
343            _vJavaSourceFiles = new Vector();
344            _vJavaSourceFiles.addElement(sJavaSourceFile_);
345            try {
346                _measureRoot(System.in);
347            } catch(Exception e) {
348                    System.out.println( "Javancss.<init>(String).e: " + e );
349            } catch(TokenMgrError pError) {
350                    System.out.println( "Javancss.<init>(String).pError: " + pError );
351            }
352        }
353        
354        //cobertura:  add this next constructor so any input stream can be used.
355        public Javancss(InputStream isJavaSource_) {
356            _sErrorMessage = null;
357            _vJavaSourceFiles = new Vector();
358            try {
359                _measureRoot(isJavaSource_);
360            } catch(Exception e) {
361                    System.out.println( "Javancss.<init>(InputStream).e: " + e );
362            } catch(TokenMgrError pError) {
363                    System.out.println( "Javancss.<init>(InputStream).pError: " + pError );
364            }
365        }
366    
367        /**
368         * Only way to create object that does not immediately
369         * start to parse.
370         */
371        public Javancss() {
372            super();
373    
374            _sErrorMessage = null;
375            _thrwError = null;
376        }
377    
378        public boolean parseImports() {
379            if ( Util.isEmpty( _sJavaSourceFileName ) ) {
380                    System.out.println( "Javancss.parseImports().NO_FILE" );
381    
382                return true;
383            }
384            DataInputStream disSource = createInputStream
385                   ( _sJavaSourceFileName );
386            if ( disSource == null ) {
387                    System.out.println( "Javancss.parseImports().NO_DIS" );
388    
389                return true;
390            }
391    
392            try {
393                _pJavaParser = new JavaParser(disSource);
394                _pJavaParser.ImportUnit();
395                _vImports = _pJavaParser.getImports();
396                _aoPackage = _pJavaParser.getPackageObjects();
397            } catch(ParseException pParseException) {
398                    System.out.println( "Javancss.parseImports().PARSE_EXCEPTION" );
399                if (_sErrorMessage == null) {
400                    _sErrorMessage = "";
401                }
402                _sErrorMessage += "ParseException in STDIN";
403                if (_pJavaParser != null) {
404                    _sErrorMessage += "\nLast useful checkpoint: \"" + _pJavaParser.getLastFunction() + "\"\n";
405                }
406                _sErrorMessage += pParseException.getMessage() + "\n";
407                _thrwError = pParseException;
408    
409                return true;
410            } catch(TokenMgrError pTokenMgrError) {
411                    System.out.println( "Javancss.parseImports().TOKEN_ERROR" );
412                if (_sErrorMessage == null) {
413                    _sErrorMessage = "";
414                }
415                _sErrorMessage += "TokenMgrError in STDIN\n";
416                _sErrorMessage += pTokenMgrError.getMessage() + "\n";
417                _thrwError = pTokenMgrError;
418    
419                return true;
420            }
421    
422            return false;
423        }
424    
425        public void setSourceFile( String sJavaSourceFile_ ) {
426            _sJavaSourceFileName = sJavaSourceFile_;
427            _vJavaSourceFiles = new Vector();
428            _vJavaSourceFiles.addElement(sJavaSourceFile_);
429        }
430    
431        public int getNcss() {
432            return _ncss;
433        }
434    
435        public int getLOC() {
436            return _loc;
437        }
438    
439        // added by SMS
440        public int getJvdc() {
441            return _pJavaParser.getJvdc();
442        }
443    
444        /**
445         * JDCL stands for javadoc coment lines (while jvdc stands
446         * for number of javadoc comments).
447         */
448        public int getJdcl() {
449            return JavaParserTokenManager._iFormalComments;
450        }
451        
452        public int getSl() {
453            return JavaParserTokenManager._iSingleComments;
454        }
455        
456        public int getMl() {
457            return JavaParserTokenManager._iMultiComments;
458        }
459        //
460    
461        public Vector getFunctionMetrics() {
462            return(_vFunctionMetrics);
463        }
464    
465        public Vector getObjectMetrics() {
466            return(_vObjectMetrics);
467        }
468    
469        /**
470         * Returns list of packages in the form
471         * PackageMetric objects.
472         */
473        public Vector getPackageMetrics() {
474            return(_vPackageMetrics);
475        }
476    
477        public String getLastErrorMessage() {
478            if (_sErrorMessage == null) {
479                return null;
480            }
481            return(new String(_sErrorMessage));
482        }
483    
484        public Throwable getLastError() {
485            return _thrwError;
486        }
487    
488    }