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 }