001: /*******************************************************************************
002: * Copyright (c) 2000, 2006 IBM Corporation and others.
003: * All rights reserved. This program and the accompanying materials
004: * are made available under the terms of the Eclipse Public License v1.0
005: * which accompanies this distribution, and is available at
006: * http://www.eclipse.org/legal/epl-v10.html
007: *
008: * Contributors:
009: * IBM Corporation - initial API and implementation
010: * Brock Janiczak (brockj_eclipse@ihug.com.au) - https://bugs.eclipse.org/bugs/show_bug.cgi?id=20644
011: * Brock Janiczak (brockj_eclipse@ihug.com.au) - https://bugs.eclipse.org/bugs/show_bug.cgi?id=83607
012: *******************************************************************************/package org.eclipse.jdt.internal.ui.text.javadoc;
013:
014: import java.io.IOException;
015: import java.io.Reader;
016: import java.util.ArrayList;
017: import java.util.Iterator;
018: import java.util.List;
019:
020: import org.eclipse.jface.internal.text.html.HTMLPrinter;
021: import org.eclipse.jface.internal.text.html.SubstitutionTextReader;
022:
023: import org.eclipse.jdt.core.dom.TagElement;
024:
025: /**
026: * Processes JavaDoc tags.
027: */
028: public class JavaDoc2HTMLTextReader extends SubstitutionTextReader {
029:
030: static private class Pair {
031: String fTag;
032: String fContent;
033:
034: Pair(String tag, String content) {
035: fTag = tag;
036: fContent = content;
037: }
038: }
039:
040: private List fParameters;
041: private String fReturn;
042: private List fExceptions;
043: private List fAuthors;
044: private List fSees;
045: private List fSince;
046: private List fRest; // list of Pair objects
047:
048: public JavaDoc2HTMLTextReader(Reader reader) {
049: super (reader);
050: setSkipWhitespace(false);
051: }
052:
053: private int getTag(StringBuffer buffer) throws IOException {
054: int c = nextChar();
055: while (c == '.' || c != -1 && Character.isLetter((char) c)) {
056: buffer.append((char) c);
057: c = nextChar();
058: }
059: return c;
060: }
061:
062: private int getContent(StringBuffer buffer, char stopChar)
063: throws IOException {
064: int c = nextChar();
065: while (c != -1 && c != stopChar) {
066: buffer.append((char) c);
067: c = nextChar();
068: }
069: return c;
070: }
071:
072: private int getContentUntilNextTag(StringBuffer buffer)
073: throws IOException {
074: int c = nextChar();
075: boolean blockStartRead = false;
076: while (c != -1) {
077: if (c == '@') {
078: int index = buffer.length();
079: while (--index >= 0
080: && Character.isWhitespace(buffer.charAt(index))) {
081: switch (buffer.charAt(index)) {
082: case '\n':
083: case '\r':
084: return c;
085: }
086: if (index <= 0) {
087: return c;
088: }
089: }
090: }
091: if (blockStartRead) {
092: buffer.append(processBlockTag());
093: blockStartRead = false;
094: } else {
095: buffer.append((char) c);
096: }
097:
098: c = nextChar();
099: blockStartRead = c == '{';
100: }
101: return c;
102: }
103:
104: private String substituteQualification(String qualification) {
105: String result = qualification.replace('#', '.');
106: if (result.startsWith(".")) { //$NON-NLS-1$
107: result = result.substring(1);
108: }
109: return result;
110: }
111:
112: private void printDefinitions(StringBuffer buffer, List list,
113: boolean firstword) {
114: Iterator e = list.iterator();
115: while (e.hasNext()) {
116: String s = (String) e.next();
117: buffer.append("<dd>"); //$NON-NLS-1$
118: if (!firstword)
119: buffer.append(s);
120: else {
121: buffer.append("<b>"); //$NON-NLS-1$
122:
123: int i = getParamEndOffset(s);
124: if (i <= s.length()) {
125: buffer.append(HTMLPrinter.convertToHTMLContent(s
126: .substring(0, i)));
127: buffer.append("</b>"); //$NON-NLS-1$
128: buffer.append(s.substring(i));
129: } else {
130: buffer.append("</b>"); //$NON-NLS-1$
131: }
132: }
133: buffer.append("</dd>"); //$NON-NLS-1$
134: }
135: }
136:
137: private int getParamEndOffset(String s) {
138: int i = 0;
139: final int length = s.length();
140: // \s*
141: while (i < length && Character.isWhitespace(s.charAt(i)))
142: ++i;
143: if (i < length && s.charAt(i) == '<') {
144: // generic type parameter
145: // read <\s*\w*\s*>
146: while (i < length && Character.isWhitespace(s.charAt(i)))
147: ++i;
148: while (i < length
149: && Character.isJavaIdentifierPart(s.charAt(i)))
150: ++i;
151: while (i < length && s.charAt(i) != '>')
152: ++i;
153: } else {
154: // simply read an identifier
155: while (i < length
156: && Character.isJavaIdentifierPart(s.charAt(i)))
157: ++i;
158: }
159:
160: return i;
161: }
162:
163: private void print(StringBuffer buffer, String tag, List elements,
164: boolean firstword) {
165: if (!elements.isEmpty()) {
166: buffer.append("<dt>"); //$NON-NLS-1$
167: buffer.append(tag);
168: buffer.append("</dt>"); //$NON-NLS-1$
169: printDefinitions(buffer, elements, firstword);
170: }
171: }
172:
173: private void print(StringBuffer buffer, String tag, String content) {
174: if (content != null) {
175: buffer.append("<dt>"); //$NON-NLS-1$
176: buffer.append(tag);
177: buffer.append("</dt>"); //$NON-NLS-1$
178: buffer.append("<dd>"); //$NON-NLS-1$
179: buffer.append(content);
180: buffer.append("</dd>"); //$NON-NLS-1$
181: }
182: }
183:
184: private void printRest(StringBuffer buffer) {
185: if (!fRest.isEmpty()) {
186: Iterator e = fRest.iterator();
187: while (e.hasNext()) {
188: Pair p = (Pair) e.next();
189: buffer.append("<dt>"); //$NON-NLS-1$
190: if (p.fTag != null)
191: buffer.append(p.fTag);
192: buffer.append("</dt>"); //$NON-NLS-1$
193: buffer.append("<dd>"); //$NON-NLS-1$
194: if (p.fContent != null)
195: buffer.append(p.fContent);
196: buffer.append("</dd>"); //$NON-NLS-1$
197: }
198: }
199: }
200:
201: private String printSimpleTag() {
202: StringBuffer buffer = new StringBuffer();
203: buffer.append("<dl>"); //$NON-NLS-1$
204: print(buffer,
205: JavaDocMessages.JavaDoc2HTMLTextReader_see_section,
206: fSees, false);
207: print(
208: buffer,
209: JavaDocMessages.JavaDoc2HTMLTextReader_parameters_section,
210: fParameters, true);
211: print(buffer,
212: JavaDocMessages.JavaDoc2HTMLTextReader_returns_section,
213: fReturn);
214: print(buffer,
215: JavaDocMessages.JavaDoc2HTMLTextReader_throws_section,
216: fExceptions, false);
217: print(buffer,
218: JavaDocMessages.JavaDoc2HTMLTextReader_author_section,
219: fAuthors, false);
220: print(buffer,
221: JavaDocMessages.JavaDoc2HTMLTextReader_since_section,
222: fSince, false);
223: printRest(buffer);
224: buffer.append("</dl>"); //$NON-NLS-1$
225:
226: return buffer.toString();
227: }
228:
229: private void handleTag(String tag, String tagContent) {
230:
231: tagContent = tagContent.trim();
232:
233: if (TagElement.TAG_PARAM.equals(tag))
234: fParameters.add(tagContent);
235: else if (TagElement.TAG_RETURN.equals(tag))
236: fReturn = tagContent;
237: else if (TagElement.TAG_EXCEPTION.equals(tag))
238: fExceptions.add(tagContent);
239: else if (TagElement.TAG_THROWS.equals(tag))
240: fExceptions.add(tagContent);
241: else if (TagElement.TAG_AUTHOR.equals(tag))
242: fAuthors.add(substituteQualification(tagContent));
243: else if (TagElement.TAG_SEE.equals(tag))
244: fSees.add(substituteQualification(tagContent));
245: else if (TagElement.TAG_SINCE.equals(tag))
246: fSince.add(substituteQualification(tagContent));
247: else if (tagContent != null)
248: fRest.add(new Pair(tag, tagContent));
249: }
250:
251: /*
252: * A '@' has been read. Process a javadoc tag
253: */
254: private String processSimpleTag() throws IOException {
255:
256: fParameters = new ArrayList();
257: fExceptions = new ArrayList();
258: fAuthors = new ArrayList();
259: fSees = new ArrayList();
260: fSince = new ArrayList();
261: fRest = new ArrayList();
262:
263: StringBuffer buffer = new StringBuffer();
264: int c = '@';
265: while (c != -1) {
266:
267: buffer.setLength(0);
268: buffer.append((char) c);
269: c = getTag(buffer);
270: String tag = buffer.toString();
271:
272: buffer.setLength(0);
273: if (c != -1) {
274: c = getContentUntilNextTag(buffer);
275: }
276:
277: handleTag(tag, buffer.toString());
278: }
279:
280: return printSimpleTag();
281: }
282:
283: private String printBlockTag(String tag, String tagContent) {
284:
285: if (TagElement.TAG_LINK.equals(tag)
286: || TagElement.TAG_LINKPLAIN.equals(tag)) {
287:
288: char[] contentChars = tagContent.toCharArray();
289: boolean inParentheses = false;
290: int labelStart = 0;
291:
292: for (int i = 0; i < contentChars.length; i++) {
293: char nextChar = contentChars[i];
294:
295: // tagContent always has a leading space
296: if (i == 0 && Character.isWhitespace(nextChar)) {
297: labelStart = 1;
298: continue;
299: }
300:
301: if (nextChar == '(') {
302: inParentheses = true;
303: continue;
304: }
305:
306: if (nextChar == ')') {
307: inParentheses = false;
308: continue;
309: }
310:
311: // Stop at first whitespace that is not in parentheses
312: if (!inParentheses && Character.isWhitespace(nextChar)) {
313: labelStart = i + 1;
314: break;
315: }
316: }
317: if (TagElement.TAG_LINK.equals(tag))
318: return "<code>" + substituteQualification(tagContent.substring(labelStart)) + "</code>"; //$NON-NLS-1$//$NON-NLS-2$
319: else
320: return substituteQualification(tagContent
321: .substring(labelStart));
322:
323: } else if (TagElement.TAG_LITERAL.equals(tag)) {
324: return printLiteral(tagContent);
325:
326: } else if (TagElement.TAG_CODE.equals(tag)) {
327: return "<code>" + printLiteral(tagContent) + "</code>"; //$NON-NLS-1$//$NON-NLS-2$
328: }
329:
330: // If something went wrong at least replace the {} with the content
331: return substituteQualification(tagContent);
332: }
333:
334: private String printLiteral(String tagContent) {
335: int contentStart = 0;
336: for (int i = 0; i < tagContent.length(); i++) {
337: if (!Character.isWhitespace(tagContent.charAt(i))) {
338: contentStart = i;
339: break;
340: }
341: }
342: return HTMLPrinter.convertToHTMLContent(tagContent
343: .substring(contentStart));
344: }
345:
346: /*
347: * A '{' has been read. Process a block tag
348: */
349: private String processBlockTag() throws IOException {
350:
351: int c = nextChar();
352:
353: if (c != '@') {
354: StringBuffer buffer = new StringBuffer();
355: buffer.append('{');
356: buffer.append((char) c);
357: return buffer.toString();
358: }
359:
360: StringBuffer buffer = new StringBuffer();
361: if (c != -1) {
362:
363: buffer.setLength(0);
364: buffer.append((char) c);
365:
366: c = getTag(buffer);
367: String tag = buffer.toString();
368:
369: buffer.setLength(0);
370: if (c != -1 && c != '}') {
371: buffer.append((char) c);
372: c = getContent(buffer, '}');
373: }
374:
375: return printBlockTag(tag, buffer.toString());
376: }
377:
378: return null;
379: }
380:
381: /*
382: * @see SubstitutionTextReaderr#computeSubstitution(int)
383: */
384: protected String computeSubstitution(int c) throws IOException {
385: if (c == '@' && fWasWhiteSpace)
386: return processSimpleTag();
387:
388: if (c == '{')
389: return processBlockTag();
390:
391: return null;
392: }
393: }
|