1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21 package javancss;
22
23 import java.awt.event.WindowAdapter;
24 import java.awt.event.WindowEvent;
25 import java.io.*;
26 import java.util.ArrayList;
27 import java.util.Collections;
28 import java.util.HashMap;
29 import java.util.HashSet;
30 import java.util.List;
31 import java.util.Map;
32 import java.util.Set;
33 import java.util.logging.Level;
34 import java.util.logging.Logger;
35
36 import org.apache.commons.cli.CommandLine;
37 import org.apache.commons.cli.DefaultParser;
38 import org.apache.commons.cli.HelpFormatter;
39 import org.apache.commons.cli.OptionBuilder;
40 import org.apache.commons.cli.Options;
41 import org.apache.commons.cli.ParseException;
42
43 import javancss.parser.JavaParser;
44 import javancss.parser.JavaParserDebug;
45 import javancss.parser.JavaParserInterface;
46 import javancss.parser.JavaParserTokenManager;
47 import javancss.parser.TokenMgrError;
48
49 import javax.swing.*;
50
51
52
53
54
55
56
57
58
59
60
61
62 public class Javancss
63 {
64 private Logger log = Logger.getLogger( getClass().getName() );
65
66 private static final String DEFAULT_ENCODING = null;
67
68 private boolean exit = false;
69
70 private List<File> _vJavaSourceFiles = null;
71 private String encoding = DEFAULT_ENCODING;
72
73 private String _sErrorMessage = null;
74 private Throwable _thrwError = null;
75
76 private JavaParserInterface _pJavaParser = null;
77 private int _ncss = 0;
78 private int _loc = 0;
79 private List<FunctionMetric> _vFunctionMetrics = new ArrayList<FunctionMetric>();
80 private List<ObjectMetric> _vObjectMetrics = new ArrayList<ObjectMetric>();
81 private List<PackageMetric> _vPackageMetrics = null;
82 private List<Object[]> _vImports = null;
83 private Map<String,PackageMetric> _htPackages = null;
84 private Object[] _aoPackage = null;
85
86
87
88
89 private File _sJavaSourceFile = null;
90
91 private Reader createSourceReader( File sSourceFile_ )
92 {
93 try
94 {
95 return newReader( sSourceFile_ );
96 }
97 catch ( IOException pIOException )
98 {
99 if ( _sErrorMessage == null || _sErrorMessage.trim().length() == 0 )
100 {
101 _sErrorMessage = "";
102 }
103 else
104 {
105 _sErrorMessage += "\n";
106 }
107 _sErrorMessage += "File not found: " + sSourceFile_.getAbsolutePath();
108 _thrwError = pIOException;
109
110 return null;
111 }
112 }
113
114 private void _measureSource( File sSourceFile_ )
115 throws Exception, Error
116 {
117 Reader reader;
118
119
120 try
121 {
122 reader = newReader( sSourceFile_ );
123 }
124 catch ( IOException pIOException )
125 {
126 if ( _sErrorMessage == null || _sErrorMessage.trim().length() == 0 )
127 {
128 _sErrorMessage = "";
129 }
130 else
131 {
132 _sErrorMessage += "\n";
133 }
134 _sErrorMessage += "File not found: " + sSourceFile_.getAbsolutePath();
135 _thrwError = pIOException;
136
137 throw pIOException;
138 }
139
140 String sTempErrorMessage = _sErrorMessage;
141 try
142 {
143
144 _measureSource( reader );
145 }
146 catch ( Exception pParseException )
147 {
148 if ( sTempErrorMessage == null )
149 {
150 sTempErrorMessage = "";
151 }
152 sTempErrorMessage += "ParseException in " + sSourceFile_.getAbsolutePath() +
153 "\nLast useful checkpoint: \"" + _pJavaParser.getLastFunction() + "\"\n";
154 sTempErrorMessage += pParseException.getMessage() + "\n";
155
156 _sErrorMessage = sTempErrorMessage;
157 _thrwError = pParseException;
158
159 throw pParseException;
160 }
161 catch ( Error pTokenMgrError )
162 {
163 if ( sTempErrorMessage == null )
164 {
165 sTempErrorMessage = "";
166 }
167 sTempErrorMessage += "TokenMgrError in " + sSourceFile_.getAbsolutePath() +
168 "\n" + pTokenMgrError.getMessage() + "\n";
169 _sErrorMessage = sTempErrorMessage;
170 _thrwError = pTokenMgrError;
171
172 throw pTokenMgrError;
173 }
174 }
175
176 private void _measureSource( Reader reader )
177 throws Exception, Error
178 {
179 log.fine( "_measureSource(Reader).ENTER" );
180
181 try
182 {
183
184 if ( log.isLoggable( Level.FINE ) )
185 {
186 log.fine( "creating JavaParserDebug" );
187 _pJavaParser = new JavaParserDebug( reader );
188 }
189 else
190 {
191 log.fine( "creating JavaParser" );
192 _pJavaParser = new JavaParser( reader );
193 }
194
195
196 _pJavaParser.parse();
197 log.fine( "Javancss._measureSource(DataInputStream).SUCCESSFULLY_PARSED" );
198
199 _ncss += _pJavaParser.getNcss();
200 _loc += _pJavaParser.getLOC();
201
202 _vFunctionMetrics.addAll( _pJavaParser.getFunction() );
203 _vObjectMetrics.addAll( _pJavaParser.getObject() );
204 Map<String, PackageMetric> htNewPackages = _pJavaParser.getPackage();
205
206
207 for ( Map.Entry<String, PackageMetric> entry : htNewPackages.entrySet() )
208 {
209 String sPackage = entry.getKey();
210
211 PackageMetric pckmNext = htNewPackages.get( sPackage );
212 pckmNext.name = sPackage;
213
214 PackageMetric pckmPrevious = _htPackages.get( sPackage );
215 pckmNext.add( pckmPrevious );
216
217 _htPackages.put( sPackage, pckmNext );
218 }
219 }
220 catch ( Exception pParseException )
221 {
222 if ( _sErrorMessage == null )
223 {
224 _sErrorMessage = "";
225 }
226 _sErrorMessage += "ParseException in STDIN";
227 if ( _pJavaParser != null )
228 {
229 _sErrorMessage += "\nLast useful checkpoint: \"" + _pJavaParser.getLastFunction() + "\"\n";
230 }
231 _sErrorMessage += pParseException.getMessage() + "\n";
232 _thrwError = pParseException;
233
234 throw pParseException;
235 }
236 catch ( Error pTokenMgrError )
237 {
238 if ( _sErrorMessage == null )
239 {
240 _sErrorMessage = "";
241 }
242 _sErrorMessage += "TokenMgrError in STDIN\n";
243 _sErrorMessage += pTokenMgrError.getMessage() + "\n";
244 _thrwError = pTokenMgrError;
245
246 throw pTokenMgrError;
247 }
248 }
249
250 private void _measureFiles( List<File> sourceFiles ) throws TokenMgrError
251 {
252 for ( File file : sourceFiles )
253 {
254 if ( !exit )
255 {
256 try
257 {
258 _measureSource( file );
259 }
260 catch ( Throwable pThrowable )
261 {
262
263
264 }
265 }
266 }
267 }
268
269
270
271
272
273 private void _measureRoot( Reader reader )
274 throws Exception, Error
275 {
276 _htPackages = new HashMap<String, PackageMetric>();
277
278
279 if ( _vJavaSourceFiles == null )
280 {
281 _measureSource( reader );
282 }
283 else
284 {
285
286 _measureFiles( _vJavaSourceFiles );
287 }
288
289 _vPackageMetrics = new ArrayList<PackageMetric>();
290 for ( PackageMetric pkm : _htPackages.values() )
291 {
292 _vPackageMetrics.add( pkm );
293 }
294 Collections.sort( _vPackageMetrics );
295 }
296
297 public List<Object[]> getImports()
298 {
299 return _vImports;
300 }
301
302
303
304
305
306
307 public Object[] getPackage()
308 {
309 return _aoPackage;
310 }
311
312
313
314
315 public List<FunctionMetric> getFunctions()
316 {
317 return _vFunctionMetrics;
318 }
319
320 public void printObjectNcss( Writer w )
321 throws IOException
322 {
323 getFormatter().printObjectNcss( w );
324 }
325
326 public void printFunctionNcss( Writer w )
327 throws IOException
328 {
329 getFormatter().printFunctionNcss( w );
330 }
331
332 public void printPackageNcss( Writer w )
333 throws IOException
334 {
335 getFormatter().printPackageNcss( w );
336 }
337
338 public void printJavaNcss( Writer w )
339 throws IOException
340 {
341 getFormatter().printJavaNcss( w );
342 }
343
344 public void printStart( Writer pw )
345 throws IOException
346 {
347 getFormatter().printStart( pw );
348 }
349
350 public void printEnd( Writer pw )
351 throws IOException
352 {
353 getFormatter().printEnd( pw );
354 }
355
356 public Javancss( List<File> vJavaSourceFiles_ )
357 {
358 this( vJavaSourceFiles_, DEFAULT_ENCODING );
359 }
360
361 public Javancss( List<File> vJavaSourceFiles_, String encoding_ )
362 {
363 setEncoding( encoding_ );
364 _vJavaSourceFiles = vJavaSourceFiles_;
365 _measureRoot();
366 }
367
368 private void _measureRoot()
369 throws Error
370 {
371 try
372 {
373 _measureRoot( newReader( System.in ) );
374 }
375 catch ( Throwable pThrowable )
376 {
377 log.fine( "Javancss._measureRoot().e: " + pThrowable );
378 pThrowable.printStackTrace(System.err);
379 }
380 }
381
382 public Javancss( File sJavaSourceFile_ )
383 {
384 this( sJavaSourceFile_, DEFAULT_ENCODING );
385 }
386
387 public Javancss( File sJavaSourceFile_, String encoding_ )
388 {
389 log.fine( "Javancss.<init>(String).sJavaSourceFile_: " + sJavaSourceFile_ );
390 setEncoding( encoding_ );
391 _sErrorMessage = null;
392 _vJavaSourceFiles = new ArrayList<File>();
393 _vJavaSourceFiles.add( sJavaSourceFile_ );
394 _measureRoot();
395 }
396
397
398
399
400
401 public Javancss()
402 {
403 _sErrorMessage = null;
404 _thrwError = null;
405 }
406
407 public boolean parseImports()
408 {
409 if ( _sJavaSourceFile == null )
410 {
411 log.fine( "Javancss.parseImports().NO_FILE" );
412
413 return true;
414 }
415 Reader reader = createSourceReader( _sJavaSourceFile );
416 if ( reader == null )
417 {
418 log.fine( "Javancss.parseImports().NO_DIS" );
419
420 return true;
421 }
422
423 try
424 {
425 log.fine( "Javancss.parseImports().START_PARSING" );
426 if ( !log.isLoggable( Level.FINE ) )
427 {
428 _pJavaParser = new JavaParser( reader );
429 }
430 else
431 {
432 _pJavaParser = new JavaParserDebug( reader );
433 }
434 _pJavaParser.parseImportUnit();
435 _vImports = _pJavaParser.getImports();
436 _aoPackage = _pJavaParser.getPackageObjects();
437 log.fine( "Javancss.parseImports().END_PARSING" );
438 }
439 catch ( Exception pParseException )
440 {
441 log.fine( "Javancss.parseImports().PARSE_EXCEPTION" );
442 if ( _sErrorMessage == null )
443 {
444 _sErrorMessage = "";
445 }
446 _sErrorMessage += "ParseException in STDIN";
447 if ( _pJavaParser != null )
448 {
449 _sErrorMessage += "\nLast useful checkpoint: \"" + _pJavaParser.getLastFunction() + "\"\n";
450 }
451 _sErrorMessage += pParseException.getMessage() + "\n";
452 _thrwError = pParseException;
453
454 return true;
455 }
456 catch ( Error pTokenMgrError )
457 {
458 log.fine( "Javancss.parseImports().TOKEN_ERROR" );
459 if ( _sErrorMessage == null )
460 {
461 _sErrorMessage = "";
462 }
463 _sErrorMessage += "TokenMgrError in STDIN\n";
464 _sErrorMessage += pTokenMgrError.getMessage() + "\n";
465 _thrwError = pTokenMgrError;
466
467 return true;
468 }
469
470 return false;
471 }
472
473 public void setSourceFile( File javaSourceFile_ )
474 {
475 _sJavaSourceFile = javaSourceFile_;
476 _vJavaSourceFiles = new ArrayList<File>();
477 _vJavaSourceFiles.add( javaSourceFile_ );
478 }
479
480 public Javancss( Reader reader )
481 {
482 this( reader, DEFAULT_ENCODING );
483 }
484
485 public Javancss( Reader reader, String encoding_ )
486 {
487 setEncoding( encoding_ );
488 try
489 {
490 _measureRoot( reader );
491 }
492 catch ( Throwable pThrowable )
493 {
494 log.fine( "Javancss.<init>(Reader).e: " + pThrowable );
495 pThrowable.printStackTrace(System.err);
496 }
497 }
498
499
500
501
502
503
504 private static void _addJavaFiles( File dir, List<File> v )
505 {
506 File[] files = dir.listFiles();
507 if ( files == null || files.length == 0 )
508 {
509 return;
510 }
511
512 for ( File file : files )
513 {
514 if ( file.isDirectory() )
515 {
516
517 _addJavaFiles( file, v );
518 }
519 else
520 {
521 if ( file.getName().endsWith( ".java" ) )
522 {
523 v.add( file );
524 }
525 }
526 }
527 }
528
529 private List<File> findFiles( List<String> filenames, boolean recursive )
530 throws IOException
531 {
532 if ( log.isLoggable( Level.FINE ) )
533 {
534 log.fine( "filenames: " + filenames );
535 }
536 if ( filenames.size() == 0 )
537 {
538 if ( recursive )
539 {
540
541 filenames.add( "." );
542 }
543 else
544 {
545 return null;
546 }
547 }
548
549 Set<String> _processedAtFiles = new HashSet<String>();
550 List<File> newFiles = new ArrayList<File>();
551 for ( String filename : filenames )
552 {
553
554 if ( filename.startsWith( "@" ) )
555 {
556 filename = filename.substring( 1 );
557 if ( filename.length() > 1 )
558 {
559 filename = normalizeFileName( filename );
560 if ( _processedAtFiles.add( filename ) )
561 {
562 String sJavaSourceFileNames = null;
563 try
564 {
565 sJavaSourceFileNames = readFile( filename );
566 }
567 catch( IOException pIOException )
568 {
569 _sErrorMessage = "File Read Error: " + filename;
570 _thrwError = pIOException;
571 throw pIOException;
572 }
573 String[] vTheseJavaSourceFiles = sJavaSourceFileNames.split( "\n" );
574 for ( String name : vTheseJavaSourceFiles )
575 {
576 newFiles.add( new File( name ) );
577 }
578 }
579 }
580 }
581 else
582 {
583 filename = normalizeFileName( filename );
584 File file = new File( filename );
585 if ( file.isDirectory() )
586 {
587 _addJavaFiles( file, newFiles );
588 }
589 else
590 {
591 newFiles.add( file );
592 }
593 }
594 }
595
596 if ( log.isLoggable( Level.FINE ) )
597 {
598 log.fine( "resolved filenames: " + newFiles );
599 }
600
601 return newFiles;
602 }
603
604
605
606
607 @Deprecated
608 public Javancss( String[] args, String sRcsHeader_ ) throws IOException
609 {
610 this( args );
611 }
612
613
614
615
616
617
618
619 public Javancss( String[] args ) throws IOException
620 {
621 Options options = new Options();
622 options.addOption( OptionBuilder.create( "help" ) );
623 options.addOption( OptionBuilder.create( "version" ) );
624 options.addOption( OptionBuilder.create( "debug" ) );
625 options.addOption( OptionBuilder.withDescription( "Counts the program NCSS (default)." ).create( "ncss" ) );
626 options.addOption( OptionBuilder.withDescription( "Assembles a statistic on package level." ).create( "package" ) );
627 options.addOption( OptionBuilder.withDescription( "Counts the object NCSS." ).create( "object" ) );
628 options.addOption( OptionBuilder.withDescription( "Counts the function NCSS." ).create( "function" ) );
629 options.addOption( OptionBuilder.withDescription( "The same as '-function -object -package'." ).create( "all" ) );
630 options.addOption( OptionBuilder.withDescription( "Opens a GUI to present the '-all' output in tabbed panels." ).create( "gui" ) );
631 options.addOption( OptionBuilder.withDescription( "Output in XML format." ).create( "xml" ) );
632 options.addOption( OptionBuilder.withDescription( "Output file name. By default output goes to standard out." ).create( "out" ) );
633 options.addOption( OptionBuilder.withDescription( "Recurse to subdirs." ).create( "recursive" ) );
634 options.addOption( OptionBuilder.withDescription( "Encoding used while reading source files (default: platform encoding)." ).hasArg().create( "encoding" ) );
635
636 CommandLine cl;
637
638 try
639 {
640 cl = new DefaultParser().parse( options, args );
641 }
642 catch ( ParseException e )
643 {
644 System.err.println( "javancss: " + e.getMessage() );
645 System.err.println( "Try `javancss -help' for more information." );
646 return;
647 }
648
649 if ( cl.hasOption( "help" ) )
650 {
651 HelpFormatter helpFormatter = new HelpFormatter();
652 helpFormatter.printHelp( "javancss [options] @srcfiles.txt | *.java | <directory> | <stdin>", options );
653
654 return;
655 }
656
657 if ( cl.hasOption( "version" ) )
658 {
659 System.out.println( "JavaNCSS " + getClass().getPackage().getSpecificationVersion() + " by Chr. Clemens Lee & co" );
660 return;
661 }
662
663 if ( cl.hasOption( "debug" ) )
664 {
665 log.setLevel( Level.FINE );
666 }
667
668 setEncoding( cl.getOptionValue( "encoding" ) );
669 setXML( cl.hasOption( "xml" ) );
670
671
672 _vJavaSourceFiles = findFiles( cl.getArgList(), cl.hasOption( "recursive" ) );
673
674 if ( cl.hasOption( "gui" ) )
675 {
676 try
677 {
678 UIManager.setLookAndFeel(UIManager.getSystemLookAndFeelClassName());
679 }
680 catch ( Exception e )
681 {
682 }
683
684 JavancssFrame pJavancssFrame = new JavancssFrame( cl.getArgList() );
685
686 pJavancssFrame.addWindowListener( new WindowAdapter()
687 {
688 @Override
689 public void windowClosing( WindowEvent event )
690 {
691 setExit();
692 }
693 } );
694 pJavancssFrame.setVisible( true );
695
696 try
697 {
698 _measureRoot( newReader( System.in ) );
699 }
700 catch ( Throwable pThrowable )
701 {
702
703
704 }
705
706 pJavancssFrame.showJavancss( this );
707 pJavancssFrame.setSelectedTab( JavancssFrame.S_PACKAGES );
708
709 return;
710 }
711
712
713 try
714 {
715 _measureRoot( newReader( System.in ) );
716 }
717 catch ( Throwable pThrowable )
718 {
719 log.fine( "Javancss.<init>(String[]).e: " + pThrowable );
720 pThrowable.printStackTrace(System.err);
721 }
722 if ( getLastErrorMessage() != null )
723 {
724 System.err.println( getLastErrorMessage() + "\n" );
725 if ( getNcss() <= 0 )
726 {
727 return;
728 }
729 }
730
731 String sOutputFile = cl.getOptionValue( "out" );
732 OutputStream out = System.out;
733 if ( sOutputFile != null )
734 {
735 try
736 {
737 out = new FileOutputStream( normalizeFileName( sOutputFile ) );
738 }
739 catch ( Exception exception )
740 {
741 System.err.println( "Error opening output file '" + sOutputFile + "': " + exception.getMessage() );
742 sOutputFile = null;
743 }
744 }
745
746 final PrintWriter pw = useXML() ? new PrintWriter( new OutputStreamWriter( out, "UTF-8" ) ) : new PrintWriter( out );
747 try {
748
749 format( pw, cl.hasOption( "package" ), cl.hasOption( "object" ), cl.hasOption( "function" ), cl.hasOption( "all" ) );
750
751 } finally {
752 if ( sOutputFile != null )
753 {
754 pw.close();
755 }
756 else
757 {
758
759 pw.flush();
760 }
761 }
762 }
763
764 private void format( PrintWriter pw, boolean packages, boolean object, boolean function, boolean all )
765 throws IOException
766 {
767 printStart( pw );
768
769 boolean bNoNCSS = false;
770 if ( packages || all )
771 {
772 printPackageNcss( pw );
773 bNoNCSS = true;
774 }
775 if ( object || all )
776 {
777 if ( bNoNCSS )
778 {
779 pw.println();
780 }
781 printObjectNcss( pw );
782 bNoNCSS = true;
783 }
784 if ( function || all )
785 {
786 if ( bNoNCSS )
787 {
788 pw.println();
789 }
790 printFunctionNcss( pw );
791 bNoNCSS = true;
792 }
793 if ( !bNoNCSS )
794 {
795 printJavaNcss( pw );
796 }
797
798 printEnd( pw );
799 }
800
801 public int getNcss()
802 {
803 return _ncss;
804 }
805
806 public int getLOC()
807 {
808 return _loc;
809 }
810
811 public int getJvdc()
812 {
813 return _pJavaParser.getJvdc();
814 }
815
816
817
818
819
820 public int getJdcl()
821 {
822 return JavaParserTokenManager._iFormalComments;
823 }
824
825 public int getSl()
826 {
827 return JavaParserTokenManager._iSingleComments;
828 }
829
830 public int getMl()
831 {
832 return JavaParserTokenManager._iMultiComments;
833 }
834
835 public List<FunctionMetric> getFunctionMetrics()
836 {
837 return _vFunctionMetrics;
838 }
839
840 public List<ObjectMetric> getObjectMetrics()
841 {
842 return _vObjectMetrics;
843 }
844
845
846
847
848
849 public List<PackageMetric> getPackageMetrics()
850 {
851 return _vPackageMetrics;
852 }
853
854 public String getLastErrorMessage()
855 {
856 return _sErrorMessage;
857 }
858
859 public Throwable getLastError()
860 {
861 return _thrwError;
862 }
863
864 public void setExit()
865 {
866 exit = true;
867 }
868
869 private boolean _bXML = false;
870
871 public void setXML( boolean bXML )
872 {
873 _bXML = bXML;
874 }
875
876 public boolean useXML()
877 {
878 return _bXML;
879 }
880
881 public Formatter getFormatter()
882 {
883 if ( useXML() )
884 {
885 return new XmlFormatter( this );
886 }
887
888 return new AsciiFormatter( this );
889 }
890
891 public String getEncoding()
892 {
893 return encoding;
894 }
895
896 public void setEncoding( String encoding )
897 {
898 this.encoding = encoding;
899 }
900
901 private Reader newReader( InputStream stream )
902 throws UnsupportedEncodingException
903 {
904 return ( encoding == null ) ? new InputStreamReader( stream ) : new InputStreamReader( stream, encoding );
905 }
906
907 private Reader newReader( File file )
908 throws FileNotFoundException, UnsupportedEncodingException
909 {
910 return newReader( new FileInputStream( file ) );
911 }
912
913 private String normalizeFileName( String filename )
914 {
915 String userdir = ( String ) System.getProperties().get( "user.dir" );
916
917 filename = filename.trim();
918 if ( filename.length() == 0 || filename.equals( "." ) )
919 {
920 filename = userdir;
921 }
922 else if ( !new File( filename ).isAbsolute() )
923 {
924 filename = new File( userdir, filename ).getPath();
925 }
926
927 try
928 {
929 return new File( filename ).getCanonicalPath();
930 }
931 catch ( IOException e )
932 {
933 return filename;
934 }
935 }
936
937 private String readFile( String filename ) throws IOException
938 {
939 StringBuilder content = new StringBuilder( 100000 );
940
941 BufferedReader in = null;
942 try
943 {
944 in = new BufferedReader( new FileReader( filename ) );
945 String line;
946 while ( ( line = in.readLine() ) != null )
947 {
948 content.append( line ).append( '\n' );
949 }
950 }
951 finally
952 {
953 if ( in != null )
954 {
955 in.close();
956 }
957 }
958
959 return content.toString();
960 }
961 }