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 }