1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19 package net.sf.jameleon.ant;
20
21 import java.io.File;
22 import java.io.FileOutputStream;
23 import java.io.IOException;
24 import java.util.Iterator;
25 import java.util.List;
26 import java.util.Properties;
27
28 import net.sf.jameleon.bean.FunctionalPoint;
29 import net.sf.jameleon.bean.Javadoc2Bean;
30 import net.sf.jameleon.exception.JameleonException;
31 import net.sf.jameleon.util.InstanceSerializer;
32
33 import org.apache.tools.ant.BuildException;
34
35 import com.thoughtworks.qdox.ant.AbstractQdoxTask;
36 import com.thoughtworks.qdox.model.JavaClass;
37
38 /***
39 * An Ant task that registers all functional points (defined in <code>isA</code>, which defaults to
40 * <code>FunctionTag</code>) so they can be recognized by the Jameleon engine.
41 */
42 public class JameleonXDoclet extends AbstractQdoxTask{
43
44 protected File outputDir = null;
45 protected String outputFileName = new String("TestCaseTagDefs.properties");
46 protected String isA = "net.sf.jameleon.function.FunctionTag";
47 protected boolean quiet = false;
48 protected Javadoc2Bean j2b;
49
50 /***
51 * Set the directory where TestCaseTagDefs.properties will be generated to
52 * @param outputDir - The directory where TestCaseTagDefs.properties will be generated to.
53 */
54 public void setOutputDir(File outputDir){
55 this.outputDir = outputDir;
56 }
57
58 /***
59 * Restricts the javadocs to be extracted to all classes that implement this class
60 * @param isA - The class to restrict by.
61 */
62 public void setIsA(String isA){
63 this.isA = isA;
64 }
65
66 /***
67 * Sets the messages to be printed to stdout to true/false
68 * @param quiet - Set to true to be quite. Defaults to false
69 */
70 public void setQuiet(boolean quiet){
71 this.quiet = quiet;
72 }
73
74 /***
75 * Set the file where the function points will be registered to. The default is TestCaseTagDefs.properties if this is not set
76 * @param fileName - The name of the file where the information will be sent to. This does not include the directory as that is set
77 * via the outputDir property.
78 */
79 public void setOutputFileName(String fileName){
80 outputFileName = fileName;
81 }
82
83 /***
84 * Implement this method and play with _xJavaDoc
85 *
86 * @exception BuildException Ant's way of reporting exception
87 */
88 public void execute() throws BuildException{
89 if (outputDir == null) {
90 throw new BuildException("outputDir must be set to the directory where TestCaseTagDefs.properties will be generated to.");
91 }else if (!outputDir.isDirectory()) {
92 throw new BuildException("outputDir is not a valid directory! Set outputDir to the directory where TestCaseTagDefs.properties will be generated to.");
93 }
94 try{
95 validateAttributes();
96 buildFileMap();
97 j2b = new Javadoc2Bean();
98 j2b.setIsA(isA);
99 mergeBuilderSources(j2b);
100 Properties props = new Properties();
101 JavaClass[] classes = j2b.getJavaDocBuilder().getClasses();
102 for (int i = 0; i < classes.length; i++) {
103 setClassAttributes(classes[i],props,j2b);
104 }
105 saveTagDefs(props, getTagDefsFileName());
106 }catch(RuntimeException re){
107 re.printStackTrace();
108 }
109 }
110
111 protected void mergeBuilderSources(Javadoc2Bean j2b) {
112 for (Iterator iterator = fileMap.keySet().iterator(); iterator.hasNext();) {
113 String sourceFile = (String) iterator.next();
114 j2b.getJavaDocBuilder().addSourceTree((File) fileMap.get(sourceFile));
115 }
116 }
117
118 /***
119 * Simply constructs a file from a String that represents the fully qualified class
120 * name. The path to the file will be prefixed by the <code>outputDir</code>.
121 * @param className - a String representing the name and partial (package) location of the file.
122 * @return A file that starts with <code>outputDir</code> and is followed by the directory
123 * structure (package) of the <code>className</code>
124 */
125 protected File constructFileFromClassName(String className){
126 String sourceName = new String(className);
127 if (sourceName.endsWith(".class")) {
128 sourceName = sourceName.substring(0,sourceName.indexOf(".class"));
129 }
130 sourceName = sourceName.replace('.',File.separatorChar);
131 sourceName += InstanceSerializer.SERIALIZED_EXT;
132 return new File(outputDir, sourceName);
133 }
134
135 /***
136 * @return the complete path to the output file for the tag definitions of functional points
137 */
138 protected String getTagDefsFileName(){
139 String fileName = outputDir.getPath();
140 if (fileName.lastIndexOf(File.separator) < (fileName.length() -1) ) {
141 fileName += File.separator;
142 }
143 fileName += outputFileName;
144 return fileName;
145 }
146
147 /***
148 * Saves the function point names and their corresponding class names to a file
149 * @param props - The properties to save to a file.
150 */
151 protected void saveTagDefs(Properties props, String fileName) throws BuildException{
152 FileOutputStream fos = null;
153 try{
154 fos = new FileOutputStream(fileName);
155 props.store(fos,"Function Tag Definitions generated by Jameleon.");
156 }catch(IOException ioe){
157 throw new BuildException("Could not write to file "+fileName,ioe);
158 }finally{
159 try{
160 if (fos != null){
161 fos.close();
162 }
163 }catch(IOException ioe){
164 ioe.printStackTrace();
165
166 }
167 }
168 }
169
170 /***
171 * Set the class-specific attributes
172 */
173 protected void setClassAttributes(JavaClass clazz, Properties props, Javadoc2Bean j2b) throws BuildException{
174 String className = clazz.getFullyQualifiedName();
175 FunctionalPoint fp = null;
176 try{
177 fp = j2b.getFunctionalPoint(clazz);
178 if (fp != null){
179
180 fp.getType();
181 }
182 } catch (JameleonException iae){
183 throw new BuildException(className + ": "+iae.getMessage());
184 }
185 String warningMsg = "WARNING: "+className+" is a valid function point, but it will NOT be automatically registered! "+
186 "Please add a @jameleon.function name=\"functionName\" to the class javadocs of the function point";
187 if (fp != null) {
188 List tagNames = fp.getTagNames();
189 Iterator it = tagNames.iterator();
190 while (it.hasNext()) {
191 String tagName = (String)it.next();
192 if ( tagName != null && tagName.length() > 0 && props.getProperty(tagName) == null) {
193 props.setProperty(tagName,className);
194 serializeFunctionalPoint(fp, className);
195 }else if(tagName != null && props.getProperty(tagName) != null){
196 throw new BuildException(className +" and "+ props.getProperty(tagName) +" both have the same function point name registered! "+
197 "Change the @jameleon.function name=\"\" attribute of one of these classes to something unique and try again.");
198 }else{
199 if (!quiet) {
200 System.out.println(warningMsg);
201 }
202 }
203 }
204 if (tagNames.size() == 0) {
205 serializeFunctionalPoint(fp, className);
206 if (!quiet) {
207 System.out.println(warningMsg);
208 }
209 }
210 }
211
212 }
213
214 protected void serializeFunctionalPoint(FunctionalPoint fp, String className) throws BuildException{
215 try{
216 InstanceSerializer.serialize(fp,constructFileFromClassName(className));
217 }catch(IOException ioe){
218 throw new BuildException(ioe.toString());
219 }
220 }
221
222 }