001: package org.incava.javadoc;
002:
003: import java.io.*;
004: import java.util.*;
005: import org.incava.text.LineMapping;
006: import org.incava.text.Location;
007:
008: /**
009: * A tagged element, such as:
010: * @since 0.1
011: */
012: public class JavadocTaggedNode extends JavadocElement {
013: private boolean parsed = false;
014:
015: private JavadocTag tag = null;
016:
017: private JavadocElement target = null;
018:
019: private JavadocElement description = null;
020:
021: private JavadocElement descriptionNonTarget = null;
022:
023: public JavadocTaggedNode(String text, Location start, Location end) {
024: super (text, start, end);
025: }
026:
027: public JavadocTag getTag() {
028: parse();
029: return tag;
030: }
031:
032: /**
033: * Tag targets may be one of three forms:
034: *
035: * @html <a href="http://www.foo.org/something.html">An HTML Target</a>
036: * @quoted "A Quoted Target"
037: * @word Word
038: */
039: public JavadocElement getTarget() {
040: parse();
041: return target;
042: }
043:
044: /**
045: * This returns the text following the tag, and including the target.
046: */
047: public JavadocElement getDescription() {
048: parse();
049: return description;
050: }
051:
052: /**
053: * This returns the text following the target.
054: */
055: public JavadocElement getDescriptionNonTarget() {
056: parse();
057: return descriptionNonTarget;
058: }
059:
060: protected void parse() {
061: if (!parsed) {
062: int pos = 0;
063: int line = start.line;
064: int col = start.column;
065: int len = text.length();
066:
067: // has to be a tag first
068: while (pos < len
069: && !Character.isWhitespace(text.charAt(pos))) {
070: ++pos;
071: }
072:
073: // tr.Ace.log("after tag, pos: " + pos);
074: // tr.Ace.log("tag line : " + line);
075: // tr.Ace.log("start col: " + start.column);
076: // tr.Ace.log("end col : " + (pos - 1 + start.column));
077:
078: tag = new JavadocTag(text.substring(0, pos), new Location(
079: line, start.column), new Location(line, pos - 1
080: + start.column));
081:
082: // tr.Ace.log("created tag '" + tag.text + "'");
083:
084: LineMapping lines = new LineMapping(text, start.line,
085: start.column);
086:
087: // skip non text
088: while (pos < len
089: && (Character.isWhitespace(text.charAt(pos)) || text
090: .charAt(pos) == '*')) {
091: ++pos;
092: }
093:
094: // tr.Ace.log("position: " + pos);
095: // tr.Ace.log("current char: " + text.charAt(pos));
096:
097: if (pos < len) {
098: // tr.Ace.log("parsing target ...");
099:
100: // target types:
101: final int HTML = 0;
102: final int QUOTED = 1;
103: final int WORD = 2;
104:
105: int targetStart = pos;
106:
107: int type;
108: if (pos + 2 < len
109: && text.substring(pos, pos + 2)
110: .equalsIgnoreCase("<a")) {
111: type = HTML;
112: } else if (text.charAt(pos) == '"') {
113: type = QUOTED;
114: } else {
115: type = WORD;
116: }
117:
118: // tr.Ace.log("target type: " + type);
119:
120: // Also handle targets with balanced parentheses, for example:
121: // @see set(int, double, java.net.Socket)
122: // These can't be nested.
123:
124: boolean inParen = false;
125:
126: while (pos < len) {
127: char ch = text.charAt(pos);
128: if (ch == '\\' && pos + 1 < len) {
129: ++pos;
130: } else if (type == WORD) {
131: if (ch == '(') {
132: inParen = true;
133: } else if (inParen && ch == ')') {
134: inParen = false;
135: }
136: if (!inParen) {
137: if (pos + 1 == len) {
138: // we'll never get a space, because we're at the end
139: ++pos;
140: break;
141: } else if (Character.isWhitespace(ch)) {
142: // we have a space between the target and the next word
143: break;
144: }
145: }
146: } else if (type == HTML
147: && ch == '>'
148: && Character.toLowerCase(text
149: .charAt(pos - 1)) == 'a') {
150: // HTML target
151: ++pos;
152: break;
153: } else if (type == QUOTED && ch == '"') {
154: // quoted target
155: ++pos;
156: break;
157: }
158: ++pos;
159: }
160:
161: // even unbalanced HTML or double-quoted strings will get a target:
162:
163: Location[] targetLocations = lines.getLocations(
164: targetStart, pos - 1);
165:
166: // tr.Ace.log("creating target ...");
167: target = new JavadocElement(text.substring(targetStart,
168: pos), targetLocations[0], targetLocations[1]);
169: // tr.Ace.log("target: " + target);
170:
171: // skip non text
172: while (pos < len
173: && (Character.isWhitespace(text.charAt(pos)) || text
174: .charAt(pos) == '*')) {
175: ++pos;
176: }
177:
178: if (pos == len) {
179: // no description beyond target
180: // tr.Ace.log("no description beyond target");
181: descriptionNonTarget = null;
182: description = new JavadocElement(text.substring(
183: targetStart, len), targetLocations[0], end);
184: } else if (pos < len
185: && !Character.isWhitespace(text.charAt(pos))) {
186: // tr.Ace.log("creating description non-target");
187: Location dntStart = lines.getLocation(pos);
188: descriptionNonTarget = new JavadocElement(text
189: .substring(pos, len), dntStart, end);
190: // tr.Ace.log("created description non-target: " + descriptionNonTarget);
191: description = new JavadocElement(text.substring(
192: targetStart, len), targetLocations[0], end);
193: }
194: }
195: }
196: }
197:
198: }
|