1 | /******************************************************************************* |
---|---|
2 | * Copyright (c) 2004, 2022 IBM Corporation and others. |
3 | * |
4 | * This program and the accompanying materials |
5 | * are made available under the terms of the Eclipse Public License 2.0 |
6 | * which accompanies this distribution, and is available at |
7 | * https://www.eclipse.org/legal/epl-2.0/ |
8 | * |
9 | * SPDX-License-Identifier: EPL-2.0 |
10 | * |
11 | * Contributors: |
12 | * IBM Corporation - initial API and implementation |
13 | *******************************************************************************/ |
14 | package org.eclipse.jdt.core.dom; |
15 | |
16 | import java.util.ArrayList; |
17 | import java.util.Iterator; |
18 | import java.util.List; |
19 | import java.util.Map; |
20 | |
21 | import org.eclipse.jdt.core.compiler.CharOperation; |
22 | import org.eclipse.jdt.core.compiler.InvalidInputException; |
23 | import org.eclipse.jdt.internal.compiler.classfmt.ClassFileConstants; |
24 | import org.eclipse.jdt.internal.compiler.parser.AbstractCommentParser; |
25 | import org.eclipse.jdt.internal.compiler.parser.Scanner; |
26 | import org.eclipse.jdt.internal.compiler.parser.ScannerHelper; |
27 | import org.eclipse.jdt.internal.compiler.parser.TerminalTokens; |
28 | |
29 | /** |
30 | * Internal parser used for decoding doc comments. |
31 | * |
32 | * @since 3.0 |
33 | */ |
34 | @SuppressWarnings({ "rawtypes", "unchecked" }) |
35 | class DocCommentParser extends AbstractCommentParser { |
36 | |
37 | private Javadoc docComment; |
38 | private AST ast; |
39 | |
40 | DocCommentParser(AST ast, Scanner scanner, boolean check) { |
41 | super(null); |
42 | this.ast = ast; |
43 | this.scanner = scanner; |
44 | switch(this.ast.apiLevel()) { |
45 | case AST.JLS2_INTERNAL : |
46 | this.sourceLevel = ClassFileConstants.JDK1_3; |
47 | break; |
48 | case AST.JLS3_INTERNAL: |
49 | this.sourceLevel = ClassFileConstants.JDK1_5; |
50 | break; |
51 | default: |
52 | // AST.JLS4 for now |
53 | this.sourceLevel = ClassFileConstants.JDK1_7; |
54 | } |
55 | this.checkDocComment = check; |
56 | this.kind = DOM_PARSER | TEXT_PARSE; |
57 | } |
58 | |
59 | /* (non-Javadoc) |
60 | * Returns true if tag @deprecated is present in annotation. |
61 | * |
62 | * If annotation checking is enabled, will also construct an Annotation node, which will be stored into Parser.annotation |
63 | * slot for being consumed later on. |
64 | */ |
65 | public Javadoc parse(int[] positions) { |
66 | return parse(positions[0], positions[1]-positions[0]); |
67 | } |
68 | public Javadoc parse(int start, int length) { |
69 | |
70 | // Init |
71 | this.source = this.scanner.source; |
72 | this.lineEnds = this.scanner.lineEnds; |
73 | this.docComment = new Javadoc(this.ast); |
74 | |
75 | // Parse |
76 | if (this.checkDocComment) { |
77 | this.javadocStart = start; |
78 | this.javadocEnd = start+length-1; |
79 | this.firstTagPosition = this.javadocStart; |
80 | commentParse(); |
81 | } |
82 | this.docComment.setSourceRange(start, length); |
83 | if (this.ast.apiLevel == AST.JLS2_INTERNAL) { |
84 | setComment(start, length); // backward compatibility |
85 | } |
86 | return this.docComment; |
87 | } |
88 | |
89 | /** |
90 | * Sets the comment starting at the given position and with the given length. |
91 | * <p> |
92 | * Note the only purpose of this method is to hide deprecated warnings. |
93 | * @deprecated mark deprecated to hide deprecated usage |
94 | */ |
95 | private void setComment(int start, int length) { |
96 | this.docComment.setComment(new String(this.source, start, length)); |
97 | } |
98 | |
99 | @Override |
100 | public String toString() { |
101 | StringBuilder buffer = new StringBuilder(); |
102 | buffer.append("javadoc: ").append(this.docComment).append("\n"); //$NON-NLS-1$ //$NON-NLS-2$ |
103 | buffer.append(super.toString()); |
104 | return buffer.toString(); |
105 | } |
106 | |
107 | @Override |
108 | protected Object createArgumentReference(char[] name, int dim, boolean isVarargs, Object typeRef, long[] dimPositions, long argNamePos) throws InvalidInputException { |
109 | try { |
110 | MethodRefParameter argument = this.ast.newMethodRefParameter(); |
111 | ASTNode node = (ASTNode) typeRef; |
112 | int argStart = node.getStartPosition(); |
113 | int argEnd = node.getStartPosition()+node.getLength()-1; |
114 | if (dim > 0) argEnd = (int) dimPositions[dim-1]; |
115 | if (argNamePos >= 0) argEnd = (int) argNamePos; |
116 | if (name.length != 0) { |
117 | final SimpleName argName = new SimpleName(this.ast); |
118 | argName.internalSetIdentifier(new String(name)); |
119 | argument.setName(argName); |
120 | int argNameStart = (int) (argNamePos >>> 32); |
121 | argName.setSourceRange(argNameStart, argEnd-argNameStart+1); |
122 | } |
123 | Type argType = null; |
124 | if (node.getNodeType() == ASTNode.PRIMITIVE_TYPE) { |
125 | argType = (PrimitiveType) node; |
126 | } else { |
127 | Name argTypeName = (Name) node; |
128 | argType = this.ast.newSimpleType(argTypeName); |
129 | argType.setSourceRange(argStart, node.getLength()); |
130 | } |
131 | if (dim > 0 && !isVarargs) { |
132 | if (this.ast.apiLevel <= AST.JLS4_INTERNAL) { |
133 | for (int i=0; i<dim; i++) { |
134 | argType = this.ast.newArrayType(argType); |
135 | argType.setSourceRange(argStart, ((int) dimPositions[i])-argStart+1); |
136 | } |
137 | } else { |
138 | ArrayType argArrayType = this.ast.newArrayType(argType, 0); |
139 | argType = argArrayType; |
140 | argType.setSourceRange(argStart, ((int) dimPositions[dim-1])-argStart+1); |
141 | for (int i=0; i<dim; i++) { |
142 | Dimension dimension = this.ast.newDimension(); |
143 | int dimStart = (int) (dimPositions[i] >>> 32); |
144 | int dimEnd = (int) dimPositions[i]; |
145 | dimension.setSourceRange(dimStart, dimEnd-dimStart+1); |
146 | argArrayType.dimensions().add(dimension); |
147 | } |
148 | } |
149 | } |
150 | argument.setType(argType); |
151 | if (this.ast.apiLevel > AST.JLS8_INTERNAL) { |
152 | argument.setVarargs(isVarargs); |
153 | } |
154 | argument.setSourceRange(argStart, argEnd - argStart + 1); |
155 | return argument; |
156 | } |
157 | catch (ClassCastException ex) { |
158 | throw new InvalidInputException(); |
159 | } |
160 | } |
161 | |
162 | @Override |
163 | protected Object createFieldReference(Object receiver) throws InvalidInputException { |
164 | try { |
165 | MemberRef fieldRef = this.ast.newMemberRef(); |
166 | SimpleName fieldName = new SimpleName(this.ast); |
167 | fieldName.internalSetIdentifier(new String(this.identifierStack[0])); |
168 | fieldRef.setName(fieldName); |
169 | int start = (int) (this.identifierPositionStack[0] >>> 32); |
170 | int end = (int) this.identifierPositionStack[0]; |
171 | fieldName.setSourceRange(start, end - start + 1); |
172 | if (receiver == null) { |
173 | start = this.memberStart; |
174 | fieldRef.setSourceRange(start, end - start + 1); |
175 | } else { |
176 | Name typeRef = (Name) receiver; |
177 | fieldRef.setQualifier(typeRef); |
178 | start = typeRef.getStartPosition(); |
179 | end = fieldName.getStartPosition()+fieldName.getLength()-1; |
180 | fieldRef.setSourceRange(start, end-start+1); |
181 | } |
182 | return fieldRef; |
183 | } |
184 | catch (ClassCastException ex) { |
185 | throw new InvalidInputException(); |
186 | } |
187 | } |
188 | |
189 | @Override |
190 | protected Object createMethodReference(Object receiver, List arguments) throws InvalidInputException { |
191 | try { |
192 | // Create method ref |
193 | MethodRef methodRef = this.ast.newMethodRef(); |
194 | SimpleName methodName = new SimpleName(this.ast); |
195 | int length = this.identifierLengthStack[0] - 1; // may be > 0 for member class constructor reference |
196 | methodName.internalSetIdentifier(new String(this.identifierStack[length])); |
197 | methodRef.setName(methodName); |
198 | int start = (int) (this.identifierPositionStack[length] >>> 32); |
199 | int end = (int) this.identifierPositionStack[length]; |
200 | methodName.setSourceRange(start, end - start + 1); |
201 | // Set qualifier |
202 | if (receiver == null) { |
203 | start = this.memberStart; |
204 | methodRef.setSourceRange(start, end - start + 1); |
205 | } else { |
206 | Name typeRef = (Name) receiver; |
207 | methodRef.setQualifier(typeRef); |
208 | start = typeRef.getStartPosition(); |
209 | } |
210 | // Add arguments |
211 | if (arguments != null) { |
212 | Iterator parameters = arguments.listIterator(); |
213 | while (parameters.hasNext()) { |
214 | MethodRefParameter param = (MethodRefParameter) parameters.next(); |
215 | methodRef.parameters().add(param); |
216 | } |
217 | } |
218 | methodRef.setSourceRange(start, this.scanner.getCurrentTokenEndPosition()-start+1); |
219 | return methodRef; |
220 | } |
221 | catch (ClassCastException ex) { |
222 | throw new InvalidInputException(); |
223 | } |
224 | } |
225 | |
226 | @Override |
227 | protected void createTag() { |
228 | int position = this.scanner.currentPosition; |
229 | this.scanner.resetTo(this.tagSourceStart, this.tagSourceEnd); |
230 | StringBuilder tagName = new StringBuilder(); |
231 | int start = this.tagSourceStart; |
232 | this.scanner.getNextChar(); |
233 | while (this.scanner.currentPosition <= (this.tagSourceEnd+1)) { |
234 | tagName.append(this.scanner.currentCharacter); |
235 | this.scanner.getNextChar(); |
236 | } |
237 | if (TagElement.TAG_SNIPPET.equals(tagName.toString())) { |
238 | this.tagSourceEnd = this.index; |
239 | // need to use createSnippetTag to create @snippet |
240 | return; |
241 | } |
242 | TagElement tagElement = this.ast.newTagElement(); |
243 | tagElement.setTagName(tagName.toString()); |
244 | if (this.inlineTagStarted) { |
245 | start = this.inlineTagStart; |
246 | TagElement previousTag = null; |
247 | if (this.astPtr == -1) { |
248 | previousTag = this.ast.newTagElement(); |
249 | previousTag.setSourceRange(start, this.tagSourceEnd-start+1); |
250 | pushOnAstStack(previousTag, true); |
251 | } else { |
252 | previousTag = (TagElement) this.astStack[this.astPtr]; |
253 | } |
254 | int previousStart = previousTag.getStartPosition(); |
255 | previousTag.fragments().add(tagElement); |
256 | previousTag.setSourceRange(previousStart, this.tagSourceEnd-previousStart+1); |
257 | } else { |
258 | pushOnAstStack(tagElement, true); |
259 | } |
260 | tagElement.setSourceRange(start, this.tagSourceEnd-start+1); |
261 | this.scanner.resetTo(position, this.javadocEnd); |
262 | } |
263 | |
264 | @Override |
265 | protected Object createSnippetTag() { |
266 | TagElement tagElement = this.ast.newTagElement(); |
267 | int position = this.scanner.currentPosition; |
268 | this.scanner.resetTo(this.tagSourceStart, this.tagSourceEnd); |
269 | StringBuilder tagName = new StringBuilder(); |
270 | int start = this.tagSourceStart; |
271 | this.scanner.getNextChar(); |
272 | while (this.scanner.currentPosition <= (this.tagSourceEnd+1)) { |
273 | tagName.append(this.scanner.currentCharacter); |
274 | this.scanner.getNextChar(); |
275 | } |
276 | tagElement.setTagName(tagName.toString()); |
277 | if (this.inlineTagStarted) { |
278 | start = this.inlineTagStart; |
279 | TagElement previousTag = null; |
280 | if (this.astPtr == -1) { |
281 | previousTag = this.ast.newTagElement(); |
282 | previousTag.setSourceRange(start, this.tagSourceEnd-start+1); |
283 | pushOnAstStack(previousTag, true); |
284 | } else { |
285 | previousTag = (TagElement) this.astStack[this.astPtr]; |
286 | } |
287 | int previousStart = previousTag.getStartPosition(); |
288 | previousTag.fragments().add(tagElement); |
289 | previousTag.setSourceRange(previousStart, this.tagSourceEnd-previousStart+1); |
290 | } else { |
291 | pushOnAstStack(tagElement, true); |
292 | } |
293 | tagElement.setSourceRange(start, this.tagSourceEnd-start+1); |
294 | this.scanner.resetTo(position, this.javadocEnd); |
295 | return tagElement; |
296 | } |
297 | |
298 | @Override |
299 | protected void setSnippetIsValid(Object tag, boolean value) { |
300 | if (tag instanceof TagElement) { |
301 | ((TagElement) tag).setProperty(TagProperty.TAG_PROPERTY_SNIPPET_IS_VALID, value); |
302 | } |
303 | } |
304 | |
305 | @Override |
306 | protected void setSnippetError(Object tag, String value) { |
307 | if (tag instanceof TagElement) { |
308 | ((TagElement) tag).setProperty(TagProperty.TAG_PROPERTY_SNIPPET_ERROR, value); |
309 | } |
310 | } |
311 | |
312 | @Override |
313 | protected void setSnippetID(Object tag, String value) { |
314 | if (tag instanceof TagElement) { |
315 | ((TagElement) tag).setProperty(TagProperty.TAG_PROPERTY_SNIPPET_ID, value); |
316 | } |
317 | } |
318 | |
319 | @Override |
320 | protected Object createSnippetRegion(String name, List<Object> tags, Object snippetTag, boolean isDummyRegion, boolean considerPrevTag) { |
321 | if (!isDummyRegion) { |
322 | return createSnippetOriginalRegion(name, tags); |
323 | } |
324 | List<TagElement> tagsToBeProcessed = new ArrayList<>(); |
325 | Object toBeReturned = null; |
326 | if (tags != null && tags.size() > 0) { |
327 | int start = -1; |
328 | int end = -1; |
329 | for (Object tag : tags) { |
330 | if (tag instanceof TagElement) { |
331 | TagElement tagElem = (TagElement) tag; |
332 | tagsToBeProcessed.add(tagElem); |
333 | int tagStart = tagElem.getStartPosition(); |
334 | int tagEnd = tagStart + tagElem.getLength(); |
335 | if (start == -1 || start > tagStart) { |
336 | start = tagStart; |
337 | } |
338 | if (end ==-1 || end < tagEnd) { |
339 | end = tagEnd; |
340 | } |
341 | } else if (tag instanceof JavaDocRegion && snippetTag instanceof TagElement) { |
342 | TagElement snippet = (TagElement) snippetTag; |
343 | JavaDocRegion reg = (JavaDocRegion) tag; |
344 | if (!reg.isDummyRegion()) { |
345 | snippet.fragments().add(reg); |
346 | } |
347 | toBeReturned = reg; |
348 | } |
349 | } |
350 | if (tagsToBeProcessed.size() > 0) { |
351 | boolean process = true; |
352 | if (considerPrevTag && snippetTag instanceof TagElement) { |
353 | TagElement snippetTagElem = (TagElement) snippetTag; |
354 | Object prevTag = snippetTagElem.fragments().get(snippetTagElem.fragments().size() - 1); |
355 | if (prevTag instanceof TagElement) { |
356 | tagsToBeProcessed.add(0, (TagElement)prevTag); |
357 | snippetTagElem.fragments().remove(prevTag); |
358 | } else if (prevTag instanceof JavaDocRegion) { |
359 | JavaDocRegion region = (JavaDocRegion) prevTag; |
360 | region.tags().addAll(tagsToBeProcessed); |
361 | toBeReturned = snippetTag; |
362 | process = false; |
363 | } |
364 | } |
365 | if (process) { |
366 | if (tagsToBeProcessed.size() == 1) { |
367 | return tagsToBeProcessed.get(0); |
368 | } |
369 | else { |
370 | JavaDocRegion region = this.ast.newJavaDocRegion(); |
371 | region.tags().addAll(tagsToBeProcessed); |
372 | region.setSourceRange(start, end); |
373 | toBeReturned = region; |
374 | } |
375 | } |
376 | } else { |
377 | toBeReturned = snippetTag; |
378 | } |
379 | } |
380 | return toBeReturned; |
381 | } |
382 | |
383 | private Object createSnippetOriginalRegion(String name, List<Object> tags) { |
384 | JavaDocRegion region = this.ast.newJavaDocRegion(); |
385 | if (tags != null && tags.size() > 0) { |
386 | int start = -1; |
387 | int end = -1; |
388 | for (Object tag : tags) { |
389 | if (tag instanceof TagElement) { |
390 | TagElement tagElem = (TagElement) tag; |
391 | region.tags().add(tagElem); |
392 | int tagStart = tagElem.getStartPosition(); |
393 | int tagEnd = tagStart + tagElem.getLength(); |
394 | if (start == -1 || start > tagStart) { |
395 | start = tagStart; |
396 | } |
397 | if (end ==-1 || end < tagEnd) { |
398 | end = tagEnd; |
399 | } |
400 | } |
401 | } |
402 | region.setSourceRange(start, end-start); |
403 | region.setDummyRegion(false); |
404 | } |
405 | if (name != null) { |
406 | region.setTagName(name); |
407 | } |
408 | return region; |
409 | } |
410 | |
411 | @Override |
412 | protected Object createSnippetInnerTag(String tagName, int start, int end) { |
413 | if (tagName != null) { |
414 | TagElement tagElement = this.ast.newTagElement(); |
415 | tagElement.setTagName(tagName.toString()); |
416 | if (this.astPtr == -1) { |
417 | return null; |
418 | } |
419 | tagElement.setSourceRange(start, end-start); |
420 | return tagElement; |
421 | } |
422 | return null; |
423 | } |
424 | |
425 | @Override |
426 | protected void closeJavaDocRegion(String name, Object snippetTag, int end) { |
427 | if (snippetTag instanceof TagElement) { |
428 | TagElement snippet = (TagElement) snippetTag; |
429 | List<JavaDocRegion> regions = snippet.tagRegions(); |
430 | JavaDocRegion regionToClose = null; |
431 | if (name != null) { |
432 | for (JavaDocRegion region : regions) { |
433 | if (name.equals(region.getTagName())) { |
434 | if (!this.isRegionToBeEnded(region) |
435 | && !this.hasRegionEnded(region)) { |
436 | regionToClose = region; |
437 | break; |
438 | } |
439 | } |
440 | } |
441 | } else { |
442 | for (int i= regions.size()-1; i >-1; i--) { |
443 | JavaDocRegion region = regions.get(i); |
444 | if (!this.isRegionToBeEnded(region) |
445 | && !this.hasRegionEnded(region)) { |
446 | regionToClose = region; |
447 | break; |
448 | } |
449 | } |
450 | } |
451 | if (regionToClose != null) { |
452 | setRegionToBeEnded(regionToClose, true); |
453 | int start = regionToClose.getStartPosition(); |
454 | int curEnd = start + regionToClose.getLength(); |
455 | if (end > curEnd) { |
456 | regionToClose.setSourceRange(start, end-start); |
457 | } |
458 | } |
459 | } |
460 | |
461 | } |
462 | |
463 | @Override |
464 | protected void addTagProperties(Object tag, Map<String, Object> map, int tagCount) { |
465 | if (tag instanceof TagElement) { |
466 | TagElement tagElement = (TagElement) tag; |
467 | map.forEach((k, v) -> { |
468 | TagProperty tagProperty = this.ast.newTagProperty(); |
469 | tagProperty.setName(k); |
470 | if (v instanceof String) { |
471 | tagProperty.setStringValue((String)v); |
472 | } else if (v instanceof ASTNode) { |
473 | tagProperty.setNodeValue((ASTNode)v); |
474 | } |
475 | tagElement.tagProperties().add(tagProperty); |
476 | }); |
477 | tagElement.setProperty(TagProperty.TAG_PROPERTY_SNIPPET_INLINE_TAG_COUNT, tagCount); |
478 | } |
479 | } |
480 | |
481 | @Override |
482 | protected void addSnippetInnerTag(Object obj, Object snippetTag) { |
483 | boolean isNotDummyRegion = false; |
484 | if (obj instanceof JavaDocRegion) { |
485 | JavaDocRegion region = (JavaDocRegion) obj; |
486 | if (snippetTag instanceof TagElement) { |
487 | TagElement snippetTagElem = (TagElement) snippetTag; |
488 | if (!region.isDummyRegion()) { |
489 | snippetTagElem.fragments().add(region); |
490 | isNotDummyRegion = true; |
491 | } else { |
492 | Iterator<Object> itr = region.tags().iterator(); |
493 | while (itr.hasNext()) { |
494 | Object tag = itr.next(); |
495 | if (tag instanceof JavaDocRegion) { |
496 | JavaDocRegion reg = (JavaDocRegion) tag; |
497 | if (!reg.isDummyRegion()) { |
498 | region.tags().remove(reg); |
499 | snippetTagElem.fragments().add(reg); |
500 | } |
501 | } |
502 | } |
503 | } |
504 | } |
505 | } |
506 | if (obj instanceof AbstractTagElement && !isNotDummyRegion) { |
507 | AbstractTagElement tagElement = (AbstractTagElement) obj; |
508 | AbstractTagElement previousTag = null; |
509 | if (this.astPtr == -1) { |
510 | return; |
511 | } else { |
512 | previousTag = (AbstractTagElement) this.astStack[this.astPtr]; |
513 | List fragments = previousTag.fragments(); |
514 | if (this.inlineTagStarted) { |
515 | int size = fragments.size(); |
516 | if (size == 0) { |
517 | //do nothing |
518 | } else { |
519 | // If last fragment is a tag, then use it as previous tag |
520 | ASTNode lastFragment = (ASTNode) fragments.get(size-1); |
521 | if (lastFragment instanceof AbstractTagElement) { |
522 | previousTag = (AbstractTagElement) lastFragment; |
523 | } |
524 | } |
525 | } |
526 | } |
527 | previousTag.fragments().add(tagElement); |
528 | } |
529 | } |
530 | |
531 | |
532 | @Override |
533 | protected Object createTypeReference(int primitiveToken, boolean canBeModule) { |
534 | return createTypeReference(primitiveToken); |
535 | } |
536 | |
537 | @Override |
538 | protected Object createTypeReference(int primitiveToken) { |
539 | int size = this.identifierLengthStack[this.identifierLengthPtr]; |
540 | String[] identifiers = new String[size]; |
541 | int pos = this.identifierPtr - size + 1; |
542 | for (int i = 0; i < size; i++) { |
543 | identifiers[i] = new String(this.identifierStack[pos+i]); |
544 | } |
545 | ASTNode typeRef = null; |
546 | if (primitiveToken == -1) { |
547 | typeRef = this.ast.internalNewName(identifiers); |
548 | } else { |
549 | switch (primitiveToken) { |
550 | case TerminalTokens.TokenNamevoid : |
551 | typeRef = this.ast.newPrimitiveType(PrimitiveType.VOID); |
552 | break; |
553 | case TerminalTokens.TokenNameboolean : |
554 | typeRef = this.ast.newPrimitiveType(PrimitiveType.BOOLEAN); |
555 | break; |
556 | case TerminalTokens.TokenNamebyte : |
557 | typeRef = this.ast.newPrimitiveType(PrimitiveType.BYTE); |
558 | break; |
559 | case TerminalTokens.TokenNamechar : |
560 | typeRef = this.ast.newPrimitiveType(PrimitiveType.CHAR); |
561 | break; |
562 | case TerminalTokens.TokenNamedouble : |
563 | typeRef = this.ast.newPrimitiveType(PrimitiveType.DOUBLE); |
564 | break; |
565 | case TerminalTokens.TokenNamefloat : |
566 | typeRef = this.ast.newPrimitiveType(PrimitiveType.FLOAT); |
567 | break; |
568 | case TerminalTokens.TokenNameint : |
569 | typeRef = this.ast.newPrimitiveType(PrimitiveType.INT); |
570 | break; |
571 | case TerminalTokens.TokenNamelong : |
572 | typeRef = this.ast.newPrimitiveType(PrimitiveType.LONG); |
573 | break; |
574 | case TerminalTokens.TokenNameshort : |
575 | typeRef = this.ast.newPrimitiveType(PrimitiveType.SHORT); |
576 | break; |
577 | default: |
578 | // should not happen |
579 | return null; |
580 | } |
581 | } |
582 | // Update ref for whole name |
583 | int start = (int) (this.identifierPositionStack[pos] >>> 32); |
584 | // int end = (int) this.identifierPositionStack[this.identifierPtr]; |
585 | // typeRef.setSourceRange(start, end-start+1); |
586 | // Update references of each simple name |
587 | if (size > 1) { |
588 | Name name = (Name)typeRef; |
589 | int nameIndex = size; |
590 | for (int i=this.identifierPtr; i>pos; i--, nameIndex--) { |
591 | int s = (int) (this.identifierPositionStack[i] >>> 32); |
592 | int e = (int) this.identifierPositionStack[i]; |
593 | name.index = nameIndex; |
594 | SimpleName simpleName = ((QualifiedName)name).getName(); |
595 | simpleName.index = nameIndex; |
596 | simpleName.setSourceRange(s, e-s+1); |
597 | name.setSourceRange(start, e-start+1); |
598 | name = ((QualifiedName)name).getQualifier(); |
599 | } |
600 | int end = (int) this.identifierPositionStack[pos]; |
601 | name.setSourceRange(start, end-start+1); |
602 | name.index = nameIndex; |
603 | } else { |
604 | int end = (int) this.identifierPositionStack[pos]; |
605 | typeRef.setSourceRange(start, end-start+1); |
606 | } |
607 | return typeRef; |
608 | } |
609 | |
610 | private ModuleQualifiedName createModuleReference(int moduleRefTokenCount) { |
611 | String[] identifiers = new String[moduleRefTokenCount]; |
612 | for (int i = 0; i < moduleRefTokenCount; i++) { |
613 | identifiers[i] = new String(this.identifierStack[i]); |
614 | } |
615 | ModuleQualifiedName moduleRef = new ModuleQualifiedName(this.ast); |
616 | |
617 | ASTNode typeRef = null; |
618 | typeRef = this.ast.internalNewName(identifiers); |
619 | int start = (int) (this.identifierPositionStack[0] >>> 32); |
620 | if (moduleRefTokenCount > 1) { |
621 | Name name = (Name)typeRef; |
622 | int nameIndex = moduleRefTokenCount; |
623 | for (int i=moduleRefTokenCount-1; i>0; i--, nameIndex--) { |
624 | int s = (int) (this.identifierPositionStack[i] >>> 32); |
625 | int e = (int) this.identifierPositionStack[i]; |
626 | name.index = nameIndex; |
627 | SimpleName simpleName = ((QualifiedName)name).getName(); |
628 | simpleName.index = nameIndex; |
629 | simpleName.setSourceRange(s, e-s+1); |
630 | name.setSourceRange(start, e-start+1); |
631 | name = ((QualifiedName)name).getQualifier(); |
632 | } |
633 | int end = (int) this.identifierPositionStack[0]; |
634 | name.setSourceRange(start, end-start+1); |
635 | name.index = nameIndex; |
636 | } else { |
637 | int end = (int) this.identifierPositionStack[0]; |
638 | typeRef.setSourceRange(start, end-start+1); |
639 | } |
640 | moduleRef.setModuleQualifier((Name)typeRef); |
641 | moduleRef.setName(null); |
642 | moduleRef.setSourceRange(typeRef.getStartPosition(), typeRef.getLength()+1); |
643 | return moduleRef; |
644 | } |
645 | |
646 | @Override |
647 | protected Object createModuleTypeReference(int primitiveToken, int moduleRefTokenCount) { |
648 | int size = this.identifierLengthStack[this.identifierLengthPtr]; |
649 | ModuleQualifiedName moduleRef= null; |
650 | Name typeRef= null; |
651 | if (size == moduleRefTokenCount) { |
652 | moduleRef= createModuleReference(moduleRefTokenCount); |
653 | this.lastIdentifierEndPosition++; |
654 | } else { |
655 | String[] moduleIdentifiers = new String[moduleRefTokenCount]; |
656 | String[] identifiers = new String[size- moduleRefTokenCount]; |
657 | int pos = this.identifierPtr - size + 1; |
658 | for (int i = 0; i < size; i++) { |
659 | if (i < moduleRefTokenCount) { |
660 | moduleIdentifiers[i] = new String(this.identifierStack[pos+i]); |
661 | } else { |
662 | identifiers[i-moduleRefTokenCount] = new String(this.identifierStack[pos+i]); |
663 | } |
664 | } |
665 | moduleRef= createModuleReference(moduleRefTokenCount); |
666 | pos = this.identifierPtr+moduleRefTokenCount - size + 1; |
667 | |
668 | if (primitiveToken == -1) { |
669 | typeRef = this.ast.internalNewName(identifiers); |
670 | // Update ref for whole name |
671 | int start = (int) (this.identifierPositionStack[pos] >>> 32); |
672 | // int end = (int) this.identifierPositionStack[this.identifierPtr]; |
673 | // typeRef.setSourceRange(start, end-start+1); |
674 | // Update references of each simple name |
675 | if (size-moduleRefTokenCount > 1) { |
676 | Name name = typeRef; |
677 | int nameIndex = size-moduleRefTokenCount; |
678 | for (int i=this.identifierPtr; i>pos; i--, nameIndex--) { |
679 | int s = (int) (this.identifierPositionStack[i] >>> 32); |
680 | int e = (int) this.identifierPositionStack[i]; |
681 | name.index = nameIndex; |
682 | SimpleName simpleName = ((QualifiedName)name).getName(); |
683 | simpleName.index = nameIndex; |
684 | simpleName.setSourceRange(s, e-s+1); |
685 | name.setSourceRange(start, e-start+1); |
686 | name = ((QualifiedName)name).getQualifier(); |
687 | } |
688 | int end = (int) this.identifierPositionStack[pos]; |
689 | name.setSourceRange(start, end-start+1); |
690 | name.index = nameIndex; |
691 | } else { |
692 | int end = (int) this.identifierPositionStack[pos]; |
693 | typeRef.setSourceRange(start, end-start+1); |
694 | } |
695 | moduleRef.setName(typeRef); |
696 | moduleRef.setSourceRange(moduleRef.getStartPosition(), moduleRef.getLength() + typeRef.getLength()); |
697 | } |
698 | } |
699 | return moduleRef; |
700 | } |
701 | |
702 | @Override |
703 | protected boolean parseIdentifierTag(boolean report) { |
704 | if (super.parseIdentifierTag(report)) { |
705 | createTag(); |
706 | this.index = this.tagSourceEnd+1; |
707 | this.scanner.resetTo(this.index, this.javadocEnd); |
708 | return true; |
709 | } |
710 | return false; |
711 | } |
712 | |
713 | /* |
714 | * Parse @return tag declaration |
715 | */ |
716 | protected boolean parseReturn() { |
717 | createTag(); |
718 | return true; |
719 | } |
720 | |
721 | @Override |
722 | protected boolean parseTag(int previousPosition) throws InvalidInputException { |
723 | |
724 | // Read tag name |
725 | int currentPosition = this.index; |
726 | int token = readTokenAndConsume(); |
727 | char[] tagName = CharOperation.NO_CHAR; |
728 | if (currentPosition == this.scanner.startPosition) { |
729 | this.tagSourceStart = this.scanner.getCurrentTokenStartPosition(); |
730 | this.tagSourceEnd = this.scanner.getCurrentTokenEndPosition(); |
731 | tagName = this.scanner.getCurrentIdentifierSource(); |
732 | } else { |
733 | this.tagSourceEnd = currentPosition-1; |
734 | } |
735 | |
736 | // Try to get tag name other than java identifier |
737 | // (see bug https://bugs.eclipse.org/bugs/show_bug.cgi?id=51660) |
738 | if (this.scanner.currentCharacter != ' ' && !ScannerHelper.isWhitespace(this.scanner.currentCharacter)) { |
739 | tagNameToken: while (token != TerminalTokens.TokenNameEOF && this.index < this.scanner.eofPosition) { |
740 | int length = tagName.length; |
741 | // !, ", #, %, &, ', -, :, <, >, * chars and spaces are not allowed in tag names |
742 | switch (this.scanner.currentCharacter) { |
743 | case '}': |
744 | case '*': // break for '*' as this is perhaps the end of comment (bug 65288) |
745 | case '!': |
746 | case '#': |
747 | case '%': |
748 | case '&': |
749 | case '\'': |
750 | case '"': |
751 | case ':': |
752 | case '<': |
753 | case '>': |
754 | break tagNameToken; |
755 | case '-': // allowed in tag names as this character is often used in doclets (bug 68087) |
756 | System.arraycopy(tagName, 0, tagName = new char[length+1], 0, length); |
757 | tagName[length] = this.scanner.currentCharacter; |
758 | break; |
759 | default: |
760 | if (this.scanner.currentCharacter == ' ' || ScannerHelper.isWhitespace(this.scanner.currentCharacter)) { |
761 | break tagNameToken; |
762 | } |
763 | token = readTokenAndConsume(); |
764 | char[] ident = this.scanner.getCurrentIdentifierSource(); |
765 | System.arraycopy(tagName, 0, tagName = new char[length+ident.length], 0, length); |
766 | System.arraycopy(ident, 0, tagName, length, ident.length); |
767 | break; |
768 | } |
769 | this.tagSourceEnd = this.scanner.getCurrentTokenEndPosition(); |
770 | this.scanner.getNextChar(); |
771 | this.index = this.scanner.currentPosition; |
772 | } |
773 | } |
774 | int length = tagName.length; |
775 | this.index = this.tagSourceEnd+1; |
776 | this.scanner.currentPosition = this.tagSourceEnd+1; |
777 | this.tagSourceStart = previousPosition; |
778 | |
779 | // tage name may be empty (see bug https://bugs.eclipse.org/bugs/show_bug.cgi?id=125903) |
780 | if (tagName.length == 0) { |
781 | return false; |
782 | } |
783 | |
784 | // Decide which parse to perform depending on tag name |
785 | this.tagValue = NO_TAG_VALUE; |
786 | boolean valid = true; |
787 | switch (token) { |
788 | case TerminalTokens.TokenNameIdentifier : |
789 | switch (tagName[0]) { |
790 | case 'c': |
791 | if (length == TAG_CATEGORY_LENGTH && CharOperation.equals(TAG_CATEGORY, tagName)) { |
792 | this.tagValue = TAG_CATEGORY_VALUE; |
793 | valid = parseIdentifierTag(false); // TODO (frederic) reconsider parameter value when @category will be significant in spec |
794 | } else if (length == TAG_CODE_LENGTH && CharOperation.equals(TAG_CODE, tagName)) { |
795 | this.tagValue = TAG_CODE_VALUE; |
796 | createTag(); |
797 | } else { |
798 | this.tagValue = TAG_OTHERS_VALUE; |
799 | createTag(); |
800 | } |
801 | break; |
802 | case 'd': |
803 | if (length == TAG_DEPRECATED_LENGTH && CharOperation.equals(TAG_DEPRECATED, tagName)) { |
804 | this.deprecated = true; |
805 | this.tagValue = TAG_DEPRECATED_VALUE; |
806 | } else { |
807 | this.tagValue = TAG_OTHERS_VALUE; |
808 | } |
809 | createTag(); |
810 | break; |
811 | case 'i': |
812 | if (length == TAG_INHERITDOC_LENGTH && CharOperation.equals(TAG_INHERITDOC, tagName)) { |
813 | if (this.reportProblems) { |
814 | recordInheritedPosition((((long) this.tagSourceStart) << 32) + this.tagSourceEnd); |
815 | } |
816 | this.tagValue = TAG_INHERITDOC_VALUE; |
817 | } else { |
818 | this.tagValue = TAG_OTHERS_VALUE; |
819 | } |
820 | createTag(); |
821 | break; |
822 | case 'p': |
823 | if (length == TAG_PARAM_LENGTH && CharOperation.equals(TAG_PARAM, tagName)) { |
824 | this.tagValue = TAG_PARAM_VALUE; |
825 | valid = parseParam(); |
826 | } else { |
827 | this.tagValue = TAG_OTHERS_VALUE; |
828 | createTag(); |
829 | } |
830 | break; |
831 | case 'e': |
832 | if (length == TAG_EXCEPTION_LENGTH && CharOperation.equals(TAG_EXCEPTION, tagName)) { |
833 | this.tagValue = TAG_EXCEPTION_VALUE; |
834 | valid = parseThrows(); |
835 | } else { |
836 | this.tagValue = TAG_OTHERS_VALUE; |
837 | createTag(); |
838 | } |
839 | break; |
840 | case 's': |
841 | if (length == TAG_SEE_LENGTH && CharOperation.equals(TAG_SEE, tagName)) { |
842 | this.tagValue = TAG_SEE_VALUE; |
843 | if (this.inlineTagStarted) { |
844 | // bug https://bugs.eclipse.org/bugs/show_bug.cgi?id=53290 |
845 | // Cannot have @see inside inline comment |
846 | valid = false; |
847 | } else { |
848 | valid = parseReference(true); |
849 | } |
850 | } else if (length == TAG_SNIPPET_LENGTH && CharOperation.equals(TAG_SNIPPET, tagName)) { |
851 | this.tagValue = TAG_SNIPPET_LENGTH; |
852 | if (!this.inlineTagStarted) { |
853 | // @snippet is an inline comment |
854 | valid = false; |
855 | } else { |
856 | this.tagValue = TAG_SNIPPET_VALUE; |
857 | valid = parseSnippet(); |
858 | } |
859 | } else { |
860 | this.tagValue = TAG_OTHERS_VALUE; |
861 | createTag(); |
862 | } |
863 | break; |
864 | case 'l': |
865 | if (length == TAG_LINK_LENGTH && CharOperation.equals(TAG_LINK, tagName)) { |
866 | this.tagValue = TAG_LINK_VALUE; |
867 | } else if (length == TAG_LINKPLAIN_LENGTH && CharOperation.equals(TAG_LINKPLAIN, tagName)) { |
868 | this.tagValue = TAG_LINKPLAIN_VALUE; |
869 | } else if (length == TAG_LITERAL_LENGTH && CharOperation.equals(TAG_LITERAL, tagName)) { |
870 | this.tagValue = TAG_LITERAL_VALUE; |
871 | } |
872 | |
873 | if (this.tagValue != NO_TAG_VALUE && this.tagValue != TAG_LITERAL_VALUE) { |
874 | if (this.inlineTagStarted) { |
875 | valid = parseReference(true); |
876 | } else { |
877 | // bug https://bugs.eclipse.org/bugs/show_bug.cgi?id=53290 |
878 | // Cannot have @link outside inline comment |
879 | valid = false; |
880 | } |
881 | } else { |
882 | if (this.tagValue == NO_TAG_VALUE) this.tagValue = TAG_OTHERS_VALUE; |
883 | createTag(); |
884 | } |
885 | break; |
886 | case 'v': |
887 | if (this.sourceLevel >= ClassFileConstants.JDK1_5 && length == TAG_VALUE_LENGTH && CharOperation.equals(TAG_VALUE, tagName)) { |
888 | this.tagValue = TAG_VALUE_VALUE; |
889 | if (this.inlineTagStarted) { |
890 | valid = parseReference(); |
891 | } else { |
892 | valid = false; |
893 | } |
894 | } else { |
895 | this.tagValue = TAG_OTHERS_VALUE; |
896 | createTag(); |
897 | } |
898 | break; |
899 | default: |
900 | this.tagValue = TAG_OTHERS_VALUE; |
901 | createTag(); |
902 | } |
903 | break; |
904 | case TerminalTokens.TokenNamereturn : |
905 | this.tagValue = TAG_RETURN_VALUE; |
906 | valid = parseReturn(); |
907 | break; |
908 | case TerminalTokens.TokenNamethrows : |
909 | this.tagValue = TAG_THROWS_VALUE; |
910 | valid = parseThrows(); |
911 | break; |
912 | case TerminalTokens.TokenNameabstract: |
913 | case TerminalTokens.TokenNameassert: |
914 | case TerminalTokens.TokenNameboolean: |
915 | case TerminalTokens.TokenNamebreak: |
916 | case TerminalTokens.TokenNamebyte: |
917 | case TerminalTokens.TokenNamecase: |
918 | case TerminalTokens.TokenNamecatch: |
919 | case TerminalTokens.TokenNamechar: |
920 | case TerminalTokens.TokenNameclass: |
921 | case TerminalTokens.TokenNamecontinue: |
922 | case TerminalTokens.TokenNamedefault: |
923 | case TerminalTokens.TokenNamedo: |
924 | case TerminalTokens.TokenNamedouble: |
925 | case TerminalTokens.TokenNameelse: |
926 | case TerminalTokens.TokenNameextends: |
927 | case TerminalTokens.TokenNamefalse: |
928 | case TerminalTokens.TokenNamefinal: |
929 | case TerminalTokens.TokenNamefinally: |
930 | case TerminalTokens.TokenNamefloat: |
931 | case TerminalTokens.TokenNamefor: |
932 | case TerminalTokens.TokenNameif: |
933 | case TerminalTokens.TokenNameimplements: |
934 | case TerminalTokens.TokenNameimport: |
935 | case TerminalTokens.TokenNameinstanceof: |
936 | case TerminalTokens.TokenNameint: |
937 | case TerminalTokens.TokenNameinterface: |
938 | case TerminalTokens.TokenNamelong: |
939 | case TerminalTokens.TokenNamenative: |
940 | case TerminalTokens.TokenNamenew: |
941 | case TerminalTokens.TokenNamenull: |
942 | case TerminalTokens.TokenNamepackage: |
943 | case TerminalTokens.TokenNameprivate: |
944 | case TerminalTokens.TokenNameprotected: |
945 | case TerminalTokens.TokenNamepublic: |
946 | case TerminalTokens.TokenNameshort: |
947 | case TerminalTokens.TokenNamestatic: |
948 | case TerminalTokens.TokenNamestrictfp: |
949 | case TerminalTokens.TokenNamesuper: |
950 | case TerminalTokens.TokenNameswitch: |
951 | case TerminalTokens.TokenNamesynchronized: |
952 | case TerminalTokens.TokenNamethis: |
953 | case TerminalTokens.TokenNamethrow: |
954 | case TerminalTokens.TokenNametransient: |
955 | case TerminalTokens.TokenNametrue: |
956 | case TerminalTokens.TokenNametry: |
957 | case TerminalTokens.TokenNamevoid: |
958 | case TerminalTokens.TokenNamevolatile: |
959 | case TerminalTokens.TokenNamewhile: |
960 | case TerminalTokens.TokenNameenum : |
961 | case TerminalTokens.TokenNameconst : |
962 | case TerminalTokens.TokenNamegoto : |
963 | this.tagValue = TAG_OTHERS_VALUE; |
964 | createTag(); |
965 | break; |
966 | } |
967 | this.textStart = this.index; |
968 | return valid; |
969 | } |
970 | |
971 | @Override |
972 | protected boolean pushParamName(boolean isTypeParam) { |
973 | int idIndex = isTypeParam ? 1 : 0; |
974 | final SimpleName name = new SimpleName(this.ast); |
975 | name.internalSetIdentifier(new String(this.identifierStack[idIndex])); |
976 | int nameStart = (int) (this.identifierPositionStack[idIndex] >>> 32); |
977 | int nameEnd = (int) (this.identifierPositionStack[idIndex] & 0x00000000FFFFFFFFL); |
978 | name.setSourceRange(nameStart, nameEnd-nameStart+1); |
979 | TagElement paramTag = this.ast.newTagElement(); |
980 | paramTag.setTagName(TagElement.TAG_PARAM); |
981 | if (isTypeParam) { // specific storage for @param <E> (see bug 79809) |
982 | // '<' was stored in identifiers stack |
983 | TextElement text = this.ast.newTextElement(); |
984 | text.setText(new String(this.identifierStack[0])); |
985 | int txtStart = (int) (this.identifierPositionStack[0] >>> 32); |
986 | int txtEnd = (int) (this.identifierPositionStack[0] & 0x00000000FFFFFFFFL); |
987 | text.setSourceRange(txtStart, txtEnd-txtStart+1); |
988 | paramTag.fragments().add(text); |
989 | // add simple name |
990 | paramTag.fragments().add(name); |
991 | // '>' was stored in identifiers stack |
992 | text = this.ast.newTextElement(); |
993 | text.setText(new String(this.identifierStack[2])); |
994 | txtStart = (int) (this.identifierPositionStack[2] >>> 32); |
995 | txtEnd = (int) (this.identifierPositionStack[2] & 0x00000000FFFFFFFFL); |
996 | text.setSourceRange(txtStart, txtEnd-txtStart+1); |
997 | paramTag.fragments().add(text); |
998 | // set param tag source range |
999 | paramTag.setSourceRange(this.tagSourceStart, txtEnd-this.tagSourceStart+1); |
1000 | } else { |
1001 | paramTag.setSourceRange(this.tagSourceStart, nameEnd-this.tagSourceStart+1); |
1002 | paramTag.fragments().add(name); |
1003 | } |
1004 | pushOnAstStack(paramTag, true); |
1005 | return true; |
1006 | } |
1007 | |
1008 | @Override |
1009 | protected boolean pushSeeRef(Object statement) { |
1010 | TagElement seeTag = this.ast.newTagElement(); |
1011 | ASTNode node = (ASTNode) statement; |
1012 | seeTag.fragments().add(node); |
1013 | int end = node.getStartPosition()+node.getLength()-1; |
1014 | if (this.inlineTagStarted) { |
1015 | seeTag.setSourceRange(this.inlineTagStart, end-this.inlineTagStart+1); |
1016 | switch (this.tagValue) { |
1017 | case TAG_LINK_VALUE: |
1018 | seeTag.setTagName(TagElement.TAG_LINK); |
1019 | break; |
1020 | case TAG_LINKPLAIN_VALUE: |
1021 | seeTag.setTagName(TagElement.TAG_LINKPLAIN); |
1022 | break; |
1023 | case TAG_VALUE_VALUE: |
1024 | seeTag.setTagName(TagElement.TAG_VALUE); |
1025 | break; |
1026 | } |
1027 | TagElement previousTag = null; |
1028 | int previousStart = this.inlineTagStart; |
1029 | if (this.astPtr == -1) { |
1030 | previousTag = this.ast.newTagElement(); |
1031 | pushOnAstStack(previousTag, true); |
1032 | } else { |
1033 | previousTag = (TagElement) this.astStack[this.astPtr]; |
1034 | previousStart = previousTag.getStartPosition(); |
1035 | } |
1036 | previousTag.fragments().add(seeTag); |
1037 | previousTag.setSourceRange(previousStart, end-previousStart+1); |
1038 | } else { |
1039 | seeTag.setTagName(TagElement.TAG_SEE); |
1040 | seeTag.setSourceRange(this.tagSourceStart, end-this.tagSourceStart+1); |
1041 | pushOnAstStack(seeTag, true); |
1042 | } |
1043 | return true; |
1044 | } |
1045 | |
1046 | @Override |
1047 | protected void pushText(int start, int end) { |
1048 | |
1049 | // Create text element |
1050 | TextElement text = this.ast.newTextElement(); |
1051 | text.setText(new String( this.source, start, end-start)); |
1052 | text.setSourceRange(start, end-start); |
1053 | |
1054 | // Search previous tag on which to add the text element |
1055 | TagElement previousTag = null; |
1056 | int previousStart = start; |
1057 | if (this.astPtr == -1) { |
1058 | previousTag = this.ast.newTagElement(); |
1059 | previousTag.setSourceRange(start, end-start); |
1060 | pushOnAstStack(previousTag, true); |
1061 | } else { |
1062 | previousTag = (TagElement) this.astStack[this.astPtr]; |
1063 | previousStart = previousTag.getStartPosition(); |
1064 | } |
1065 | |
1066 | // If we're in a inline tag, then retrieve previous tag in its fragments |
1067 | List fragments = previousTag.fragments(); |
1068 | if (this.inlineTagStarted) { |
1069 | int size = fragments.size(); |
1070 | if (size == 0) { |
1071 | // no existing fragment => just add the element |
1072 | TagElement inlineTag = this.ast.newTagElement(); |
1073 | fragments.add(inlineTag); |
1074 | previousTag = inlineTag; |
1075 | } else { |
1076 | // If last fragment is a tag, then use it as previous tag |
1077 | ASTNode lastFragment = (ASTNode) fragments.get(size-1); |
1078 | if (lastFragment.getNodeType() == ASTNode.TAG_ELEMENT) { |
1079 | previousTag = (TagElement) lastFragment; |
1080 | previousStart = previousTag.getStartPosition(); |
1081 | } |
1082 | } |
1083 | } |
1084 | |
1085 | // Add the text |
1086 | previousTag.fragments().add(text); |
1087 | previousTag.setSourceRange(previousStart, end-previousStart); |
1088 | this.textStart = -1; |
1089 | } |
1090 | |
1091 | @Override |
1092 | protected void pushSnippetText(int start, int end, boolean addNewLine, Object snippetTag) { |
1093 | |
1094 | // Create text element |
1095 | TextElement text = this.ast.newTextElement(); |
1096 | String textToBeAdded= new String( this.source, start, end-start); |
1097 | int iindex = textToBeAdded.indexOf('*'); |
1098 | if (iindex > -1 && textToBeAdded.substring(0, iindex+1).trim().equals("*")) { //$NON-NLS-1$ |
1099 | textToBeAdded = textToBeAdded.substring(iindex+1); |
1100 | if (addNewLine) { |
1101 | textToBeAdded += System.lineSeparator(); |
1102 | } |
1103 | } |
1104 | text.setText(textToBeAdded); |
1105 | text.setSourceRange(start, end-start); |
1106 | |
1107 | // Search previous tag on which to add the text element |
1108 | AbstractTagElement previousTag = null; |
1109 | int previousStart = start; |
1110 | if (this.astPtr == -1) { |
1111 | previousTag = this.ast.newTagElement(); |
1112 | previousTag.setSourceRange(start, end-start); |
1113 | pushOnAstStack(previousTag, true); |
1114 | } else { |
1115 | previousTag = (AbstractTagElement) this.astStack[this.astPtr]; |
1116 | previousStart = previousTag.getStartPosition(); |
1117 | } |
1118 | |
1119 | AbstractTagElement prevTag = null; |
1120 | // If we're in a inline tag, then retrieve previous tag in its fragments |
1121 | List fragments = previousTag.fragments(); |
1122 | if (this.inlineTagStarted) { |
1123 | int size = fragments.size(); |
1124 | if (size == 0) { |
1125 | //do nothing |
1126 | } else { |
1127 | // If last fragment is a tag, then use it as previous tag |
1128 | ASTNode lastFragment = (ASTNode) fragments.get(size-1); |
1129 | if (lastFragment instanceof AbstractTagElement) { |
1130 | previousTag = (AbstractTagElement) lastFragment; |
1131 | previousStart = previousTag.getStartPosition(); |
1132 | if (this.snippetInlineTagStarted) { |
1133 | fragments = previousTag.fragments(); |
1134 | size = fragments.size(); |
1135 | if (size == 0) { |
1136 | //do nothing |
1137 | } else { |
1138 | lastFragment = (ASTNode) fragments.get(size-1); |
1139 | if (lastFragment instanceof AbstractTagElement) { |
1140 | prevTag = (AbstractTagElement) lastFragment; |
1141 | this.snippetInlineTagStarted = false; |
1142 | } |
1143 | } |
1144 | this.snippetInlineTagStarted = false; |
1145 | } |
1146 | } |
1147 | } |
1148 | } |
1149 | |
1150 | int finEnd = end; |
1151 | boolean isNotDummyJavaDocRegion = false; |
1152 | if (prevTag instanceof JavaDocRegion && !((JavaDocRegion)prevTag).isDummyRegion()) { |
1153 | isNotDummyJavaDocRegion = true; |
1154 | } |
1155 | // Add the text |
1156 | if (prevTag != null && !isNotDummyJavaDocRegion) { |
1157 | |
1158 | prevTag.fragments().add(text); |
1159 | int curStart = prevTag.getStartPosition(); |
1160 | int curEnd = curStart + prevTag.getLength(); |
1161 | int finStart = start; |
1162 | if (curStart < start) { |
1163 | finStart = curStart; |
1164 | } |
1165 | if (curEnd > end) { |
1166 | finEnd = curEnd; |
1167 | } |
1168 | prevTag.setSourceRange(finStart, finEnd - finStart); |
1169 | } else { |
1170 | previousTag.fragments().add(text); |
1171 | } |
1172 | previousTag.setSourceRange(previousStart, finEnd-previousStart); |
1173 | this.textStart = -1; |
1174 | |
1175 | if (snippetTag instanceof TagElement) { |
1176 | fragments = ((TagElement) snippetTag).fragments(); |
1177 | for (Object frag : fragments) { |
1178 | if (frag instanceof JavaDocRegion) { |
1179 | JavaDocRegion region = (JavaDocRegion) frag; |
1180 | if (!region.isDummyRegion() && !hasRegionEnded(region)) { |
1181 | int startPos = region.getStartPosition(); |
1182 | int endPos = startPos + region.getLength(); |
1183 | if (startPos > start) { |
1184 | startPos = start; |
1185 | } |
1186 | if (endPos < end) { |
1187 | endPos = end; |
1188 | } |
1189 | Object textVal = region.getProperty(TagProperty.TAG_PROPERTY_SNIPPET_REGION_TEXT); |
1190 | if (!(textVal instanceof TextElement)) { |
1191 | region.setProperty(TagProperty.TAG_PROPERTY_SNIPPET_REGION_TEXT, text); |
1192 | } |
1193 | region.setSourceRange(startPos, endPos-startPos); |
1194 | if (isRegionToBeEnded(region)) { |
1195 | setRegionEnded(region, true); |
1196 | } |
1197 | } |
1198 | } |
1199 | } |
1200 | } |
1201 | } |
1202 | |
1203 | private boolean hasRegionEnded(JavaDocRegion region) { |
1204 | boolean ended = false; |
1205 | if (region != null) { |
1206 | Object value = region.getProperty(JavaDocRegion.REGION_ENDED); |
1207 | if (value instanceof Boolean && ((Boolean)value).booleanValue()) { |
1208 | ended = true; |
1209 | } |
1210 | } |
1211 | return ended; |
1212 | } |
1213 | |
1214 | private boolean isRegionToBeEnded(JavaDocRegion region) { |
1215 | boolean toBeEnded = false; |
1216 | if (region != null) { |
1217 | Object value = region.getProperty(JavaDocRegion.REGION_TO_BE_ENDED); |
1218 | if (value instanceof Boolean && ((Boolean)value).booleanValue()) { |
1219 | toBeEnded = true; |
1220 | } |
1221 | } |
1222 | return toBeEnded; |
1223 | } |
1224 | |
1225 | private void setRegionToBeEnded(JavaDocRegion region, boolean value) { |
1226 | if (region != null) { |
1227 | region.setProperty(JavaDocRegion.REGION_TO_BE_ENDED, value); |
1228 | } |
1229 | } |
1230 | |
1231 | private void setRegionEnded(JavaDocRegion region, boolean value) { |
1232 | if (region != null) { |
1233 | region.setProperty(JavaDocRegion.REGION_ENDED, value); |
1234 | setRegionToBeEnded(region, !value); |
1235 | } |
1236 | } |
1237 | |
1238 | @Override |
1239 | protected void pushExternalSnippetText(String text,int start, int end) { |
1240 | String snippetLangHeader = "<pre>"; //$NON-NLS-1$ //the code snippets comes as preformatted so need to prefix them with <pre> tag |
1241 | String snipperLangFooter = "</pre>"; //$NON-NLS-1$ |
1242 | text = snippetLangHeader + text + snipperLangFooter; |
1243 | TextElement textElement = this.ast.newTextElement(); |
1244 | textElement.setText(text); |
1245 | textElement.setSourceRange(start, end-start); |
1246 | |
1247 | // Search previous tag on which to add the text element |
1248 | TagElement previousTag = null; |
1249 | int previousStart = start; |
1250 | int previousEnd = end; |
1251 | if (this.astPtr == -1) { |
1252 | previousTag = this.ast.newTagElement(); |
1253 | previousTag.setSourceRange(start, end-start); |
1254 | pushOnAstStack(previousTag, true); |
1255 | } else { |
1256 | previousTag = (TagElement) this.astStack[this.astPtr]; |
1257 | previousStart = previousTag.getStartPosition(); |
1258 | previousEnd = previousStart + previousTag.getLength(); |
1259 | } |
1260 | previousTag.fragments().add(textElement); |
1261 | int curStart = previousStart; |
1262 | int curEnd = previousEnd; |
1263 | if (start < previousStart) { |
1264 | curStart = start; |
1265 | } |
1266 | if (end > previousEnd) { |
1267 | curEnd = end; |
1268 | } |
1269 | previousTag.setSourceRange(curStart, curEnd-curStart); |
1270 | this.textStart = -1; |
1271 | } |
1272 | |
1273 | |
1274 | @Override |
1275 | protected boolean pushThrowName(Object typeRef) { |
1276 | TagElement throwsTag = this.ast.newTagElement(); |
1277 | switch (this.tagValue) { |
1278 | case TAG_THROWS_VALUE: |
1279 | throwsTag.setTagName(TagElement.TAG_THROWS); |
1280 | break; |
1281 | case TAG_EXCEPTION_VALUE: |
1282 | throwsTag.setTagName(TagElement.TAG_EXCEPTION); |
1283 | break; |
1284 | } |
1285 | throwsTag.setSourceRange(this.tagSourceStart, this.scanner.getCurrentTokenEndPosition()-this.tagSourceStart+1); |
1286 | throwsTag.fragments().add(typeRef); |
1287 | pushOnAstStack(throwsTag, true); |
1288 | return true; |
1289 | } |
1290 | |
1291 | @Override |
1292 | protected void refreshInlineTagPosition(int previousPosition) { |
1293 | if (this.astPtr != -1) { |
1294 | TagElement previousTag = (TagElement) this.astStack[this.astPtr]; |
1295 | if (this.inlineTagStarted) { |
1296 | int previousStart = previousTag.getStartPosition(); |
1297 | previousTag.setSourceRange(previousStart, previousPosition-previousStart+1); |
1298 | if (previousTag.fragments().size() > 0) { |
1299 | ASTNode inlineTag = (ASTNode) previousTag.fragments().get(previousTag.fragments().size()-1); |
1300 | if (inlineTag.getNodeType() == ASTNode.TAG_ELEMENT) { |
1301 | int inlineStart = inlineTag.getStartPosition(); |
1302 | inlineTag.setSourceRange(inlineStart, previousPosition-inlineStart+1); |
1303 | } |
1304 | } |
1305 | } |
1306 | } |
1307 | } |
1308 | |
1309 | /* |
1310 | * Add stored tag elements to associated comment. |
1311 | */ |
1312 | @Override |
1313 | protected void updateDocComment() { |
1314 | for (int idx = 0; idx <= this.astPtr; idx++) { |
1315 | this.docComment.tags().add(this.astStack[idx]); |
1316 | } |
1317 | } |
1318 | |
1319 | @Override |
1320 | protected boolean areRegionsClosed() { |
1321 | // do nothing |
1322 | return true; |
1323 | } |
1324 | |
1325 | @Override |
1326 | protected void setRegionPosition(int currentPosition) { |
1327 | // do nothing |
1328 | } |
1329 | } |
1330 |
Members