1 /**
2 * Redistribution and use in source and binary forms, with or without
3 * modification, are permitted provided that the following conditions are
4 * met :
5 *
6 * . Redistributions of source code must retain the above copyright
7 * notice, this list of conditions and the following disclaimer.
8 *
9 * . Redistributions in binary form must reproduce the above copyright
10 * notice, this list of conditions and the following disclaimer in the
11 * documentation and/or other materials provided with the distribution.
12 *
13 * . The name of the author may not be used to endorse or promote products
14 * derived from this software without specific prior written permission.
15 *
16 * THIS SOFTWARE IS PROVIDED BY THE AUTHOR ``AS IS'' AND ANY EXPRESS OR
17 * IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE IMPLIED
18 * WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE ARE
19 * DISCLAIMED. IN NO EVENT SHALL THE AUTHOR BE LIABLE FOR ANY DIRECT,
20 * INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES
21 * (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR
22 * SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION)
23 * HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT,
24 * STRICT LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN
25 * ANY WAY OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE
26 * POSSIBILITY OF SUCH DAMAGE.
27 */
28
29 package cppncss;
30
31 import java.io.FileNotFoundException;
32 import java.io.FileOutputStream;
33 import java.io.PrintStream;
34 import java.util.ArrayList;
35 import java.util.List;
36 import cppast.VisitorComposite;
37 import cppncss.counter.CcnCounter;
38 import cppncss.counter.Counter;
39 import cppncss.counter.FileVisitor;
40 import cppncss.counter.FunctionCounter;
41 import cppncss.counter.FunctionVisitor;
42 import cppncss.counter.NcssCounter;
43 import cppncss.measure.AverageCollector;
44 import cppncss.measure.Collector;
45 import cppncss.measure.MeasureCollector;
46 import cppncss.measure.SumCollector;
47 import cpptools.Analyzer;
48 import cpptools.ConsoleLogger;
49 import cpptools.EventHandler;
50 import cpptools.FileObserverComposite;
51 import cpptools.Options;
52 import cpptools.Usage;
53
54 /**
55 * Provides code measures for C++.
56 *
57 * @author Mathieu Champlon
58 */
59 public final class CppNcss
60 {
61 private final VisitorComposite visitors = new VisitorComposite();
62 private final FileObserverComposite observers = new FileObserverComposite();
63 private final List<Collector> collectors = new ArrayList<Collector>();
64 private final ResultOutput output;
65 private final Analyzer analyzer;
66
67 /**
68 * Implements a factory to create visitors from a given counter.
69 *
70 * @author Mathieu Champlon
71 */
72 private interface VisitorFactory
73 {
74 void register( Counter counter );
75 }
76
77 private final VisitorFactory functionVisitorFactory = new VisitorFactory()
78 {
79 public void register( final Counter counter )
80 {
81 visitors.register( new FunctionVisitor( counter ) );
82 }
83 };
84 private final VisitorFactory fileVisitorFactory = new VisitorFactory()
85 {
86 public void register( final Counter counter )
87 {
88 final FileVisitor visitor = new FileVisitor( counter );
89 observers.register( visitor );
90 visitors.register( visitor );
91 }
92 };
93
94 /**
95 * Create a CppNcss instance.
96 *
97 * @param options the options
98 * @param handler the log handler
99 * @throws FileNotFoundException when the log file fails
100 */
101 public CppNcss( final Options options, final EventHandler handler ) throws FileNotFoundException
102 {
103 output = createOutput( options );
104 analyzer = new Analyzer( options, visitors, observers, handler );
105 register( options, new MeasureCollector( options, new ResultOutputAdapter( "Function", output ) ), functionVisitorFactory );
106 register( options, new AverageCollector( new ResultOutputAdapter( "Function", output ) ), functionVisitorFactory );
107 register( options, new MeasureCollector( options, new ResultOutputAdapter( "File", output ) ), fileVisitorFactory );
108 register( options, new AverageCollector( new ResultOutputAdapter( "File", output ) ), fileVisitorFactory );
109 register( options, new SumCollector( new ResultOutputAdapter( "Project", output ) ), fileVisitorFactory );
110 }
111
112 private void register( final Options options, final Collector collector, final VisitorFactory factory )
113 {
114 collectors.add( collector );
115 observers.register( collector );
116 for( String counter : filter( options ) )
117 factory.register( create( collector, counter ) );
118 }
119
120 private List<String> filter( final Options options )
121 {
122 final List<String> counters = new ArrayList<String>();
123 for( String value : extract( options ).split( "," ) )
124 if( !counters.contains( value ) )
125 counters.add( value );
126 return counters;
127 }
128
129 private String extract( final Options options )
130 {
131 final List<String> values = options.getOptionPropertyValues( "m" );
132 if( values.isEmpty() )
133 return "NCSS,CCN,function";
134 return values.get( 0 );
135 }
136
137 private Counter create( final Collector collector, final String counter )
138 {
139 if( counter.equals( "NCSS" ) )
140 return new NcssCounter( collector );
141 if( counter.equals( "CCN" ) )
142 return new CcnCounter( collector );
143 if( counter.equals( "function" ) )
144 return new FunctionCounter( collector );
145 throw new IllegalArgumentException( "invalid measurement '" + counter + "'" );
146 }
147
148 /**
149 * Run the analyzis.
150 */
151 public void run()
152 {
153 analyzer.run();
154 for( Collector collector : collectors )
155 collector.flush();
156 output.flush();
157 }
158
159 private ResultOutput createOutput( final Options options ) throws FileNotFoundException
160 {
161 final PrintStream stream = createStream( options );
162 if( !options.hasOption( "x" ) )
163 return new AsciiResultOutput( stream );
164 return new XmlResultOutput( stream );
165 }
166
167 private PrintStream createStream( final Options options ) throws FileNotFoundException
168 {
169 if( options.hasOption( "f" ) )
170 return new PrintStream( new FileOutputStream( options.getOptionPropertyValues( "f" ).get( 0 ) ) );
171 return System.out;
172 }
173
174 /**
175 * Run the application.
176 *
177 * @param args the arguments
178 * @throws FileNotFoundException when the log file fails
179 */
180 public static void main( final String[] args ) throws FileNotFoundException
181 {
182 if( !check( args ) )
183 return;
184 final Options options = new Options( args );
185 new CppNcss( options, new ConsoleLogger( options ) ).run();
186 }
187
188 private static boolean check( final String[] args )
189 {
190 if( args.length > 0 && !args[0].equals( "-h" ) )
191 return true;
192 usage();
193 return false;
194 }
195
196 private static void usage()
197 {
198 final Usage usage = new Usage( "cppncss", "http://cppncss.sourceforge.net", "1.0.3" );
199 usage.addOption( "h", "print this message" );
200 usage.addOption( "d", "print debugging information" );
201 usage.addOption( "v", "be extra verbose" );
202 usage.addOption( "k", "keep going on parsing errors" );
203 usage.addOption( "r", "process directories recursively" );
204 usage.addOption( "x", "output result as xml" );
205 usage.addOption( "m=<measurements>", "output the <measurements> sorted in given order, default is equivalent to -m=NCSS,CCN,function" );
206 usage.addOption( "n=<number>", "output only the top <number> results" );
207 usage.addOption( "f=<file>", "output result to <file>" );
208 usage.addOption( "D<symbol>[=[<value>]]", "replace define <symbol> with <value>" );
209 usage.addOption( "M<symbol>[=[<value>]]", "replace macro <symbol> with <value>" );
210 usage.addOption( "p=<path>", "remove <path> prefix when displaying file names" );
211 usage.display();
212 }
213 }