You can not select more than 25 topics Topics must start with a letter or number, can include dashes ('-') and can be up to 35 characters long.

AjdocCompiler.java 22KB

123456789101112131415161718192021222324252627282930313233343536373839404142434445464748495051525354555657585960616263646566676869707172737475767778798081828384858687888990919293949596979899100101102103104105106107108109110111112113114115116117118119120121122123124125126127128129130131132133134135136137138139140141142143144145146147148149150151152153154155156157158159160161162163164165166167168169170171172173174175176177178179180181182183184185186187188189190191192193194195196197198199200201202203204205206207208209210211212213214215216217218219220221222223224225226227228229230231232233234235236237238239240241242243244245246247248249250251252253254255256257258259260261262263264265266267268269270271272273274275276277278279280281282283284285286287288289290291292293294295296297298299300301302303304305306307308309310311312313314315316317318319320321322323324325326327328329330331332333334335336337338339340341342343344345346347348349350351352353354355356357358359360361362363364365366367368369370371372373374375376377378379380381382383384385386387388389390391392393394395396397398399400401402403404405406407408409410411412413414415416417418419420421422423424425426427428429430431432433434435436437438439440441442443444445446447448449450451452453454455456457458459460461462463464465466467468469470471472473474475476477478479480481482483484485486487488489490491492493494495496497498499500501502503504505506507508509510511512513514515516517518519520521522523524525526527528529530531532533534535536537538539540541542543544545546547548549550551552553554555556557558559560561562563564565566567568569570571572573574575576577578579580581582583584585586587588589590591592593594595596597598599600601602603604605
  1. /* -*- Mode: JDE; tab-width: 4; indent-tabs-mode: nil; c-basic-offset: 4 -*-
  2. *
  3. * This file is part of the debugger and core tools for the AspectJ(tm)
  4. * programming language; see http://aspectj.org
  5. *
  6. * The contents of this file are subject to the Mozilla Public License
  7. * Version 1.1 (the "License"); you may not use this file except in
  8. * compliance with the License. You may obtain a copy of the License at
  9. * either http://www.mozilla.org/MPL/ or http://aspectj.org/MPL/.
  10. *
  11. * Software distributed under the License is distributed on an "AS IS" basis,
  12. * WITHOUT WARRANTY OF ANY KIND, either express or implied. See the License
  13. * for the specific language governing rights and limitations under the
  14. * License.
  15. *
  16. * The Original Code is AspectJ.
  17. *
  18. * The Initial Developer of the Original Code is Xerox Corporation. Portions
  19. * created by Xerox Corporation are Copyright (C) 1999-2002 Xerox Corporation.
  20. * All Rights Reserved.
  21. */
  22. package org.aspectj.tools.ajdoc;
  23. import org.aspectj.compiler.base.AbstractCompilerPass;
  24. import org.aspectj.compiler.base.ErrorHandler;
  25. import org.aspectj.compiler.base.ast.CompilationUnit;
  26. import org.aspectj.compiler.base.ast.Dec;
  27. import org.aspectj.compiler.base.ast.Decs;
  28. import org.aspectj.compiler.base.ast.TypeDec;
  29. import org.aspectj.compiler.base.ast.World;
  30. import org.aspectj.compiler.crosscuts.AspectJCompiler;
  31. import com.sun.javadoc.DocErrorReporter;
  32. import com.sun.javadoc.RootDoc;
  33. import java.io.BufferedReader;
  34. import java.io.File;
  35. import java.io.FileFilter;
  36. import java.io.FileReader;
  37. import java.io.IOException;
  38. import java.util.ArrayList;
  39. import java.util.Collection;
  40. import java.util.Collections;
  41. import java.util.HashMap;
  42. import java.util.HashSet;
  43. import java.util.Iterator;
  44. import java.util.List;
  45. import java.util.Map;
  46. import java.util.Set;
  47. import java.util.StringTokenizer;
  48. /**
  49. * Extension of the AspectJCompiler to provide
  50. * functionality for creating documentation.
  51. *
  52. * @author Jeff Palm
  53. */
  54. public class AjdocCompiler extends AspectJCompiler implements RootDocMaker {
  55. /** The name of the program. */
  56. protected final String programName;
  57. /** The error printer we use. */
  58. protected final ErrPrinter err;
  59. /**
  60. * Construct a new ajdoc compile with the
  61. * error handler <code>errorHandler</code> and
  62. * name <code>programName</code>
  63. *
  64. * @param errorHandler the error handler.
  65. * @param programName the name of the program.
  66. */
  67. public AjdocCompiler(ErrorHandler errorHandler, String programName) {
  68. super(errorHandler);
  69. getOptions().preprocess = true;
  70. getOptions().nocomments = true;
  71. (errorHandler = err =
  72. new ErrPrinter(this.programName = programName)).
  73. setCompiler(this);
  74. }
  75. /**
  76. * Construct a new ajdoc compile with the
  77. * name <code>programName</code>.
  78. *
  79. * @param programName the name of the program.
  80. */
  81. public AjdocCompiler(String programName) {
  82. this(null, programName);
  83. }
  84. /**
  85. * Returns the ErrPrinter currently used.
  86. *
  87. * @return the ErrPrinter currently used.
  88. */
  89. public ErrPrinter err() {
  90. return err;
  91. }
  92. /** The packages found on the command line. */
  93. private Set pkgnames = new HashSet();
  94. /** The classes found on the command line and from files. */
  95. private Set classnames = new HashSet();
  96. /** The source files on the command line. */
  97. private Set files = new HashSet();
  98. /** The list of source files to compile. */
  99. protected final List srcfiles = new ArrayList();
  100. /** The list of filenames that came from user-specified source files. */
  101. protected List srcSrcfilenames = new ArrayList();
  102. /** The list of filenames that came from user-specified packages. */
  103. protected List pkgSrcfilenames = new ArrayList();
  104. /** The list of filenames that came from user-specified classes. */
  105. protected List clsSrcfilenames = new ArrayList();
  106. /** The source path with which to search. */
  107. protected final List sourcepaths = new ArrayList();
  108. {
  109. sourcepaths.add(new File("."));
  110. }
  111. /** The list of filenames that came from user-specified classes. */
  112. protected AccessChecker filter;
  113. /**
  114. * Create the RootDoc.
  115. */
  116. public RootDoc makeRootDoc(String sourcepath,
  117. String classpath,
  118. String bootclasspath,
  119. String extdirs,
  120. long flags,
  121. String encoding,
  122. String locale,
  123. String source,
  124. List filenamesAndPackages,
  125. List options,
  126. DocErrorReporter err,
  127. String programName,
  128. AccessChecker filter)
  129. throws CannotMakeRootDocException {
  130. if ((null != filter) && (this.filter != filter)) {
  131. this.filter = filter;
  132. }
  133. if (null == this.filter) {
  134. this.filter = AccessChecker.PROTECTED;
  135. }
  136. if (classpath != null) {
  137. getOptions().classpath = classpath;
  138. }
  139. if (bootclasspath != null) {
  140. getOptions().bootclasspath = bootclasspath;
  141. }
  142. if (extdirs != null) {
  143. getOptions().extdirs = extdirs;
  144. }
  145. if (source != null) {
  146. getOptions().source = source;
  147. }
  148. resolveSourcePath(sourcepath);
  149. resolveFilesAndPackages(filenamesAndPackages);
  150. Collections.sort(pkgSrcfilenames);
  151. Collections.sort(clsSrcfilenames);
  152. Collections.sort(srcSrcfilenames);
  153. srcfiles.addAll(pkgSrcfilenames);
  154. srcfiles.addAll(clsSrcfilenames);
  155. srcfiles.addAll(srcSrcfilenames);
  156. err().notice("starting_internal_compile");
  157. for (Iterator i = options.iterator(); i.hasNext();) {
  158. String[] opts = (String[])i.next();
  159. if (opts.length == 1) {
  160. if (opts[0].equals("-verbose")) {
  161. getOptions().verbose = true;
  162. }
  163. } else if (opts.length == 2) {
  164. if (opts[0].equals("-classpath")) {
  165. getOptions().classpath = opts[1];
  166. } else if (opts[1].equals("-bootclasspath")) {
  167. getOptions().bootclasspath = opts[1];
  168. } else if (opts[1].equals("-extdirs")) {
  169. getOptions().extdirs = opts[1];
  170. }
  171. }
  172. }
  173. // Compile the srcfiles - have to add passes first
  174. addPasses();
  175. internalCompile(srcfiles);
  176. // This is the world with which we create the root
  177. World world = getWorld();
  178. // Add all the classes found in the source files
  179. // to the list of specified classnames
  180. for (Iterator i = world.getCompilationUnits().iterator();
  181. i.hasNext();) {
  182. Decs decs = ((CompilationUnit)i.next()).getDecs();
  183. for (int j = 0, N = decs.size(); j < N; j++) {
  184. Dec dec = decs.get(j);
  185. if (dec instanceof TypeDec) {
  186. classnames.add(((TypeDec)dec).getFullName());
  187. }
  188. }
  189. }
  190. // Initialize and return the root created
  191. // from the our world
  192. err().notice("creating_root");
  193. RootDoc result = init(this, (String[][])options.toArray
  194. (new String[options.size()][]));
  195. // do another pass at RootDoc after constructed
  196. com.sun.javadoc.ClassDoc[] cds = result.classes();
  197. for (int i = 0; i < cds.length; i++) {
  198. if (cds[i] instanceof ClassDocImpl) {
  199. ClassDocImpl cd = (ClassDocImpl) cds[i];
  200. cd.postProcess();
  201. }
  202. }
  203. return result;
  204. }
  205. private static AjdocCompiler instance;
  206. { instance = this; }
  207. public static AjdocCompiler instance() {
  208. return instance;
  209. }
  210. /**
  211. * The entry point to initialize a world created
  212. * from an AspectJCompiler.
  213. *
  214. * @param ajc the compiler.
  215. * @param options the ajdoc options.
  216. * @return a RootDocImpl representing the
  217. * documentation tree.
  218. */
  219. public static RootDocImpl init(AspectJCompiler ajc, String[][] options) {
  220. if (ajc == null) return null; //TODO: make empty
  221. World world = ajc.getWorld();
  222. Collection classnames = null;
  223. Collection pkgnames = null;
  224. if (ajc instanceof AjdocCompiler) {
  225. AjdocCompiler ajdoc = (AjdocCompiler)ajc;
  226. pkgnames = ajdoc.pkgnames;
  227. classnames = ajdoc.classnames;
  228. }
  229. PackageDocImpl.init(ajc);
  230. AccessChecker filter = AccessChecker.PUBLIC;
  231. if (ajc instanceof AjdocCompiler) {
  232. filter = ((AjdocCompiler) ajc).getFilter();
  233. }
  234. RootDocImpl root = new RootDocImpl(world,
  235. options,
  236. pkgnames,
  237. classnames,
  238. filter);
  239. return root;
  240. }
  241. /** set filter associated with this compiler */
  242. protected void setFilter(AccessChecker filter, String arg) {
  243. this.filter = filter;
  244. }
  245. /** get filter associated with this compiler */
  246. public final AccessChecker getFilter() {
  247. return filter;
  248. }
  249. protected final void expandAtFile(String filename,
  250. List args) throws IOException {
  251. BufferedReader in = new BufferedReader(new FileReader(filename));
  252. String dirfile = new File(filename).getParent();
  253. File basedir = new File(null == dirfile ? "." : dirfile ) ;
  254. String line;
  255. while ((line = in.readLine()) != null) {
  256. if (line == null || line.length() < 1) continue;
  257. line = line.trim();
  258. if (line.startsWith("//")) continue;
  259. if (line.startsWith("#")) continue;
  260. if (line.startsWith("@")) {
  261. line = line.substring(1);
  262. File newfile = new File(line);
  263. newfile = newfile.isAbsolute() ?
  264. newfile : new File(basedir, line);
  265. expandAtFile(newfile.getPath(), args);
  266. } else {
  267. File newfile = new File(line);
  268. newfile = newfile.isAbsolute() ?
  269. newfile : new File(basedir, line);
  270. if (newfile.exists()) {
  271. boolean result = maybeAdd(newfile, args);
  272. if (!result) {
  273. // we only support valid filenames, not options
  274. cantResolve(newfile);
  275. }
  276. } else {
  277. boolean addedFile = false;
  278. FileFilter filter = null;
  279. String name = newfile.getName().trim();
  280. if (name.equals("*.java")) {
  281. filter = new FileFilter() {
  282. public boolean accept(File f) {
  283. return f != null &&
  284. f.getName().endsWith(".java");
  285. }
  286. };
  287. } else if (name.equals("*.aj")) {
  288. filter = new FileFilter() {
  289. public boolean accept(File f) {
  290. return f != null &&
  291. f.getName().endsWith(".java");
  292. }
  293. };
  294. } else if (name.equals("*")) {
  295. filter = new FileFilter() {
  296. public boolean accept(File f) {
  297. return f != null &&
  298. (f.getName().endsWith(".java")
  299. || f.getName().endsWith(".aj"));
  300. }
  301. };
  302. }
  303. if (null != filter) {
  304. File parentDir = newfile.getParentFile();
  305. File[] javafiles = parentDir.listFiles(filter);
  306. if (javafiles != null) {
  307. for (int i = 0; i < javafiles.length; i++) {
  308. if (maybeAdd(javafiles[i], args)) {
  309. if (!addedFile) addedFile = true;
  310. } else {
  311. cantResolve(javafiles[i]);
  312. }
  313. }
  314. }
  315. }
  316. if (!addedFile) {
  317. if (isValidPkg(line)) {
  318. args.add(line);
  319. } else {
  320. cantResolve(newfile);
  321. }
  322. }
  323. }
  324. }
  325. }
  326. in.close();
  327. }
  328. protected final void cantResolve(File f) {
  329. err().error("cant_resolve_file", f.getAbsolutePath());
  330. }
  331. private void resolveSourcePath(String sourcepath) {
  332. if (sourcepath != null) {
  333. sourcepaths.remove(0);
  334. for (StringTokenizer t = new StringTokenizer(sourcepath,
  335. File.pathSeparator);
  336. t.hasMoreTokens();) {
  337. File path = new File(t.nextToken().trim());
  338. if (path.exists() && path.isDirectory()) {
  339. sourcepaths.add(path);
  340. }
  341. }
  342. // TODO: don't want this, I think ????
  343. //sourcepaths.add(new File("."));
  344. }
  345. }
  346. private void resolveFilesAndPackages(List filenamesAndPackages) {
  347. Collection pkgnamesFromCmd = new HashSet();
  348. for (Iterator i = filenamesAndPackages.iterator(); i.hasNext();) {
  349. String str = (String)i.next();
  350. File file = new File(str);
  351. if (/*file.isAbsolute() &&*/ maybeAdd(file, srcSrcfilenames)) {
  352. addFile(file);
  353. continue;
  354. } else {
  355. for (Iterator j = sourcepaths.iterator(); j.hasNext();) {
  356. File sourcepath = (File)j.next();
  357. file = new File(sourcepath, str);
  358. if (maybeAdd(file, srcSrcfilenames)) {
  359. addFile(file);
  360. continue;
  361. }
  362. }
  363. }
  364. pkgnamesFromCmd.add(str);
  365. }
  366. for (Iterator i = pkgnamesFromCmd.iterator(); i.hasNext();) {
  367. resolvePackageOrClass((String)i.next());
  368. }
  369. }
  370. private void resolvePackageOrClass(String pkgOrClassName) {
  371. boolean recurse;
  372. String pkgOrClass =
  373. (recurse = (pkgOrClassName.endsWith(".*"))) ?
  374. pkgOrClassName.substring(0, pkgOrClassName.length()-2) :
  375. pkgOrClassName;
  376. for (Iterator i = sourcepaths.iterator(); i.hasNext();) {
  377. File sourcepath = (File)i.next();
  378. File possiblePkg = new File(sourcepath,
  379. pkgOrClass.replace
  380. ('.', File.separatorChar));
  381. if (possiblePkg.exists() && possiblePkg.isDirectory()) {
  382. if (recurse) {
  383. File[] dirs = possiblePkg.listFiles
  384. (new FileFilter() {
  385. public boolean accept(File f) {
  386. return f != null && f.isDirectory();
  387. }
  388. });
  389. for (int j = 0; j < dirs.length; j++) {
  390. String pkgname = pkgOrClass + '.' + dirs[j].getName();
  391. resolvePackageOrClass(pkgname + ".*");
  392. }
  393. }
  394. File[] javafiles = possiblePkg.listFiles
  395. (new FileFilter() {
  396. public boolean accept(File f) {
  397. return f != null && !f.isDirectory();
  398. }
  399. });
  400. if (javafiles.length > 0) {
  401. pkgnames.add(pkgOrClass);
  402. }
  403. boolean addedPkg = false;
  404. for (int j = 0; j < javafiles.length; j++) {
  405. if (maybeAdd(javafiles[j], pkgSrcfilenames) && !addedPkg) {
  406. addPkg(pkgOrClass, javafiles[j]);
  407. addedPkg = true;
  408. }
  409. }
  410. break;
  411. } else {
  412. String pkgname = "";
  413. String classname = pkgOrClass;
  414. int ilastdot = pkgOrClass.lastIndexOf('.');
  415. if (ilastdot != -1) {
  416. pkgname = pkgOrClass.substring(0, ilastdot).
  417. replace('.', File.separatorChar) + File.separatorChar;
  418. classname = pkgOrClass.substring(ilastdot+1);
  419. }
  420. File file = new File(sourcepath,
  421. pkgname + classname + ".java");
  422. if (maybeAdd(file, clsSrcfilenames)) {
  423. addClass(pkgOrClass, file);
  424. break;
  425. }
  426. }
  427. }
  428. }
  429. protected final File findFile(String filename, boolean isDir) {
  430. for (Iterator i = sourcepaths.iterator(); i.hasNext();) {
  431. File sourcepath = (File)i.next();
  432. File file = new File(sourcepath, filename);
  433. if (file.exists() && !(isDir ^ file.isDirectory())) {
  434. return file;
  435. }
  436. }
  437. return null;
  438. }
  439. protected static boolean maybeAddPkg(String pkgname,
  440. Collection pkgnames) {
  441. if (isValidPkg(pkgname)) {
  442. pkgnames.add(pkgname);
  443. return true;
  444. }
  445. return false;
  446. }
  447. protected final Map filesToClassnames = new HashMap();
  448. protected final void addClass(String classname, File file) {
  449. if (!(maybeAddClass(classname))) {
  450. err().error("invalid_class_name", classname);
  451. } else {
  452. filesToClassnames.put(file.getAbsoluteFile(), classname);
  453. }
  454. }
  455. protected final boolean maybeAddClass(String classname) {
  456. return maybeAddClass(classname, classnames);
  457. }
  458. protected static boolean maybeAddClass(String classname,
  459. Collection classnames) {
  460. if (isValidClass(classname)) {
  461. classnames.add(classname);
  462. return true;
  463. }
  464. return false;
  465. }
  466. protected final static boolean isValidClass(String classname) {
  467. return isValidPkg(classname);
  468. }
  469. protected final Map filesToPkgnames = new HashMap();
  470. protected final void addPkg(String pkgname, File file) {
  471. if (!maybeAddPkg(pkgname)) {
  472. err().error("invalid_package_name", pkgname);
  473. } else {
  474. filesToPkgnames.put(file.getAbsoluteFile(), pkgname);
  475. }
  476. }
  477. protected final boolean maybeAddPkg(String pkgname) {
  478. return maybeAddPkg(pkgname, pkgnames);
  479. }
  480. protected final Map filesToFilenames = new HashMap();
  481. protected final void addFile(File file) {
  482. files.add(file);
  483. filesToFilenames.put(file.getAbsoluteFile(), file.getAbsolutePath());
  484. }
  485. protected static boolean maybeAdd(File file, Collection files) {
  486. if (isValidJavaFile(file)) {
  487. files.add(file.getAbsolutePath());
  488. return true;
  489. }
  490. return false;
  491. }
  492. protected final static boolean isValidJavaFile(File file) {
  493. return file != null && file.exists() && !file.isDirectory()
  494. && (file.getName().endsWith(".java")
  495. || file.getName().endsWith(".aj")) ;
  496. }
  497. protected final static boolean isValidPkg(String pkgname) {
  498. if (pkgname == null) {
  499. return false;
  500. }
  501. if (pkgname.length() < 1) {
  502. return true;
  503. }
  504. if (!Character.isJavaIdentifierStart(pkgname.charAt(0))) {
  505. return false;
  506. }
  507. for (int i = 1; i < pkgname.length(); i++) {
  508. char c = pkgname.charAt(i);
  509. if (c == '.' && i == pkgname.length()-1) {
  510. return false;
  511. }
  512. if (!(c == '.' || Character.isJavaIdentifierPart(c))) {
  513. return false;
  514. }
  515. }
  516. return true;
  517. }
  518. protected void loading(CompilationUnit cu) {
  519. File srcfile = cu.getSourceFile().getAbsoluteFile();
  520. String pkgname, classname, filename;
  521. if ((pkgname = (String)filesToPkgnames.get(srcfile))!= null) {
  522. AjdocCompiler.this.err().notice
  523. ("Loading_source_files_for_package", pkgname);
  524. } else if ((classname = (String)filesToClassnames.get(srcfile)) != null) {
  525. AjdocCompiler.this.err().notice
  526. ("Loading_source_file_for_class", classname);
  527. } else if ((filename = (String)filesToFilenames.get(srcfile)) != null) {
  528. AjdocCompiler.this.err().notice
  529. ("Loading_source_file", filename);
  530. }
  531. }
  532. protected AbstractCompilerPass createParserPass() {
  533. return new PrintingParserPass(this);
  534. }
  535. protected static class PrintingParserPass extends AspectJCompiler.ParserPass {
  536. public PrintingParserPass(AjdocCompiler jc) { super(jc); }
  537. public void transform(CompilationUnit cu) {
  538. ((AjdocCompiler)getCompiler()).loading(cu);
  539. super.transform(cu);
  540. }
  541. }
  542. protected void addPasses() {
  543. passes = new ArrayList();
  544. addPreSymbolPasses();
  545. }
  546. }