1. /*
  2. * Copyright 2003-2004 The Apache Software Foundation
  3. *
  4. * Licensed under the Apache License, Version 2.0 (the "License");
  5. * you may not use this file except in compliance with the License.
  6. * You may obtain a copy of the License at
  7. *
  8. * http://www.apache.org/licenses/LICENSE-2.0
  9. *
  10. * Unless required by applicable law or agreed to in writing, software
  11. * distributed under the License is distributed on an "AS IS" BASIS,
  12. * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
  13. * See the License for the specific language governing permissions and
  14. * limitations under the License.
  15. */
  16. package org.apache.commons.attributes.compiler;
  17. import java.io.BufferedReader;
  18. import java.io.File;
  19. import java.io.FileReader;
  20. import java.io.FileWriter;
  21. import java.io.PrintWriter;
  22. import java.util.ArrayList;
  23. import java.util.Collection;
  24. import java.util.HashSet;
  25. import java.util.Iterator;
  26. import java.util.StringTokenizer;
  27. import org.apache.tools.ant.BuildException;
  28. import org.apache.tools.ant.Project;
  29. import org.apache.tools.ant.Task;
  30. import org.apache.tools.ant.types.FileSet;
  31. import org.apache.tools.ant.types.Path;
  32. import xjavadoc.SourceClass;
  33. import xjavadoc.XClass;
  34. import xjavadoc.XConstructor;
  35. import xjavadoc.XJavaDoc;
  36. import xjavadoc.XField;
  37. import xjavadoc.XMethod;
  38. import xjavadoc.XParameter;
  39. import xjavadoc.XProgramElement;
  40. import xjavadoc.XTag;
  41. import xjavadoc.ant.XJavadocTask;
  42. import xjavadoc.filesystem.AbstractFile;
  43. /**
  44. * Ant task to compile attributes. Usage:
  45. *
  46. * <pre><code>
  47. * <taskdef resource="org/apache/commons/attributes/anttasks.properties"/>
  48. *
  49. * <attribute-compiler destDir="temp/"> attributepackages="my.attributes;my.otherattributes"
  50. * <fileset dir="src/" includes="*.java"/>
  51. * </attribute-compiler>
  52. * </code></pre>
  53. *
  54. * <ul>
  55. * <li>destDir<dd>Destination directory for generated source files
  56. * <li>attributepackages<dd>A set of package names that will be automatically searched for attributes.
  57. * </ul>
  58. *
  59. * The task should be run before compiling the Java sources, and will produce some
  60. * additional Java source files in the destination directory that should be compiled
  61. * along with the input source files. (See the overview for a diagram.)
  62. */
  63. public class AttributeCompiler extends XJavadocTask {
  64. private final ArrayList fileSets = new ArrayList ();
  65. private Path src;
  66. private File destDir;
  67. private int numGenerated;
  68. private int numIgnored;
  69. private String attributePackages = "";
  70. public AttributeCompiler () {
  71. }
  72. public void setAttributePackages (String attributePackages) {
  73. this.attributePackages = attributePackages;
  74. }
  75. public void addFileset (FileSet set) {
  76. super.addFileset (set);
  77. fileSets.add (set);
  78. }
  79. public void setDestdir (File destDir) {
  80. this.destDir = destDir;
  81. }
  82. public void setSourcepathref (String pathref) {
  83. String sourcePaths = project.getReference (pathref).toString ();
  84. StringTokenizer tok = new StringTokenizer (sourcePaths, File.pathSeparator);
  85. while (tok.hasMoreTokens ()) {
  86. FileSet fs = new FileSet ();
  87. fs.setDir (new File (tok.nextToken ()));
  88. fs.setIncludes ("**/*.java");
  89. fs.setProject (project);
  90. addFileset (fs);
  91. }
  92. }
  93. protected void copyImports (File source, PrintWriter dest) throws Exception {
  94. BufferedReader br = new BufferedReader (new FileReader (source));
  95. try {
  96. String line = null;
  97. while ((line = br.readLine ()) != null) {
  98. if (line.startsWith ("import ")) {
  99. dest.println (line);
  100. }
  101. }
  102. } finally {
  103. br.close ();
  104. }
  105. }
  106. protected void addExpressions (Collection tags, PrintWriter pw, String collectionName, File sourceFile) {
  107. addExpressions (tags, null, pw, collectionName, sourceFile);
  108. }
  109. protected void addExpressions (Collection tags, String selector, PrintWriter pw, String collectionName, File sourceFile) {
  110. String fileName = sourceFile != null ? sourceFile.getPath ().replace ('\\', '/') : "<unknown>";
  111. Iterator iter = tags.iterator ();
  112. while (iter.hasNext ()) {
  113. XTag tag = (XTag) iter.next ();
  114. if (isAttribute (tag)) {
  115. String expression = tag.getName () + " " + tag.getValue ();
  116. expression = expression.trim ();
  117. // Remove the second @-sign.
  118. expression = expression.substring (1);
  119. if (selector != null) {
  120. if (expression.startsWith (".")) {
  121. // We have selector, tag does...
  122. String tagSelector = expression.substring (1, expression.indexOf (" "));
  123. expression = expression.substring (expression.indexOf (" ")).trim ();
  124. if (!selector.equals (tagSelector)) {
  125. // ...but they didn't match.
  126. continue;
  127. }
  128. } else {
  129. // We have selector, but tag doesn't
  130. continue;
  131. }
  132. } else {
  133. // No selector, but tag has selector.
  134. if (expression.startsWith (".")) {
  135. continue;
  136. }
  137. }
  138. pw.println (" {");
  139. outputAttributeExpression (pw, expression, fileName, tag.getLineNumber (), "_attr");
  140. pw.println (" Object _oattr = _attr; // Need to erase type information");
  141. pw.println (" if (_oattr instanceof org.apache.commons.attributes.Sealable) {");
  142. pw.println (" ((org.apache.commons.attributes.Sealable) _oattr).seal ();");
  143. pw.println (" }");
  144. pw.println (" " + collectionName + ".add ( _attr );");
  145. pw.println (" }");
  146. }
  147. }
  148. }
  149. protected void outputAttributeExpression (PrintWriter pw, String expression, String filename, int line, String tempVariableName) {
  150. AttributeExpressionParser.ParseResult result = AttributeExpressionParser.parse (expression, filename, line);
  151. pw.print (" " + result.className + " " + tempVariableName + " = new " + result.className + "(");
  152. boolean first = true;
  153. Iterator iter = result.arguments.iterator ();
  154. while (iter.hasNext ()) {
  155. AttributeExpressionParser.Argument arg = (AttributeExpressionParser.Argument) iter.next ();
  156. if (arg.field == null) {
  157. if (!first) {
  158. pw.print (", ");
  159. }
  160. first = false;
  161. pw.print (arg.text);
  162. }
  163. }
  164. pw.println (" // " + filename + ":" + line);
  165. pw.println (");");
  166. iter = result.arguments.iterator ();
  167. while (iter.hasNext ()) {
  168. AttributeExpressionParser.Argument arg = (AttributeExpressionParser.Argument) iter.next ();
  169. if (arg.field != null) {
  170. String methodName = "set" + arg.field.substring (0, 1).toUpperCase () + arg.field.substring (1);
  171. pw.println (" " + tempVariableName + "." + methodName + "(\n" +
  172. arg.text + " // " + filename + ":" + line + "\n" +
  173. ");");
  174. }
  175. }
  176. }
  177. protected boolean elementHasAttributes (Collection xElements) {
  178. Iterator iter = xElements.iterator ();
  179. while (iter.hasNext ()) {
  180. XProgramElement element = (XProgramElement) iter.next ();
  181. if (tagHasAttributes (element.getDoc ().getTags ())) {
  182. return true;
  183. }
  184. }
  185. return false;
  186. }
  187. /**
  188. * Encodes a class name to the internal Java name.
  189. * For example, an inner class Outer.Inner will be
  190. * encoed as Outer$Inner.
  191. */
  192. private void getTransformedQualifiedName (XClass type, StringBuffer sb) {
  193. if (type.isInner ()) {
  194. String packageName = type.getContainingPackage ().getName ();
  195. sb.append (packageName);
  196. if (packageName.length () > 0) {
  197. sb.append (".");
  198. }
  199. sb.append (type.getName ().replace ('.','$'));
  200. } else {
  201. sb.append (type.getQualifiedName ());
  202. }
  203. }
  204. protected String getParameterTypes (Collection parameters) {
  205. StringBuffer sb = new StringBuffer ();
  206. for (Iterator params = parameters.iterator (); params.hasNext ();) {
  207. XParameter parameter = (XParameter) params.next ();
  208. getTransformedQualifiedName (parameter.getType (), sb);
  209. sb.append (parameter.getDimensionAsString ());
  210. if (params.hasNext ()) {
  211. sb.append (",");
  212. }
  213. }
  214. return sb.toString ();
  215. }
  216. protected void generateClass (XClass xClass) throws Exception {
  217. String name = null;
  218. File sourceFile = null;
  219. File destFile = null;
  220. String packageName = null;
  221. String className = null;
  222. packageName = xClass.getContainingPackage().getName ();
  223. if (xClass.isInner ()) {
  224. name = xClass.getQualifiedName ().substring (packageName.length ());
  225. sourceFile = getSourceFile (xClass);
  226. className = xClass.getName ().replace ('.', '$');
  227. name = packageName + (packageName.length () > 0 ? "." : "") + className;
  228. } else {
  229. name = xClass.getQualifiedName ();
  230. sourceFile = getSourceFile (xClass);
  231. className = xClass.getName ();
  232. }
  233. if (sourceFile == null) {
  234. log ("Unable to find source file for: " + name);
  235. }
  236. destFile = new File (destDir, name.replace ('.', '/') + "$__attributeRepository.java");
  237. if (xClass.isAnonymous ()) {
  238. log (xClass.getName () + " is anonymous - ignoring.", Project.MSG_VERBOSE);
  239. numIgnored++;
  240. return;
  241. }
  242. if (!hasAttributes (xClass)) {
  243. if (destFile.exists ()) {
  244. destFile.delete ();
  245. }
  246. return;
  247. }
  248. if (destFile.exists () && sourceFile != null && destFile.lastModified () >= sourceFile.lastModified ()) {
  249. return;
  250. }
  251. numGenerated++;
  252. destFile.getParentFile ().mkdirs ();
  253. PrintWriter pw = new PrintWriter (new FileWriter (destFile));
  254. try {
  255. if (packageName != null && !packageName.equals ("")) {
  256. pw.println ("package " + packageName + ";");
  257. }
  258. if (sourceFile != null) {
  259. copyImports (sourceFile, pw);
  260. }
  261. StringTokenizer tok = new StringTokenizer (attributePackages, ";");
  262. while (tok.hasMoreTokens ()) {
  263. pw.println ("import " + tok.nextToken () + ".*;");
  264. }
  265. pw.println ("public class " + className + "$__attributeRepository implements org.apache.commons.attributes.AttributeRepositoryClass {");
  266. {
  267. pw.println (" private final java.util.Set classAttributes = new java.util.HashSet ();");
  268. pw.println (" private final java.util.Map fieldAttributes = new java.util.HashMap ();");
  269. pw.println (" private final java.util.Map methodAttributes = new java.util.HashMap ();");
  270. pw.println (" private final java.util.Map constructorAttributes = new java.util.HashMap ();");
  271. pw.println ();
  272. pw.println (" public " + className + "$__attributeRepository " + "() {");
  273. pw.println (" initClassAttributes ();");
  274. pw.println (" initMethodAttributes ();");
  275. pw.println (" initFieldAttributes ();");
  276. pw.println (" initConstructorAttributes ();");
  277. pw.println (" }");
  278. pw.println ();
  279. pw.println (" public java.util.Set getClassAttributes () { return classAttributes; }");
  280. pw.println (" public java.util.Map getFieldAttributes () { return fieldAttributes; }");
  281. pw.println (" public java.util.Map getConstructorAttributes () { return constructorAttributes; }");
  282. pw.println (" public java.util.Map getMethodAttributes () { return methodAttributes; }");
  283. pw.println ();
  284. pw.println (" private void initClassAttributes () {");
  285. addExpressions (xClass.getDoc ().getTags (), pw, "classAttributes", sourceFile);
  286. pw.println (" }");
  287. pw.println ();
  288. // ---- Field Attributes
  289. pw.println (" private void initFieldAttributes () {");
  290. pw.println (" java.util.Set attrs = null;");
  291. for (Iterator iter = xClass.getFields ().iterator (); iter.hasNext ();) {
  292. XField member = (XField) iter.next ();
  293. if (member.getDoc ().getTags ().size () > 0) {
  294. String key = member.getName ();
  295. pw.println (" attrs = new java.util.HashSet ();");
  296. addExpressions (member.getDoc ().getTags (), pw, "attrs", sourceFile);
  297. pw.println (" fieldAttributes.put (\"" + key + "\", attrs);");
  298. pw.println (" attrs = null;");
  299. pw.println ();
  300. }
  301. }
  302. pw.println (" }");
  303. // ---- Method Attributes
  304. pw.println (" private void initMethodAttributes () {");
  305. pw.println (" java.util.Set attrs = null;");
  306. pw.println (" java.util.List bundle = null;");
  307. for (Iterator iter = xClass.getMethods ().iterator (); iter.hasNext ();) {
  308. XMethod member = (XMethod) iter.next ();
  309. if (member.getDoc ().getTags ().size () > 0) {
  310. StringBuffer sb = new StringBuffer ();
  311. sb.append (member.getName ()).append ("(");
  312. sb.append (getParameterTypes (member.getParameters ()));
  313. sb.append (")");
  314. String key = sb.toString ();
  315. pw.println (" bundle = new java.util.ArrayList ();");
  316. pw.println (" attrs = new java.util.HashSet ();");
  317. addExpressions (member.getDoc ().getTags (), null, pw, "attrs", sourceFile);
  318. pw.println (" bundle.add (attrs);");
  319. pw.println (" attrs = null;");
  320. pw.println (" attrs = new java.util.HashSet ();");
  321. addExpressions (member.getDoc ().getTags (), "return", pw, "attrs", sourceFile);
  322. pw.println (" bundle.add (attrs);");
  323. pw.println (" attrs = null;");
  324. for (Iterator parameters = member.getParameters ().iterator (); parameters.hasNext ();) {
  325. XParameter parameter = (XParameter) parameters.next ();
  326. pw.println (" attrs = new java.util.HashSet ();");
  327. addExpressions (member.getDoc ().getTags (), parameter.getName (), pw, "attrs", sourceFile);
  328. pw.println (" bundle.add (attrs);");
  329. pw.println (" attrs = null;");
  330. }
  331. pw.println (" methodAttributes.put (\"" + key + "\", bundle);");
  332. pw.println (" bundle = null;");
  333. pw.println ();
  334. }
  335. }
  336. pw.println (" }");
  337. // ---- Constructor Attributes
  338. pw.println (" private void initConstructorAttributes () {");
  339. pw.println (" java.util.Set attrs = null;");
  340. pw.println (" java.util.List bundle = null;");
  341. for (Iterator iter = xClass.getConstructors ().iterator (); iter.hasNext ();) {
  342. XConstructor member = (XConstructor) iter.next ();
  343. if (member.getDoc ().getTags ().size () > 0) {
  344. StringBuffer sb = new StringBuffer ();
  345. sb.append ("(");
  346. sb.append (getParameterTypes (member.getParameters ()));
  347. sb.append (")");
  348. String key = sb.toString ();
  349. pw.println (" bundle = new java.util.ArrayList ();");
  350. pw.println (" attrs = new java.util.HashSet ();");
  351. addExpressions (member.getDoc ().getTags (), null, pw, "attrs", sourceFile);
  352. pw.println (" bundle.add (attrs);");
  353. pw.println (" attrs = null;");
  354. for (Iterator parameters = member.getParameters ().iterator (); parameters.hasNext ();) {
  355. XParameter parameter = (XParameter) parameters.next ();
  356. pw.println (" attrs = new java.util.HashSet ();");
  357. addExpressions (member.getDoc ().getTags (), parameter.getName (), pw, "attrs", sourceFile);
  358. pw.println (" bundle.add (attrs);");
  359. pw.println (" attrs = null;");
  360. }
  361. pw.println (" constructorAttributes.put (\"" + key + "\", bundle);");
  362. pw.println (" bundle = null;");
  363. pw.println ();
  364. }
  365. }
  366. pw.println (" }");
  367. }
  368. pw.println ("}");
  369. pw.close ();
  370. } catch (Exception e) {
  371. pw.close ();
  372. destFile.delete ();
  373. throw e;
  374. }
  375. }
  376. /**
  377. * Finds the source file of a class.
  378. *
  379. * @param qualifiedName the fully qualified class name
  380. * @return the file the class is defined in.
  381. * @throws BuildException if the file could not be found.
  382. */
  383. protected File getSourceFile (XClass xClass) throws BuildException {
  384. while (xClass != null && xClass.isInner ()) {
  385. xClass = xClass.getContainingClass ();
  386. }
  387. if (xClass != null && xClass instanceof SourceClass) {
  388. AbstractFile af = ((SourceClass) xClass).getFile ();
  389. return new File (af.getPath ());
  390. }
  391. return null;
  392. }
  393. protected boolean hasAttributes (XClass xClass) {
  394. if (tagHasAttributes (xClass.getDoc ().getTags ()) ||
  395. elementHasAttributes (xClass.getFields ()) ||
  396. elementHasAttributes (xClass.getMethods ()) ||
  397. elementHasAttributes (xClass.getConstructors ()) ) {
  398. return true;
  399. }
  400. return false;
  401. }
  402. /**
  403. * Tests if a tag is an attribute. Currently the test is
  404. * only "check if it is defined with two @-signs".
  405. */
  406. protected boolean isAttribute (XTag tag) {
  407. return tag.getName ().length () > 0 && tag.getName ().charAt (0) == '@';
  408. }
  409. protected void start() throws BuildException {
  410. destDir.mkdirs ();
  411. numGenerated = 0;
  412. XJavaDoc doc = getXJavaDoc ();
  413. Iterator iter = doc.getSourceClasses ().iterator ();
  414. try {
  415. while (iter.hasNext ()) {
  416. XClass xClass = (XClass) iter.next ();
  417. generateClass (xClass);
  418. }
  419. } catch (Exception e) {
  420. throw new BuildException (e.toString (), e);
  421. }
  422. log ("Generated attribute information for " + numGenerated + " classes. Ignored " + numIgnored + " classes.");
  423. }
  424. /**
  425. * Checks if a collection of XTags contain any tags specifying attributes.
  426. */
  427. protected boolean tagHasAttributes (Collection tags) {
  428. Iterator iter = tags.iterator ();
  429. while (iter.hasNext ()) {
  430. XTag tag = (XTag) iter.next ();
  431. if (isAttribute (tag)) {
  432. return true;
  433. }
  434. }
  435. return false;
  436. }
  437. }