]>
Commit | Line | Data |
---|---|---|
1b2798f6 EK |
1 | /******************************************************************************* |
2 | * Copyright (c) 2000, 2011 IBM Corporation and others. | |
3 | * All rights reserved. This program and the accompanying materials | |
4 | * are made available under the terms of the Eclipse Public License v1.0 | |
5 | * which accompanies this distribution, and is available at | |
6 | * http://www.eclipse.org/legal/epl-v10.html | |
7 | * | |
8 | * Contributors: | |
9 | * IBM Corporation - initial API and implementation | |
10 | * Brock Janiczak (brockj_eclipse@ihug.com.au) - https://bugs.eclipse.org/bugs/show_bug.cgi?id=20644 | |
11 | * Brock Janiczak (brockj_eclipse@ihug.com.au) - https://bugs.eclipse.org/bugs/show_bug.cgi?id=83607 | |
12 | * Benjamin Muskalla <b.muskalla@gmx.net> - [navigation][hovering] Javadoc view cannot find URL with anchor - https://bugs.eclipse.org/bugs/show_bug.cgi?id=70870 | |
13 | *******************************************************************************/ | |
14 | package org.eclipse.jdt.internal.ui.text.javadoc; | |
15 | ||
16 | import java.io.IOException; | |
17 | import java.io.Reader; | |
18 | import java.util.ArrayList; | |
19 | import java.util.Iterator; | |
20 | import java.util.List; | |
21 | ||
22 | import org.eclipse.jface.internal.text.html.HTMLPrinter; | |
23 | import org.eclipse.jface.internal.text.html.SubstitutionTextReader; | |
24 | ||
25 | import org.eclipse.jdt.core.dom.TagElement; | |
26 | ||
27 | ||
28 | /** | |
29 | * Processes JavaDoc tags. | |
30 | */ | |
31 | public class JavaDoc2HTMLTextReader extends SubstitutionTextReader { | |
32 | ||
33 | ||
34 | static class Pair { | |
35 | String fTag; | |
36 | String fContent; | |
37 | ||
38 | Pair(String tag, String content) { | |
39 | fTag= tag; | |
40 | fContent= content; | |
41 | } | |
42 | ||
43 | public void generated_5851077662602872979(StringBuffer buffer, JavaDoc2HTMLTextReader javadoc2htmltextreader) { | |
44 | if (fTag != null) | |
45 | buffer.append(fTag); | |
46 | buffer.append("</dt>"); //$NON-NLS-1$ | |
47 | buffer.append("<dd>"); //$NON-NLS-1$ | |
48 | if (fContent != null) | |
49 | buffer.append(fContent); | |
50 | } | |
51 | } | |
52 | ||
53 | private List<String> fParameters; | |
54 | private String fReturn; | |
55 | private List<String> fExceptions; | |
56 | private List<String> fAuthors; | |
57 | private List<String> fSees; | |
58 | private List<String> fSince; | |
59 | private List<Pair> fRest; // list of Pair objects | |
60 | ||
61 | public JavaDoc2HTMLTextReader(Reader reader) { | |
62 | super(reader); | |
63 | setSkipWhitespace(false); | |
64 | } | |
65 | ||
66 | private int getTag(StringBuffer buffer) throws IOException { | |
67 | int c= nextChar(); | |
68 | while (c == '.' || c != -1 && Character.isLetter((char) c)) { | |
69 | buffer.append((char) c); | |
70 | c= nextChar(); | |
71 | } | |
72 | return c; | |
73 | } | |
74 | ||
75 | private int getContent(StringBuffer buffer, char stopChar) throws IOException { | |
76 | int c= nextChar(); | |
77 | while (c != -1 && c != stopChar) { | |
78 | buffer.append((char) c); | |
79 | c= nextChar(); | |
80 | } | |
81 | return c; | |
82 | } | |
83 | ||
84 | private int getContentUntilNextTag(StringBuffer buffer) throws IOException { | |
85 | int c= nextChar(); | |
86 | boolean blockStartRead= false; | |
87 | while (c != -1) { | |
88 | if (c == '@') { | |
89 | int index= buffer.length(); | |
90 | while (--index >= 0 && Character.isWhitespace(buffer.charAt(index))) { | |
91 | switch (buffer.charAt(index)) { | |
92 | case '\n': | |
93 | case '\r': | |
94 | return c; | |
95 | } | |
96 | if (index <= 0) { | |
97 | return c; | |
98 | } | |
99 | } | |
100 | } | |
101 | if (blockStartRead) { | |
102 | buffer.append(processBlockTag()); | |
103 | blockStartRead= false; | |
104 | } else { | |
105 | buffer.append((char) c); | |
106 | } | |
107 | ||
108 | c= nextChar(); | |
109 | blockStartRead= c == '{'; | |
110 | } | |
111 | return c; | |
112 | } | |
113 | ||
114 | private String substituteQualification(String qualification) { | |
115 | String result; | |
116 | if (qualification.indexOf("<a") == -1) { //$NON-NLS-1$ | |
117 | // No tag at all, use smart way | |
118 | result= qualification.replace('#', '.'); | |
119 | } else { | |
120 | // Handle tags | |
121 | int length= qualification.length(); | |
122 | result= qualification; | |
123 | boolean insideTag= false; | |
124 | for (int i= 0; i < length; i++) { | |
125 | char charAt= result.charAt(i); | |
126 | if (charAt == '<' && result.charAt(i + 1) == 'a') | |
127 | insideTag= true; | |
128 | if (charAt == '>') | |
129 | insideTag= false; | |
130 | if (charAt == '#' && !insideTag) | |
131 | result= result.substring(0, i) + "." + result.substring(i + 1); //$NON-NLS-1$ | |
132 | } | |
133 | } | |
134 | ||
135 | if (result.startsWith(".")) //$NON-NLS-1$ | |
136 | result= result.substring(1); | |
137 | return result; | |
138 | } | |
139 | ||
140 | private void printDefinitions(StringBuffer buffer, List<String> list, boolean firstword) { | |
141 | Iterator<String> e= list.iterator(); | |
142 | while (e.hasNext()) { | |
143 | String s= e.next(); | |
144 | buffer.append("<dd>"); //$NON-NLS-1$ | |
145 | if (!firstword) | |
146 | buffer.append(s); | |
147 | else { | |
148 | buffer.append("<b>"); //$NON-NLS-1$ | |
149 | ||
150 | int i= getParamEndOffset(s); | |
151 | if (i <= s.length()) { | |
152 | buffer.append(HTMLPrinter.convertToHTMLContent(s.substring(0, i))); | |
153 | buffer.append("</b>"); //$NON-NLS-1$ | |
154 | buffer.append(s.substring(i)); | |
155 | } else { | |
156 | buffer.append("</b>"); //$NON-NLS-1$ | |
157 | } | |
158 | } | |
159 | buffer.append("</dd>"); //$NON-NLS-1$ | |
160 | } | |
161 | } | |
162 | ||
163 | private int getParamEndOffset(String s) { | |
164 | int i= 0; | |
165 | final int length= s.length(); | |
166 | // \s* | |
167 | while (i < length && Character.isWhitespace(s.charAt(i))) | |
168 | ++i; | |
169 | if (i < length && s.charAt(i) == '<') { | |
170 | // generic type parameter | |
171 | // read <\s*\w*\s*> | |
172 | while (i < length && Character.isWhitespace(s.charAt(i))) | |
173 | ++i; | |
174 | while (i < length && Character.isJavaIdentifierPart(s.charAt(i))) | |
175 | ++i; | |
176 | while (i < length && s.charAt(i) != '>') | |
177 | ++i; | |
178 | } else { | |
179 | // simply read an identifier | |
180 | while (i < length && Character.isJavaIdentifierPart(s.charAt(i))) | |
181 | ++i; | |
182 | } | |
183 | ||
184 | return i; | |
185 | } | |
186 | ||
187 | private void print(StringBuffer buffer, String tag, List<String> elements, boolean firstword) { | |
188 | if ( !elements.isEmpty()) { | |
189 | buffer.append("<dt>"); //$NON-NLS-1$ | |
190 | buffer.append(tag); | |
191 | buffer.append("</dt>"); //$NON-NLS-1$ | |
192 | printDefinitions(buffer, elements, firstword); | |
193 | } | |
194 | } | |
195 | ||
196 | private void print(StringBuffer buffer, String tag, String content) { | |
197 | if (content != null) { | |
198 | buffer.append("<dt>"); //$NON-NLS-1$ | |
199 | buffer.append(tag); | |
200 | buffer.append("</dt>"); //$NON-NLS-1$ | |
201 | buffer.append("<dd>"); //$NON-NLS-1$ | |
202 | buffer.append(content); | |
203 | buffer.append("</dd>"); //$NON-NLS-1$ | |
204 | } | |
205 | } | |
206 | ||
207 | private void printRest(StringBuffer buffer) { | |
208 | if ( !fRest.isEmpty()) { | |
209 | Iterator<Pair> e= fRest.iterator(); | |
210 | while (e.hasNext()) { | |
211 | Pair p= e.next(); | |
212 | buffer.append("<dt>"); //$NON-NLS-1$ | |
213 | p.generated_5851077662602872979(buffer, this); | |
214 | buffer.append("</dd>"); //$NON-NLS-1$ | |
215 | } | |
216 | } | |
217 | } | |
218 | ||
219 | private String printSimpleTag() { | |
220 | StringBuffer buffer= new StringBuffer(); | |
221 | buffer.append("<dl>"); //$NON-NLS-1$ | |
222 | print(buffer, JavaDocMessages.JavaDoc2HTMLTextReader_see_section, fSees, false); | |
223 | print(buffer, JavaDocMessages.JavaDoc2HTMLTextReader_parameters_section, fParameters, true); | |
224 | print(buffer, JavaDocMessages.JavaDoc2HTMLTextReader_returns_section, fReturn); | |
225 | print(buffer, JavaDocMessages.JavaDoc2HTMLTextReader_throws_section, fExceptions, false); | |
226 | print(buffer, JavaDocMessages.JavaDoc2HTMLTextReader_author_section, fAuthors, false); | |
227 | print(buffer, JavaDocMessages.JavaDoc2HTMLTextReader_since_section, fSince, false); | |
228 | printRest(buffer); | |
229 | buffer.append("</dl>"); //$NON-NLS-1$ | |
230 | ||
231 | return buffer.toString(); | |
232 | } | |
233 | ||
234 | private void handleTag(String tag, String tagContent) { | |
235 | ||
236 | tagContent= tagContent.trim(); | |
237 | ||
238 | if (TagElement.TAG_PARAM.equals(tag)) | |
239 | fParameters.add(tagContent); | |
240 | else if (TagElement.TAG_RETURN.equals(tag)) | |
241 | fReturn= tagContent; | |
242 | else if (TagElement.TAG_EXCEPTION.equals(tag)) | |
243 | fExceptions.add(tagContent); | |
244 | else if (TagElement.TAG_THROWS.equals(tag)) | |
245 | fExceptions.add(tagContent); | |
246 | else if (TagElement.TAG_AUTHOR.equals(tag)) | |
247 | fAuthors.add(substituteQualification(tagContent)); | |
248 | else if (TagElement.TAG_SEE.equals(tag)) | |
249 | fSees.add(substituteQualification(tagContent)); | |
250 | else if (TagElement.TAG_SINCE.equals(tag)) | |
251 | fSince.add(substituteQualification(tagContent)); | |
252 | else if (tagContent != null) | |
253 | fRest.add(new Pair(tag, tagContent)); | |
254 | } | |
255 | ||
256 | /* | |
257 | * A '@' has been read. Process a javadoc tag | |
258 | */ | |
259 | private String processSimpleTag() throws IOException { | |
260 | ||
261 | fParameters= new ArrayList<String>(); | |
262 | fExceptions= new ArrayList<String>(); | |
263 | fAuthors= new ArrayList<String>(); | |
264 | fSees= new ArrayList<String>(); | |
265 | fSince= new ArrayList<String>(); | |
266 | fRest= new ArrayList<Pair>(); | |
267 | ||
268 | StringBuffer buffer= new StringBuffer(); | |
269 | int c= '@'; | |
270 | while (c != -1) { | |
271 | ||
272 | buffer.setLength(0); | |
273 | buffer.append((char) c); | |
274 | c= getTag(buffer); | |
275 | String tag= buffer.toString(); | |
276 | ||
277 | buffer.setLength(0); | |
278 | if (c != -1) { | |
279 | c= getContentUntilNextTag(buffer); | |
280 | } | |
281 | ||
282 | handleTag(tag, buffer.toString()); | |
283 | } | |
284 | ||
285 | return printSimpleTag(); | |
286 | } | |
287 | ||
288 | private String printBlockTag(String tag, String tagContent) { | |
289 | ||
290 | if (TagElement.TAG_LINK.equals(tag) || TagElement.TAG_LINKPLAIN.equals(tag)) { | |
291 | ||
292 | char[] contentChars= tagContent.toCharArray(); | |
293 | boolean inParentheses= false; | |
294 | int labelStart= 0; | |
295 | ||
296 | for (int i= 0; i < contentChars.length; i++) { | |
297 | char nextChar= contentChars[i]; | |
298 | ||
299 | // tagContent always has a leading space | |
300 | if (i == 0 && Character.isWhitespace(nextChar)) { | |
301 | labelStart= 1; | |
302 | continue; | |
303 | } | |
304 | ||
305 | if (nextChar == '(') { | |
306 | inParentheses= true; | |
307 | continue; | |
308 | } | |
309 | ||
310 | if (nextChar == ')') { | |
311 | inParentheses= false; | |
312 | continue; | |
313 | } | |
314 | ||
315 | // Stop at first whitespace that is not in parentheses | |
316 | if (!inParentheses && Character.isWhitespace(nextChar)) { | |
317 | labelStart= i+1; | |
318 | break; | |
319 | } | |
320 | } | |
321 | if (TagElement.TAG_LINK.equals(tag)) | |
322 | return "<code>" + substituteQualification(tagContent.substring(labelStart)) + "</code>"; //$NON-NLS-1$//$NON-NLS-2$ | |
323 | else | |
324 | return substituteQualification(tagContent.substring(labelStart)); | |
325 | ||
326 | } else if (TagElement.TAG_LITERAL.equals(tag)) { | |
327 | return printLiteral(tagContent); | |
328 | ||
329 | } else if (TagElement.TAG_CODE.equals(tag)) { | |
330 | return "<code>" + printLiteral(tagContent) + "</code>"; //$NON-NLS-1$//$NON-NLS-2$ | |
331 | } | |
332 | ||
333 | // If something went wrong at least replace the {} with the content | |
334 | return substituteQualification(tagContent); | |
335 | } | |
336 | ||
337 | private String printLiteral(String tagContent) { | |
338 | int contentStart= 0; | |
339 | for (int i= 0; i < tagContent.length(); i++) { | |
340 | if (! Character.isWhitespace(tagContent.charAt(i))) { | |
341 | contentStart= i; | |
342 | break; | |
343 | } | |
344 | } | |
345 | return HTMLPrinter.convertToHTMLContent(tagContent.substring(contentStart)); | |
346 | } | |
347 | ||
348 | /* | |
349 | * A '{' has been read. Process a block tag | |
350 | */ | |
351 | private String processBlockTag() throws IOException { | |
352 | ||
353 | int c= nextChar(); | |
354 | ||
355 | if (c != '@') { | |
356 | StringBuffer buffer= new StringBuffer(); | |
357 | buffer.append('{'); | |
358 | buffer.append((char) c); | |
359 | return buffer.toString(); | |
360 | } | |
361 | ||
362 | StringBuffer buffer= new StringBuffer(); | |
363 | if (c != -1) { | |
364 | ||
365 | buffer.setLength(0); | |
366 | buffer.append((char) c); | |
367 | ||
368 | c= getTag(buffer); | |
369 | String tag= buffer.toString(); | |
370 | ||
371 | buffer.setLength(0); | |
372 | if (c != -1 && c != '}') { | |
373 | buffer.append((char) c); | |
374 | c= getContent(buffer, '}'); | |
375 | } | |
376 | ||
377 | return printBlockTag(tag, buffer.toString()); | |
378 | } | |
379 | ||
380 | return null; | |
381 | } | |
382 | ||
383 | /* | |
384 | * @see SubstitutionTextReaderr#computeSubstitution(int) | |
385 | */ | |
386 | @Override | |
387 | protected String computeSubstitution(int c) throws IOException { | |
388 | if (c == '@' && fWasWhiteSpace) | |
389 | return processSimpleTag(); | |
390 | ||
391 | if (c == '{') | |
392 | return processBlockTag(); | |
393 | ||
394 | return null; | |
395 | } | |
396 | } |