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.File;
32 import java.io.FileNotFoundException;
33 import java.util.ArrayList;
34 import java.util.List;
35 import org.apache.tools.ant.BuildException;
36 import org.apache.tools.ant.DirectoryScanner;
37 import org.apache.tools.ant.taskdefs.AntlibDefinition;
38 import org.apache.tools.ant.types.FileSet;
39 import cpptools.AntLogger;
40 import cpptools.Options;
41
42 /**
43 * Provides an Apache Ant task implementation for CppNcss.
44 *
45 * @author Mathieu Champlon
46 */
47 public final class CppNcssTask extends AntlibDefinition
48 {
49 private final List<FileSet> filesets = new ArrayList<FileSet>();
50 private final List<Argument> arguments = new ArrayList<Argument>();
51 private boolean keepGoing = false;
52 private String filename;
53 private String prefix;
54 private String measurements;
55 private int samples = -1;
56
57 /**
58 * Add a set of source files.
59 *
60 * @param fileset a set of source files.
61 */
62 public void addFileset( final FileSet fileset )
63 {
64 filesets.add( fileset );
65 }
66
67 /**
68 * Define a prefix.
69 * <p>
70 * Not required.
71 *
72 * @param prefix the prefix
73 */
74 public void setPrefix( final String prefix )
75 {
76 this.prefix = format( prefix );
77 }
78
79 /**
80 * Set the name of the output file.
81 * <p>
82 * Required.
83 *
84 * @param filename the file name
85 */
86 public void setToFile( final String filename )
87 {
88 this.filename = filename;
89 }
90
91 /**
92 * Set whether the analyzis should stop upon error or not.
93 * <p>
94 * Not required. Default is false.
95 *
96 * @param keepGoing if the analyzis should keep going upon error
97 */
98 public void setKeepGoing( final boolean keepGoing )
99 {
100 this.keepGoing = keepGoing;
101 }
102
103 /**
104 * Set the number of samples to output.
105 * <p>
106 * Not required. Default is all.
107 *
108 * @param samples truncate the output after this given number of samples
109 */
110 public void setSamples( final int samples )
111 {
112 if( samples < 0 )
113 throw new BuildException( "Parameter 'samples' must be positive" );
114 this.samples = samples;
115 }
116
117 /**
118 * Set the measurements.
119 * <p>
120 * Not required. Default is "NCSS,CCN,function".
121 *
122 * @param measurements the ordered list of measurements to perform
123 */
124 public void setMeasurements( final String measurements )
125 {
126 this.measurements = measurements;
127 }
128
129 private String format( final String path )
130 {
131 String result = path.replace( '/', File.separatorChar ).replace( '\\', File.separatorChar );
132 if( result.charAt( result.length() - 1 ) != File.separatorChar )
133 result += File.separatorChar;
134 return result;
135 }
136
137 /**
138 * Add a define definition.
139 *
140 * @param define the define
141 */
142 public void addConfiguredDefine( final Define define )
143 {
144 if( define.getName() == null )
145 throw new BuildException( "Missing required 'name' for define" );
146 arguments.add( define );
147 }
148
149 /**
150 * Add a macro definition.
151 *
152 * @param macro the macro
153 */
154 public void addConfiguredMacro( final Macro macro )
155 {
156 if( macro.getName() == null )
157 throw new BuildException( "Missing required 'name' for macro" );
158 arguments.add( macro );
159 }
160
161 /**
162 * {@inheritDoc}
163 */
164 public void execute()
165 {
166 if( filename == null )
167 throw new BuildException( "Missing 'tofile' attribute to specify output file name" );
168 try
169 {
170 new CppNcss( new Options( buildArguments() ), new AntLogger( this ) ).run();
171 }
172 catch( FileNotFoundException e )
173 {
174 throw new BuildException( e );
175 }
176 }
177
178 private String[] buildArguments()
179 {
180 final List<String> args = new ArrayList<String>();
181 args.add( "-x" );
182 args.add( "-f=" + filename );
183 if( keepGoing )
184 args.add( "-k" );
185 if( prefix != null )
186 args.add( "-p=" + prefix );
187 args.add( "-n=" + samples );
188 if( measurements != null )
189 args.add( "-m=" + measurements );
190 for( Argument argument : arguments )
191 args.add( argument.toArgument() );
192 for( FileSet fileset : filesets )
193 {
194 final DirectoryScanner scanner = fileset.getDirectoryScanner( getProject() );
195 final String directory = format( scanner.getBasedir().toString() );
196 for( String file : scanner.getIncludedFiles() )
197 args.add( directory + file );
198 }
199 return args.toArray( new String[args.size()] );
200 }
201
202 /**
203 * Defines an element convertible to a command line argument.
204 *
205 * @author Mathieu Champlon
206 */
207 private static interface Argument
208 {
209 String toArgument();
210 }
211
212 /**
213 * Provides a symbol definition.
214 *
215 * @author Mathieu Champlon
216 */
217 public static class Symbol implements Argument
218 {
219 private String name;
220 private String value;
221 private final String prefix;
222
223 /**
224 * Create a symbol.
225 *
226 * @param prefix the command line option prefix
227 */
228 protected Symbol( final String prefix )
229 {
230 this.prefix = prefix;
231 }
232
233 /**
234 * Sets the name.
235 * <p>
236 * Required.
237 *
238 * @param name the name
239 */
240 public final void setName( final String name )
241 {
242 this.name = name;
243 }
244
245 /**
246 * Retrieve the name.
247 *
248 * @return the name
249 */
250 public final String getName()
251 {
252 return name;
253 }
254
255 /**
256 * Sets the macro value.
257 * <p>
258 * Required.
259 *
260 * @param value the value
261 */
262 public final void setValue( final String value )
263 {
264 this.value = value;
265 }
266
267 /**
268 * Retrieve the value.
269 *
270 * @return the value
271 */
272 public final String getValue()
273 {
274 return value;
275 }
276
277 /**
278 * Create the corresponding command line argument.
279 *
280 * @return the formatted argument
281 */
282 public final String toArgument()
283 {
284 if( value == null )
285 return "-" + prefix + name;
286 return "-" + prefix + name + '=' + value;
287 }
288 }
289
290 /**
291 * Provides a define definition.
292 *
293 * @author Mathieu Champlon
294 */
295 public static final class Define extends Symbol
296 {
297 /**
298 * Create a define symbol.
299 */
300 public Define()
301 {
302 super( "D" );
303 }
304 }
305
306 /**
307 * Provides a macro definition.
308 *
309 * @author Mathieu Champlon
310 */
311 public static final class Macro extends Symbol
312 {
313 /**
314 * Create a macro symbol.
315 */
316 public Macro()
317 {
318 super( "M" );
319 }
320 }
321 }