001 /**
002 * Copyright (c) 2009 Timothy Cleaver. All rights reserved.
003 *
004 * Redistribution and use in source and binary forms, with or without
005 * modification, are permitted provided that the following conditions
006 * are met:
007 * 1. Redistributions of source code must retain the above copyright
008 * notice, this list of conditions and the following disclaimer.
009 * 2. Redistributions in binary form must reproduce the above copyright
010 * notice, this list of conditions and the following disclaimer in the
011 * documentation and/or other materials provided with the distribution.
012 * 3. Neither the name of the copyright holders nor the names of its
013 * contributors may be used to endorse or promote products derived from
014 * this software without specific prior written permission.
015 *
016 * THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS "AS IS"
017 * AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE
018 * IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE
019 * ARE DISCLAIMED. IN NO EVENT SHALL THE COPYRIGHT OWNER OR CONTRIBUTORS BE
020 * LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR
021 * CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF
022 * SUBSTITUTE GOODS OR SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS
023 * INTERRUPTION) HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN
024 * CONTRACT, STRICT LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE)
025 * ARISING IN ANY WAY OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF
026 * THE POSSIBILITY OF SUCH DAMAGE.
027 */
028 package com.thoughtworks.paranamer.ant;
029
030 import java.util.Arrays;
031 import java.util.Vector;
032 import java.util.Collection;
033
034 import org.apache.tools.ant.BuildException;
035 import org.apache.tools.ant.Task;
036 import org.apache.tools.ant.DirectoryScanner;
037
038 import org.apache.tools.ant.types.FileSet;
039
040 import com.thoughtworks.qdox.JavaDocBuilder;
041
042 import com.thoughtworks.paranamer.generator.QdoxParanamerGenerator;
043
044 import java.io.File;
045 import java.io.IOException;
046
047 /**
048 * Ant Task to process parameter names. This ant task facilitates the
049 * specification of the java source and class files to process as attributes,
050 * nested file sets or a combination of both. The attributes applicable to
051 * this ant task include a source directory, class directory, include pattern
052 * and exclude pattern. i.e.:
053 * <paranamer srcdir="src" classdir="classes" includes="*.java" excludes="excluded.java"/>
054 * Each of these attributes contains a default:
055 * - srcdir: the base directory of the project
056 * - classdir: srcdir
057 * - includes: "**//*.java" (all java files below the current directory)
058 * this is to be consistent with the default fileset includes.
059 * - excludes: "" (none are excluded)
060 * Thus,
061 * <paranamer srcdir="." classdir="." includes="*.java"/>
062 * <paranamer srcdir="." includes="*.java"/>
063 * <paranamer srcdir="."/>
064 * <paranamer/>
065 * are equivalent.
066 *
067 * Files can be included in nested file sets via:
068 * <paranamer>
069 * <fileset dir="." includes="*.java"/>
070 * </paranamer>
071 * In this case, for each file set the defaults are:
072 * - srcdir: dir
073 * - classdir: srcdir
074 * - includes: the default for ant standard file sets
075 * - excludes: the default for ant standard file sets
076 * No additional fileset generated from the attribute defaults will be
077 * constructed when embedded filesets are used. i.e.
078 * <paranamer>
079 * <fileset dir="directory" includes="*.java"/>
080 * </paranamer>
081 * will not apply paranamer to the java files in ".".
082 *
083 * When mixing file sets and attributes, the classdir attribute is treated
084 * differently. The classdir attribute will be applied to any files included
085 * via srcdir or an embedded file set element. For example:
086 * <paranamer classdir="classes"/>
087 * <fileset dir="." includes="*.java"/>
088 * <fileset dir="directory" includes="*.java"/>
089 * </paranamer>
090 * will require the classes to modify for the first fileset to reside in
091 * the classes directory and those of the second fileset to reside in
092 * classes/directory. Note that the default srcdir is not applied when embedded
093 * filesets are used instead.
094 *
095 * @author Timothy Cleaver
096 */
097 public class ParanamerTask extends Task {
098 /**
099 * The directory that contains the java source from which to extract the
100 * parameter names. By default this is the current directory.
101 */
102 private String srcdir = null;
103
104 /**
105 * Boolean that is true when the srcdir was set as an attribute of the
106 * target, and false otherwise.
107 */
108 private boolean srcdirSet = false;
109
110 /**
111 * The directory that contains the class files to modify. By default
112 * this is the current directory.
113 */
114 private String classdir = srcdir;
115
116 /**
117 * Boolean that is true when the classdir was set as an attribute of the
118 * target, and false otherwise.
119 */
120 private boolean classdirSet = false;
121
122 /**
123 * The pattern used to include java files to be processed within the
124 * specified source directory. By default this is the set of java files
125 * in the current directory and all its sub-directories. This is to
126 * match the default matching semantics of the fileset target.
127 */
128 private String includes = "**/*.java";
129
130 /**
131 * Boolean that is true when the includes was set as an attribute of the
132 * target, and false otherwise.
133 */
134 private boolean includesSet = false;
135
136 /**
137 * The pattern used to exclude java files from processing. By default this
138 * is empty.
139 */
140 private String excludes = "";
141
142 /**
143 * Boolean that is true when the excludes was set as an attribute of the
144 * target, and false otherwise.
145 */
146 private boolean excludesSet = false;
147
148 /**
149 * The collection of nested file sets containing the files to be processed.
150 */
151 private Collection<FileSet> filesets = new Vector<FileSet>();
152
153 /**
154 * Execute the task.
155 */
156 public void execute() throws BuildException {
157 if (srcdir == null) { srcdir = getProject().getBaseDir().getPath(); }
158 if (filesets.isEmpty() || srcdirSet || includesSet || excludesSet) {
159 FileSet set = new FileSet();
160 set.setDir(getProject().resolveFile(srcdir));
161 // ensure whitespace is ignored from around the components of the includes
162 for (Object o : Arrays.asList(includes.split(","))) {
163 set.appendIncludes(new String[]{((String) o).trim()});
164 }
165 // ensure whitespace is ignored from around the components of the excludes
166 for (Object o : Arrays.asList(excludes.split(","))) {
167 set.appendExcludes(new String[]{((String) o).trim()});
168 }
169 filesets.add(set);
170 }
171 for (Object fileset : filesets) {
172 FileSet fs = (FileSet) fileset;
173 DirectoryScanner ds = fs.getDirectoryScanner(getProject());
174 String[] includedFiles = ds.getIncludedFiles();
175 log("Generating parameter names for"
176 + includedFiles.length
177 + " files in "
178 + ds.getBasedir());
179 for (Object o : Arrays.asList(includedFiles)) {
180 String file = (String) o;
181 JavaDocBuilder builder = new JavaDocBuilder();
182 try {
183 builder.addSource(new File(ds.getBasedir(), file));
184 // if the classdir is set then we source the classes
185 // relative to classdir. otherwise we source the classes
186 // relative to the base directory of the file set.
187 if (classdirSet) {
188 makeQdoxParanamerGenerator()
189 .processClasses(builder.getClasses(),
190 classdir);
191 } else {
192 makeQdoxParanamerGenerator()
193 .processClasses(builder.getClasses(),
194 ds.getBasedir().getPath());
195 }
196 } catch (final IOException exception) {
197 throw new BuildException("Error processing: "
198 + file
199 + ". "
200 + exception.getMessage());
201 }
202 }
203 }
204 }
205
206 /**
207 * Called automatically by ant when an embedded fileset element is present.
208 *
209 * @param fileset
210 * the fileset specification
211 */
212 public void addFileset(final FileSet fileset) {
213 this.filesets.add(fileset);
214 }
215
216 /**
217 * Called automatically by ant when the srcdir attribute is present.
218 *
219 * @param srcdir
220 * the content of the srcdir attribute.
221 */
222 public void setSrcdir(final String srcdir) {
223 this.srcdir = srcdir;
224 this.srcdirSet = true;
225 }
226
227 /**
228 * Called automatically by ant when the includes attribute is present.
229 *
230 * @param includes
231 * the content of the includes attribute.
232 */
233 public void setIncludes(final String includes) {
234 this.includes = includes;
235 this.includesSet = true;
236 }
237
238 /**
239 * Called automatically by ant when the excludes attribute is present.
240 *
241 * @param excludes
242 * the content of the excludes attribute.
243 */
244 public void setExcludes(final String excludes) {
245 this.excludes = excludes;
246 this.excludesSet = true;
247 }
248
249 /**
250 * Called automatically by ant when the classdir attribute is present.
251 *
252 * @param classdir
253 * the content of the classdir attribute.
254 */
255 public void setClassdir(final String classdir) {
256 this.classdir = classdir;
257 this.classdirSet = true;
258 }
259
260 /**
261 * Provide this as a method so that it can be overridden and custom
262 * QdoxParanamerGenerators can be returned in place of the default.
263 * This is used for testing purposes.
264 *
265 * @return
266 * the qdox paranamer generated instance to use to generate the
267 * paranamer data.
268 */
269 protected QdoxParanamerGenerator makeQdoxParanamerGenerator() {
270 return new QdoxParanamerGenerator();
271 }
272 }