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.

SeeTagImpl.java 29KB

123456789101112131415161718192021222324252627282930313233343536373839404142434445464748495051525354555657585960616263646566676869707172737475767778798081828384858687888990919293949596979899100101102103104105106107108109110111112113114115116117118119120121122123124125126127128129130131132133134135136137138139140141142143144145146147148149150151152153154155156157158159160161162163164165166167168169170171172173174175176177178179180181182183184185186187188189190191192193194195196197198199200201202203204205206207208209210211212213214215216217218219220221222223224225226227228229230231232233234235236237238239240241242243244245246247248249250251252253254255256257258259260261262263264265266267268269270271272273274275276277278279280281282283284285286287288289290291292293294295296297298299300301302303304305306307308309310311312313314315316317318319320321322323324325326327328329330331332333334335336337338339340341342343344345346347348349350351352353354355356357358359360361362363364365366367368369370371372373374375376377378379380381382383384385386387388389390391392393394395396397398399400401402403404405406407408409410411412413414415416417418419420421422423424425426427428429430431432433434435436437438439440441442443444445446447448449450451452453454455456457458459460461462463464465466467468469470471472473474475476477478479480481482483484485486487488489490491492493494495496497498499500501502503504505506507508509510511512513514515516517518519520521522523524525526527528529530531532533534535536537538539540541542543544545546547548549550551552553554555556557558559560561562563564565566567568569570571572573574575576577578579580581582583584585586587588589590591592593594595596597598599600601602603604605606607608609610611612613614615616617618619620621622623624625626627628629630631632633634635636637638639640641642643644645646647648649650651652653654655656657658659660661662663664665666667668669670671672673674675676677678679680681682683684685686687688689690691692693694695696697698699700701702703704705706707708709710711712713714715716717718719720721722723724725726727728729730731732733734735736737738739740741742743744745746747748749750751752753754755756757758759760761762763764765766767768769770771772773774775776777778779780781782783784785786787788789790791792793794795796797798799800801802803804805806807808809810811812813814815816817818819820821822823824825826827828829830831832833834835836837838839840841842843844845846847848849850851852853854855856857858859860861862863864865866867868869870871872873874
  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.ajdoc.AdviceDoc;
  24. import org.aspectj.ajdoc.AspectDoc;
  25. import org.aspectj.ajdoc.PointcutDoc;
  26. import org.aspectj.compiler.base.JavaCompiler;
  27. import org.aspectj.compiler.base.ast.ASTObject;
  28. import com.sun.javadoc.ClassDoc;
  29. import com.sun.javadoc.ConstructorDoc;
  30. import com.sun.javadoc.Doc;
  31. import com.sun.javadoc.FieldDoc;
  32. import com.sun.javadoc.MemberDoc;
  33. import com.sun.javadoc.MethodDoc;
  34. import com.sun.javadoc.PackageDoc;
  35. import com.sun.javadoc.Parameter;
  36. import com.sun.javadoc.SeeTag;
  37. import java.io.PrintStream;
  38. import java.util.ArrayList;
  39. import java.util.List;
  40. import java.util.Locale;
  41. import java.util.StringTokenizer;
  42. /**
  43. * Implementation of see tags in the aspectj-world.
  44. * See source for bug comments
  45. * @author Jeff Palm
  46. */
  47. public class SeeTagImpl extends TagImpl implements SeeTag {
  48. /*
  49. * This implementation handles
  50. *<pre>{@link package.class#member label}
  51. * @see package.class#member label
  52. * @see "some reference"
  53. * @see <any tag - should be url></pre>.
  54. * It has errors since the tag-separating code which provides
  55. * input is wrong, and it's very weak in how it handles
  56. * text and URLs. Most of the action is in resolve().
  57. */
  58. /** The package name specified by the user -- may be null. */
  59. private String packageName;
  60. /** The class name specified by the user -- may be null. */
  61. private String className;
  62. /** The member name specified by the user -- may be null. */
  63. private String memberName;
  64. /** The referenced package. */
  65. private PackageDoc packageDoc;
  66. /** The referenced class. */
  67. private ClassDoc classDoc;
  68. /** The referenced member. */
  69. private MemberDoc memberDoc;
  70. /** The label associated with the tag. */
  71. private String label = "";
  72. /** A shared instance of the compiler -- a little hacky. */
  73. private static JavaCompiler ajc = AjdocCompiler.instance();
  74. /**
  75. * Constructs the new tag with given parameters and
  76. * calls resolve to parse the class, package, and members.
  77. *
  78. * @param doc the new value for <code>doc</code>.
  79. * @param name the new value for <code>name</code>.
  80. * @param text the new value for <code>text</code>.
  81. * @param locale the new value for <code>locale</code>.
  82. * @param err the new value for <code>err</code>.
  83. */
  84. public SeeTagImpl(Doc doc,
  85. String name,
  86. String text,
  87. Locale loc,
  88. ErrPrinter err) {
  89. super(doc, name, text, loc, err);
  90. resolve();
  91. }
  92. /**
  93. * Returns the label.
  94. *
  95. * @return the label.
  96. */
  97. public String label() {
  98. return label;
  99. }
  100. /**
  101. * Returns the referenced package's name.
  102. * This <b>can</b> be <code>null</code>.
  103. *
  104. * @return the referenced package's name.
  105. */
  106. public String referencedPackageName() {
  107. return packageName;
  108. }
  109. /**
  110. * Returns the referenced package.
  111. * This <b>can</b> be <code>null</code>.
  112. *
  113. * @return a PackageDoc with name packageName.
  114. */
  115. public PackageDoc referencedPackage() {
  116. look();
  117. return packageDoc;
  118. }
  119. /**
  120. * Returns the referenced class's name.
  121. * This <b>can</b> be <code>null</code>.
  122. *
  123. * @return the referenced class's name.
  124. */
  125. public String referencedClassName() {
  126. return className;
  127. }
  128. /**
  129. * Returns the referenced class.
  130. * This <b>can</b> be <code>null</code>.
  131. *
  132. * @return a ClassDoc with name className.
  133. */
  134. public ClassDoc referencedClass() {
  135. look();
  136. return classDoc;
  137. }
  138. /**
  139. * Returns the referenced members's name.
  140. * This <b>can</b> be <code>null</code>.
  141. * This can be null.
  142. *
  143. * @return the referenced class's name.
  144. */
  145. public String referencedMemberName() {
  146. return memberName;
  147. }
  148. /**
  149. * Returns the referenced member.
  150. * This <b>can</b> be <code>null</code>.
  151. *
  152. * @return a ClassDoc with name memberName.
  153. */
  154. public MemberDoc referencedMember() {
  155. look();
  156. return memberDoc;
  157. }
  158. /**
  159. * Returns <code>see</code>.
  160. *
  161. * @return <code>see</code>.
  162. */
  163. public String kind() {
  164. return "@see";
  165. }
  166. protected JavaCompiler ajc() {
  167. if (ajc != null) {
  168. return ajc;
  169. } else if (doc() instanceof ASTObject) {
  170. return ajc = ((ASTObject)doc()).getCompiler();
  171. } else if (doc() instanceof PackageDocImpl) {
  172. return ajc = ((PackageDocImpl)doc()).ajc();
  173. }
  174. return null;
  175. }
  176. void debugState(String m, PrintStream err) {
  177. if (System.getProperty("seetag.debug") != null) {
  178. if (null == err) err = System.err;
  179. err.println("\t______________________ " + m
  180. + "\n\tpackageName: "+packageName
  181. + "\n\t packageDoc: " +packageDoc
  182. + "\n\t className: " +className
  183. + "\n\t classDoc: " +classDoc
  184. + "\n\t memberName: " +memberName
  185. + "\n\t memberDoc: " +memberDoc
  186. + "\n\t label: " +label
  187. );
  188. }
  189. }
  190. private boolean looked = false;
  191. private void look() {
  192. if (!looked) {
  193. looked = true;
  194. dolook();
  195. }
  196. debugState("SeeTagImpl.look()", null);
  197. }
  198. private void dolook() {
  199. // For null or empty classnames set the current doc
  200. // to the referenced class
  201. if (className == null || className.length() < 1) {
  202. classDoc = classDoc(doc());
  203. } else {
  204. // Use the class in which this doc is contained
  205. // as a starting point...
  206. ClassDoc container = classDoc(doc());
  207. // ..and try to find the class from there
  208. if (container == null) {
  209. //TODO: Find class somewhere else
  210. } else {
  211. String fullName;
  212. if (packageName == null || packageName.length() < 1) {
  213. fullName = className;
  214. } else {
  215. fullName = packageName + '.' + className;
  216. }
  217. // As per the language spec...
  218. // http://java.sun.com/docs/books/jls/second_edition/ (con't)
  219. // html/names.doc.html#32725
  220. // We must first consider types before identifiers,
  221. // therefore, if there is no right parent in the member name
  222. // we have to consider this first an inner class, and if
  223. // one is found set the member name to nul
  224. if (memberName != null &&
  225. memberName.indexOf('(') == -1) {
  226. classDoc = container.findClass(fullName + '.' + memberName);
  227. // If we found an inner class, don't look for a member
  228. if (classDoc != null) {
  229. memberName = null;
  230. }
  231. }
  232. // Now if we didn't find an inner class, just look
  233. // up the full name
  234. if (classDoc == null) {
  235. classDoc = container.findClass(fullName);
  236. }
  237. }
  238. }
  239. // If we found a class, then the package is that
  240. // class's contained package
  241. if (classDoc != null) {
  242. packageDoc = classDoc.containingPackage();
  243. } else if (packageName == null) {
  244. // If we didn't find a class, but the class name
  245. // contains no periods, maybe the class name's really
  246. // a package name
  247. if (classDoc == null && className != null &&
  248. className.indexOf('.') == -1) {
  249. packageDoc = PackageDocImpl.getPackageDoc(className);
  250. }
  251. // Otherwise, if the referenced class isn't null, set the
  252. // referenced package to that class's contained package
  253. else if (classDoc != null) {
  254. packageDoc = classDoc.containingPackage();
  255. }
  256. // Lastly, if the current doc is a package (i.e. package.html)
  257. // then set the referenced package to the current doc
  258. else if (doc() instanceof PackageDoc) {
  259. packageDoc = (PackageDoc)doc();
  260. }
  261. } else {
  262. // Look for the fully qualified name of the
  263. // package elsewhere
  264. packageDoc = PackageDocImpl.getPackageDoc(packageName != null
  265. ? packageName
  266. : "");
  267. }
  268. // Look for the member if the member name wasn't null
  269. if (memberName != null) {
  270. lookForMember(memberName, (org.aspectj.ajdoc.ClassDoc)classDoc);
  271. }
  272. }
  273. private void lookForMember(String spec,
  274. org.aspectj.ajdoc.ClassDoc container) {
  275. int ilparen = spec.indexOf('(');
  276. String name;
  277. // No parens mean a field or a method
  278. // The order is for looking is:
  279. // [1] field
  280. // [2] method
  281. // [3] pointcut
  282. if (ilparen == -1) {
  283. name = spec;
  284. if ((memberDoc = fieldDoc(name, container)) != null) {
  285. return;
  286. }
  287. if ((memberDoc = methodDoc(name, container, null)) != null) {
  288. return;
  289. }
  290. if ((memberDoc = pointcutDoc(name, container, null)) != null) {
  291. return;
  292. }
  293. } else {
  294. name = spec.substring(0, ilparen);
  295. }
  296. int irparen = spec.lastIndexOf(')');
  297. // Crop out the parameters
  298. String paramsString;
  299. if (irparen != -1) {
  300. paramsString = spec.substring(ilparen+1, irparen);
  301. } else {
  302. paramsString = spec.substring(ilparen+1, spec.length()-1);
  303. }
  304. // Convert the raw parameters to an array
  305. String[] paramNames = paramNames(paramsString);
  306. // Try to match the name and parameters if the following order:
  307. // [1] method
  308. // [2] constructor
  309. // [3] pointcut
  310. // [4] advice
  311. if ((memberDoc = methodDoc(name, container, paramNames)) != null) {
  312. return;
  313. }
  314. if ((memberDoc = constructorDoc(container, paramNames)) != null) {
  315. return;
  316. }
  317. if ((memberDoc = pointcutDoc(name, container, paramNames)) != null) {
  318. return;
  319. }
  320. if (container instanceof AspectDoc) {
  321. if ((memberDoc = adviceDoc(name,
  322. (AspectDoc)container,
  323. paramNames)) != null) {
  324. return;
  325. }
  326. }
  327. }
  328. private String[] paramNames(String restNoParens) {
  329. if (restNoParens == null || restNoParens.length() == 0) {
  330. return new String[0];
  331. }
  332. List params = new ArrayList();
  333. for (StringTokenizer t = new StringTokenizer(restNoParens, ",", false);
  334. t.hasMoreTokens();) {
  335. String spec = t.nextToken().trim();
  336. int ispace = spec.indexOf(' ');
  337. if (ispace != -1) {
  338. spec = spec.substring(0, ispace);
  339. }
  340. params.add(spec);
  341. }
  342. return (String[])params.toArray(new String[params.size()]);
  343. }
  344. private FieldDoc fieldDoc(String name, ClassDoc container) {
  345. for (ClassDoc cd = container; cd != null; cd = cd.superclass()) {
  346. FieldDoc[] docs = cd.fields();
  347. for (int i = 0, N = docs.length; i < N; i++) {
  348. if (docs[i].name().equals(name)) {
  349. return docs[i];
  350. }
  351. }
  352. }
  353. return null;
  354. }
  355. private PointcutDoc pointcutDoc(String name,
  356. org.aspectj.ajdoc.ClassDoc container,
  357. String[] paramNames) {
  358. if (null == name) return null; // XXX warn or error
  359. for (org.aspectj.ajdoc.ClassDoc cd = container;
  360. cd != null;
  361. cd = (org.aspectj.ajdoc.ClassDoc)cd.superclass()) {
  362. PointcutDoc[] docs = cd.pointcuts();
  363. if (null == docs) {
  364. continue;
  365. }
  366. for (int i = 0, N = docs.length; i < N; i++) {
  367. PointcutDoc md = docs[i];
  368. if ((null != md)
  369. && (name.equals(md.name()))
  370. && ((null == paramNames)
  371. || parametersMatch(md.parameters(), paramNames, container))) {
  372. return md;
  373. }
  374. }
  375. }
  376. return null;
  377. }
  378. private MethodDoc methodDoc(String name, ClassDoc container,
  379. String[] paramNames) {
  380. for (ClassDoc cd = container; cd != null; cd = cd.superclass()) {
  381. MethodDoc[] docs = cd.methods();
  382. for (int i = 0, N = docs.length; i < N; i++) {
  383. MethodDoc md = docs[i];
  384. if (!md.name().equals(name)) {
  385. continue;
  386. }
  387. if (paramNames == null) {
  388. return md;
  389. } else {
  390. if (parametersMatch(md.parameters(),
  391. paramNames,
  392. container)) {
  393. return md;
  394. }
  395. }
  396. }
  397. }
  398. return null;
  399. }
  400. private ConstructorDoc constructorDoc(ClassDoc container,
  401. String[] paramNames) {
  402. for (ClassDoc cd = container; cd != null; cd = cd.superclass()) {
  403. ConstructorDoc[] docs = cd.constructors();
  404. for (int i = 0, N = docs.length; i < N; i++) {
  405. ConstructorDoc md = docs[i];
  406. if (paramNames == null) {
  407. return md;
  408. } else {
  409. if (parametersMatch(md.parameters(),
  410. paramNames,
  411. container)) {
  412. return md;
  413. }
  414. }
  415. }
  416. }
  417. return null;
  418. }
  419. private AdviceDoc adviceDoc(String name,
  420. AspectDoc container,
  421. String[] paramNames) {
  422. AspectDoc cd = container;
  423. while (cd != null) {
  424. AdviceDoc[] docs = cd.advice();
  425. for (int i = 0, N = docs.length; i < N; i++) {
  426. AdviceDoc md = docs[i];
  427. if (!(name.equals(md.name()))) {
  428. continue;
  429. }
  430. if (paramNames == null) {
  431. return md;
  432. } else {
  433. if (parametersMatch(md.parameters(),
  434. paramNames,
  435. container)) {
  436. return md;
  437. }
  438. }
  439. }
  440. Object o = cd.superclass();
  441. if (o instanceof AspectDoc) {
  442. cd = (AspectDoc) o;
  443. } else {
  444. cd = null;
  445. }
  446. }
  447. return null;
  448. }
  449. private boolean parametersMatch(Parameter[] params,
  450. String[] paramNames,
  451. ClassDoc container) {
  452. if ((null == params) || (null == paramNames) || (null == container)) {
  453. return false;
  454. }
  455. if (params.length != paramNames.length) {
  456. return false;
  457. }
  458. for (int i = 0, N = params.length; i < N; i++) {
  459. com.sun.javadoc.Type type1 = params[i].type();
  460. com.sun.javadoc.Type type2 = TypeImpl.getInstance(paramNames[i],
  461. container);
  462. if ((null == type1) || (!type1.equals(type2))) {
  463. return false;
  464. }
  465. }
  466. return true;
  467. }
  468. private ClassDoc classDoc(Doc d) {
  469. if (d instanceof ClassDoc) {
  470. return (ClassDoc)d;
  471. }
  472. if (d instanceof MemberDoc) {
  473. return ((MemberDoc)d).containingClass();
  474. }
  475. return null;
  476. }
  477. /** find next (matching) char x, ignoring instances
  478. * preceded by escape character '\'.
  479. */
  480. private static int findNextChar(final String s, final int start, final char x) { // XXX to Util
  481. if ((null != s) && (start >= 0)) {
  482. boolean escaped = false;
  483. for (int i = start; i < s.length(); i++) {
  484. char c = s.charAt(i);
  485. if (('\\' == c) && !escaped) {
  486. escaped = true;
  487. continue;
  488. } else if ((x == c) && !escaped) {
  489. return i;
  490. }
  491. if (escaped) {
  492. escaped = false;
  493. }
  494. }
  495. }
  496. return -1;
  497. }
  498. /**
  499. * This looks a bit hideous, and it is, I had a state diagram
  500. * with every thing labled, but I lost it -- sorry ;).
  501. * <pre>{@link package.class#member label} </pre>
  502. * http://java.sun.com/j2se/1.3/docs/tooldocs/solaris/javadoc.html#\{@link}
  503. */
  504. private void resolve() {
  505. String str = text();
  506. if (str == null || str.length() < 1) {
  507. return;
  508. }
  509. str = str.trim();
  510. int N = str.length();
  511. char first = str.charAt(0);
  512. if (first == '<') {
  513. if ((N < 4) || (str.charAt(N-1) != '>')) {
  514. err().error("see_tag_unterminated_url",str);
  515. } else {
  516. char second = str.charAt(1);
  517. if ((second == 'a') || (second == 'A')) {
  518. label = str;
  519. } else {
  520. err().error("see_tag_unterminated_url",str); // XXX wrong message
  521. }
  522. }
  523. return;
  524. }
  525. if (first == '"') {
  526. if (N == 1) {
  527. err().error("see_tag_unterminated_string",str);
  528. } else if (str.charAt(N-1) == '"') {
  529. label = str;
  530. } else {
  531. int loc = findNextChar(str, 1, '"');
  532. if (-1 == loc) {
  533. err().error("see_tag_unterminated_string",str);
  534. } else {
  535. label = str.substring(0, loc+1);
  536. }
  537. }
  538. return;
  539. }
  540. // XXX but does not handle URLs?
  541. char c = 0;
  542. int state = 0, next = 0;
  543. boolean finished = false;
  544. int iclassEnd = -1;
  545. int isharp = -1;
  546. int ilastDot = -1;
  547. int iStartLabel = -1;
  548. int iEndMemberLabel = -1; // membername plus parameters
  549. boolean sharp = false;
  550. int i;
  551. done:
  552. for (i = 0; i < N; i++, state = next) {
  553. c = str.charAt(i);
  554. switch (state) {
  555. case 0: // seeking initial: [type|memberName]
  556. if (ident(c)) { next = 1; }
  557. else if (c == '#') { next = 2; iclassEnd = i-1; }
  558. else {
  559. err().error("see_tag_dot_sharp_or_id","\""+c+"\"@0",str);
  560. return;
  561. }
  562. break;
  563. case 1: // reading initial [type|memberName]
  564. if (ident(c)) { next = 1; }
  565. else if (c == '#') { next = 2; iclassEnd = i-1; }
  566. else if (c == '.') { next = 3; ilastDot = i; }
  567. else if (space(c)) { iclassEnd = i-1; next = 16; }
  568. else {
  569. err().error("see_tag_invalid_package_or_class","\""+c+"\"@1",str);
  570. return;
  571. }
  572. break;
  573. case 2: // start reading membername (field only?)
  574. sharp = true;
  575. if (ident(c)) { next = 4; isharp = i; }
  576. else {
  577. err().error("see_tag_expecting_field_name","\""+c+"\"@2",str);
  578. return;
  579. }
  580. break;
  581. case 3: // reading qualified type name
  582. if (ident(c)) { next = 1; }
  583. else {
  584. err().error("see_tag_invalid_id","\""+c+"\"@3",str);
  585. return;
  586. }
  587. break;
  588. case 4: // reading membername
  589. if (ident(c)) { next = 4; }
  590. else if (space(c)) { next = 13; iEndMemberLabel = i-1;}
  591. else if (c == '(') { next = 15; }
  592. else {
  593. err().error("see_tag_invalid_param_start","\""+c+"\"@4",str);
  594. return;
  595. }
  596. break;
  597. case 5: // start reading parms
  598. if (ident(c)) { next = 6; }
  599. else if (space(c)) { next = 5; }
  600. else if (c == ')') { next = 13; iEndMemberLabel = i;}
  601. else {
  602. err().error("see_tag_premature_param_end","\""+c+"\"@5",str);
  603. return;
  604. }
  605. break;
  606. case 6: // reading parm (or type?)
  607. if (ident(c)) { next = 6; }
  608. else if (c == '.') { next = 7; }
  609. else if (c == '[') { next = 8; }
  610. else if (space(c)) { next = 10; }
  611. else if (c == ',') { next = 12; }
  612. else if (c == ')') { iEndMemberLabel = i; next = 16; }
  613. else {
  614. err().error("see_tag_invalid_parameter_type","\""+c+"\"@6",str);
  615. return;
  616. }
  617. break;
  618. case 7: // reading qualified parameter type .
  619. if (ident(c)) { next = 6; }
  620. else {
  621. err().error("see_tag_invalid_parameter_type_ident","\""+c+"\"@7",str);
  622. return;
  623. }
  624. break;
  625. case 8: // reading end of []
  626. if (c == ']') { next = 9; }
  627. else if (space(c)) { next = 8; }
  628. else {
  629. err().error("see_tag_unterminated_array_type","\""+c+"\"@8",str);
  630. return;
  631. }
  632. break;
  633. case 9: // maybe completed parameter type
  634. if (c == '[') { next = 8; }
  635. else if (space(c)) { next = 10; }
  636. else if (c == ',') { next = 12; }
  637. else if (c == ')') { iEndMemberLabel = i; next = 16; }
  638. else {
  639. err().error("see_tag_invalid_parameter_type","\""+c+"\"@9",str);
  640. return;
  641. }
  642. break;
  643. case 10: // completed parm type?
  644. if (ident(c)) { next = 11; }
  645. else if (space(c)) { next = 12; }
  646. else if (c == ',') { next = 14; }
  647. else if (c == ')') { iEndMemberLabel = i; next = 16; }
  648. else {
  649. err().error("see_tag_invalid_parameters","\""+c+"\"@10",str);
  650. return;
  651. }
  652. break;
  653. case 11: // reading parm type?
  654. if (ident(c)) { next = 11; }
  655. else if (space(c)) { next = 12; }
  656. else if (c == ',') { next = 14; }
  657. else if (c == ')') { iEndMemberLabel = i; next = 16; }
  658. else {
  659. err().error("see_tag_invalid_parameters","\""+c+"\"@11",str);
  660. return;
  661. }
  662. break;
  663. case 12: // looking for next parm?
  664. if (space(c)) { next = 12; }
  665. else if (c == ',') { next = 14; }
  666. else if (ident(c)) { next = 15; }
  667. else if (c == ')') { iEndMemberLabel = i; next = 16; }
  668. else {
  669. err().error("see_tag_invalid_parameters","\""+c+"\"@12",str);
  670. return;
  671. }
  672. break;
  673. case 13: // seeking parms or label
  674. if (space(c)) { next = 13; }
  675. else if (c == '(') { next = 5; } // start reading parms
  676. else if (ident(c)) { // start reading label
  677. iStartLabel = i; next = 17;
  678. }
  679. else {
  680. err().error("see_tag_invalid_parameters","\""+c+"\"@13",str);
  681. return;
  682. }
  683. break;
  684. case 14: // type name (or identifier)
  685. if (ident(c)) { next = 6; }
  686. else if (space(c)) { next = 14; }
  687. else {
  688. err().error("see_tag_expecting_typename_or_whitespace","\""+c+"\"@14",str);
  689. return;
  690. }
  691. break;
  692. case 15: // reading parms
  693. if (ident(c)) { next = 6; }
  694. else if (space(c)) { next = 15; }
  695. else if (c == ')') { iEndMemberLabel = i; next = 16; }
  696. else {
  697. err().error("see_tag_premature_param_end","\""+c+"\"@15",str);
  698. return;
  699. }
  700. break;
  701. case 16 : // seeking label
  702. if (ident(c)) { iStartLabel = i; next = 17; }
  703. else if (space(c)) { next = 16; }
  704. else {
  705. String s = "\"" + c + "\" in \"" + text() + "\"";
  706. err().error("see_tag_premature_param_end",s + "@16",str);
  707. return;
  708. }
  709. break;
  710. case 17 : // reading label - may have internal spaces
  711. if (ident(c)) { next = 17; }
  712. else if (space(c)) { next = 17; }
  713. // XXX known limitation - labels may only be ident + whitespace (no -)
  714. else {
  715. err().error("see_tag_premature_param_end","\""+c+"\"@17",str);
  716. return;
  717. }
  718. break;
  719. }
  720. if (i == N-1) {
  721. finished = next == -1
  722. || next == 1 || next == 13
  723. || next == 16 || next == 17
  724. || next == 4 || next == 12;
  725. }
  726. }
  727. if (sharp) {
  728. if (ilastDot != -1) {
  729. packageName = str.substring(0, ilastDot);
  730. }
  731. } else {
  732. if (ilastDot == -1) {
  733. //packageName = str;
  734. } else {
  735. packageName = str.substring(0, ilastDot);
  736. }
  737. }
  738. if (sharp) {
  739. if (iclassEnd != -1) {
  740. if (ilastDot == -1) {
  741. className = str.substring(0, iclassEnd+1);
  742. } else {
  743. className = str.substring(ilastDot+1, iclassEnd+1);
  744. }
  745. }
  746. } else {
  747. if (ilastDot == -1) {
  748. if (iclassEnd != -1) {
  749. className = str.substring(0, iclassEnd+1);
  750. } else {
  751. className = str;
  752. }
  753. } else {
  754. if (iclassEnd != -1) {
  755. className = str.substring(ilastDot+1, iclassEnd+1);
  756. } else {
  757. className = str.substring(ilastDot+1);
  758. }
  759. }
  760. }
  761. if (sharp) {
  762. if (-1 != iEndMemberLabel) {
  763. memberName = str.substring(isharp, iEndMemberLabel+1).trim();
  764. } else {
  765. memberName = str.substring(isharp).trim();
  766. }
  767. // hack to remove spaces between method name and parms
  768. int parmLoc = memberName.indexOf("(");
  769. if (-1 != parmLoc) {
  770. int spaceLoc = memberName.indexOf(" ");
  771. if ((-1 != spaceLoc) && (spaceLoc < parmLoc)) {
  772. memberName = memberName.substring(0,spaceLoc)
  773. + memberName.substring(parmLoc).trim();
  774. }
  775. }
  776. }
  777. if (!finished) {
  778. err().error("see_tag_prematurely_done",str);
  779. } else {
  780. if (iStartLabel != -1) {
  781. label = str.substring(iStartLabel).trim();
  782. } else if (i < N-1) { // when does this happen?
  783. label = str.substring(i).trim();
  784. }
  785. }
  786. }
  787. // test-only methods
  788. String getPackageName() {return packageName;}
  789. String getClassName() { return className;}
  790. String getMemberName() { return memberName; }
  791. String getLabel() { return label; }
  792. }