DocTreeMaker.java revision 3831:209b0eab0e1f
1/* 2 * Copyright (c) 2011, 2016, Oracle and/or its affiliates. All rights reserved. 3 * DO NOT ALTER OR REMOVE COPYRIGHT NOTICES OR THIS FILE HEADER. 4 * 5 * This code is free software; you can redistribute it and/or modify it 6 * under the terms of the GNU General Public License version 2 only, as 7 * published by the Free Software Foundation. Oracle designates this 8 * particular file as subject to the "Classpath" exception as provided 9 * by Oracle in the LICENSE file that accompanied this code. 10 * 11 * This code is distributed in the hope that it will be useful, but WITHOUT 12 * ANY WARRANTY; without even the implied warranty of MERCHANTABILITY or 13 * FITNESS FOR A PARTICULAR PURPOSE. See the GNU General Public License 14 * version 2 for more details (a copy is included in the LICENSE file that 15 * accompanied this code). 16 * 17 * You should have received a copy of the GNU General Public License version 18 * 2 along with this work; if not, write to the Free Software Foundation, 19 * Inc., 51 Franklin St, Fifth Floor, Boston, MA 02110-1301 USA. 20 * 21 * Please contact Oracle, 500 Oracle Parkway, Redwood Shores, CA 94065 USA 22 * or visit www.oracle.com if you need additional information or have any 23 * questions. 24 */ 25 26package com.sun.tools.javac.tree; 27 28import java.text.BreakIterator; 29import java.util.ArrayList; 30import java.util.Collection; 31import java.util.EnumSet; 32import java.util.List; 33import java.util.ListIterator; 34 35import javax.lang.model.element.Name; 36import javax.tools.Diagnostic; 37import javax.tools.JavaFileObject; 38 39import com.sun.source.doctree.AttributeTree.ValueKind; 40import com.sun.source.doctree.DocTree; 41import com.sun.source.doctree.DocTree.Kind; 42import com.sun.source.doctree.EndElementTree; 43import com.sun.source.doctree.IdentifierTree; 44import com.sun.source.doctree.ReferenceTree; 45import com.sun.source.doctree.StartElementTree; 46import com.sun.source.doctree.TextTree; 47import com.sun.source.doctree.ProvidesTree; 48import com.sun.source.doctree.UsesTree; 49import com.sun.source.util.DocTreeFactory; 50import com.sun.tools.doclint.HtmlTag; 51import com.sun.tools.javac.api.JavacTrees; 52import com.sun.tools.javac.parser.ParserFactory; 53import com.sun.tools.javac.parser.ReferenceParser; 54import com.sun.tools.javac.parser.Tokens.Comment; 55import com.sun.tools.javac.parser.Tokens.Comment.CommentStyle; 56import com.sun.tools.javac.tree.DCTree.DCAttribute; 57import com.sun.tools.javac.tree.DCTree.DCAuthor; 58import com.sun.tools.javac.tree.DCTree.DCComment; 59import com.sun.tools.javac.tree.DCTree.DCDeprecated; 60import com.sun.tools.javac.tree.DCTree.DCDocComment; 61import com.sun.tools.javac.tree.DCTree.DCDocRoot; 62import com.sun.tools.javac.tree.DCTree.DCEndElement; 63import com.sun.tools.javac.tree.DCTree.DCEntity; 64import com.sun.tools.javac.tree.DCTree.DCErroneous; 65import com.sun.tools.javac.tree.DCTree.DCHidden; 66import com.sun.tools.javac.tree.DCTree.DCIdentifier; 67import com.sun.tools.javac.tree.DCTree.DCIndex; 68import com.sun.tools.javac.tree.DCTree.DCInheritDoc; 69import com.sun.tools.javac.tree.DCTree.DCLink; 70import com.sun.tools.javac.tree.DCTree.DCLiteral; 71import com.sun.tools.javac.tree.DCTree.DCParam; 72import com.sun.tools.javac.tree.DCTree.DCProvides; 73import com.sun.tools.javac.tree.DCTree.DCReference; 74import com.sun.tools.javac.tree.DCTree.DCReturn; 75import com.sun.tools.javac.tree.DCTree.DCSee; 76import com.sun.tools.javac.tree.DCTree.DCSerial; 77import com.sun.tools.javac.tree.DCTree.DCSerialData; 78import com.sun.tools.javac.tree.DCTree.DCSerialField; 79import com.sun.tools.javac.tree.DCTree.DCSince; 80import com.sun.tools.javac.tree.DCTree.DCStartElement; 81import com.sun.tools.javac.tree.DCTree.DCText; 82import com.sun.tools.javac.tree.DCTree.DCThrows; 83import com.sun.tools.javac.tree.DCTree.DCUnknownBlockTag; 84import com.sun.tools.javac.tree.DCTree.DCUnknownInlineTag; 85import com.sun.tools.javac.tree.DCTree.DCUses; 86import com.sun.tools.javac.tree.DCTree.DCValue; 87import com.sun.tools.javac.tree.DCTree.DCVersion; 88import com.sun.tools.javac.util.Context; 89import com.sun.tools.javac.util.DefinedBy; 90import com.sun.tools.javac.util.DefinedBy.Api; 91import com.sun.tools.javac.util.DiagnosticSource; 92import com.sun.tools.javac.util.JCDiagnostic; 93import com.sun.tools.javac.util.JCDiagnostic.DiagnosticPosition; 94import com.sun.tools.javac.util.ListBuffer; 95import com.sun.tools.javac.util.Pair; 96import com.sun.tools.javac.util.Position; 97 98import static com.sun.tools.doclint.HtmlTag.*; 99 100/** 101 * 102 * <p><b>This is NOT part of any supported API. 103 * If you write code that depends on this, you do so at your own risk. 104 * This code and its internal interfaces are subject to change or 105 * deletion without notice.</b> 106 */ 107public class DocTreeMaker implements DocTreeFactory { 108 109 /** The context key for the tree factory. */ 110 protected static final Context.Key<DocTreeMaker> treeMakerKey = new Context.Key<>(); 111 112 // A subset of block tags, which acts as sentence breakers, appearing 113 // anywhere but the zero'th position in the first sentence. 114 final EnumSet<HtmlTag> sentenceBreakTags; 115 116 /** Get the TreeMaker instance. */ 117 public static DocTreeMaker instance(Context context) { 118 DocTreeMaker instance = context.get(treeMakerKey); 119 if (instance == null) 120 instance = new DocTreeMaker(context); 121 return instance; 122 } 123 124 /** The position at which subsequent trees will be created. 125 */ 126 public int pos = Position.NOPOS; 127 128 /** Access to diag factory for ErroneousTrees. */ 129 private final JCDiagnostic.Factory diags; 130 131 private final JavacTrees trees; 132 133 /** Utility class to parse reference signatures. */ 134 private final ReferenceParser referenceParser; 135 136 /** Create a tree maker with NOPOS as initial position. 137 */ 138 protected DocTreeMaker(Context context) { 139 context.put(treeMakerKey, this); 140 diags = JCDiagnostic.Factory.instance(context); 141 this.pos = Position.NOPOS; 142 trees = JavacTrees.instance(context); 143 referenceParser = new ReferenceParser(ParserFactory.instance(context)); 144 sentenceBreakTags = EnumSet.of(H1, H2, H3, H4, H5, H6, PRE, P); 145 } 146 147 /** Reassign current position. 148 */ 149 @Override @DefinedBy(Api.COMPILER_TREE) 150 public DocTreeMaker at(int pos) { 151 this.pos = pos; 152 return this; 153 } 154 155 /** Reassign current position. 156 */ 157 public DocTreeMaker at(DiagnosticPosition pos) { 158 this.pos = (pos == null ? Position.NOPOS : pos.getStartPosition()); 159 return this; 160 } 161 162 @Override @DefinedBy(Api.COMPILER_TREE) 163 public DCAttribute newAttributeTree(javax.lang.model.element.Name name, ValueKind vkind, java.util.List<? extends DocTree> value) { 164 DCAttribute tree = new DCAttribute(name, vkind, cast(value)); 165 tree.pos = pos; 166 return tree; 167 } 168 169 @Override @DefinedBy(Api.COMPILER_TREE) 170 public DCAuthor newAuthorTree(java.util.List<? extends DocTree> name) { 171 DCAuthor tree = new DCAuthor(cast(name)); 172 tree.pos = pos; 173 return tree; 174 } 175 176 @Override @DefinedBy(Api.COMPILER_TREE) 177 public DCLiteral newCodeTree(TextTree text) { 178 DCLiteral tree = new DCLiteral(Kind.CODE, (DCText) text); 179 tree.pos = pos; 180 return tree; 181 } 182 183 @Override @DefinedBy(Api.COMPILER_TREE) 184 public DCComment newCommentTree(String text) { 185 DCComment tree = new DCComment(text); 186 tree.pos = pos; 187 return tree; 188 } 189 190 @Override @DefinedBy(Api.COMPILER_TREE) 191 public DCDeprecated newDeprecatedTree(List<? extends DocTree> text) { 192 DCDeprecated tree = new DCDeprecated(cast(text)); 193 tree.pos = pos; 194 return tree; 195 } 196 197 public DCDocComment newDocCommentTree(Comment comment, List<? extends DocTree> fullBody, List<? extends DocTree> tags) { 198 Pair<List<DCTree>, List<DCTree>> pair = splitBody(fullBody); 199 DCDocComment tree = new DCDocComment(comment, cast(fullBody), pair.fst, pair.snd, cast(tags)); 200 tree.pos = pos; 201 return tree; 202 } 203 204 /* 205 * Primarily to produce a DocCommenTree when given a 206 * first sentence and a body, this is useful, in cases 207 * where the trees are being synthesized by a tool. 208 */ 209 @Override @DefinedBy(Api.COMPILER_TREE) 210 public DCDocComment newDocCommentTree(List<? extends DocTree> fullBody, List<? extends DocTree> tags) { 211 ListBuffer<DCTree> lb = new ListBuffer<>(); 212 lb.addAll(cast(fullBody)); 213 List<DCTree> fBody = lb.toList(); 214 215 // A dummy comment to keep the diagnostics logic happy. 216 Comment c = new Comment() { 217 @Override 218 public String getText() { 219 return null; 220 } 221 222 @Override 223 public int getSourcePos(int index) { 224 return Position.NOPOS; 225 } 226 227 @Override 228 public CommentStyle getStyle() { 229 return CommentStyle.JAVADOC; 230 } 231 232 @Override 233 public boolean isDeprecated() { 234 return false; 235 } 236 }; 237 Pair<List<DCTree>, List<DCTree>> pair = splitBody(fullBody); 238 DCDocComment tree = new DCDocComment(c, fBody, pair.fst, pair.snd, cast(tags)); 239 return tree; 240 } 241 242 @Override @DefinedBy(Api.COMPILER_TREE) 243 public DCDocRoot newDocRootTree() { 244 DCDocRoot tree = new DCDocRoot(); 245 tree.pos = pos; 246 return tree; 247 } 248 249 @Override @DefinedBy(Api.COMPILER_TREE) 250 public DCEndElement newEndElementTree(Name name) { 251 DCEndElement tree = new DCEndElement(name); 252 tree.pos = pos; 253 return tree; 254 } 255 256 @Override @DefinedBy(Api.COMPILER_TREE) 257 public DCEntity newEntityTree(Name name) { 258 DCEntity tree = new DCEntity(name); 259 tree.pos = pos; 260 return tree; 261 } 262 263 @Override @DefinedBy(Api.COMPILER_TREE) 264 public DCErroneous newErroneousTree(String text, Diagnostic<JavaFileObject> diag) { 265 DCErroneous tree = new DCErroneous(text, (JCDiagnostic) diag); 266 tree.pos = pos; 267 return tree; 268 } 269 270 public DCErroneous newErroneousTree(String text, DiagnosticSource diagSource, String code, Object... args) { 271 DCErroneous tree = new DCErroneous(text, diags, diagSource, code, args); 272 tree.pos = pos; 273 return tree; 274 } 275 276 @Override @DefinedBy(Api.COMPILER_TREE) 277 public DCThrows newExceptionTree(ReferenceTree name, List<? extends DocTree> description) { 278 // TODO: verify the reference is just to a type (not a field or method) 279 DCThrows tree = new DCThrows(Kind.EXCEPTION, (DCReference) name, cast(description)); 280 tree.pos = pos; 281 return tree; 282 } 283 284 @Override @DefinedBy(Api.COMPILER_TREE) 285 public DCHidden newHiddenTree(List<? extends DocTree> text) { 286 DCHidden tree = new DCHidden(cast(text)); 287 tree.pos = pos; 288 return tree; 289 } 290 291 @Override @DefinedBy(Api.COMPILER_TREE) 292 public DCIdentifier newIdentifierTree(Name name) { 293 DCIdentifier tree = new DCIdentifier(name); 294 tree.pos = pos; 295 return tree; 296 } 297 298 @Override @DefinedBy(Api.COMPILER_TREE) 299 public DCIndex newIndexTree(DocTree term, List<? extends DocTree> description) { 300 DCIndex tree = new DCIndex((DCTree) term, cast(description)); 301 tree.pos = pos; 302 return tree; 303 } 304 305 @Override @DefinedBy(Api.COMPILER_TREE) 306 public DCInheritDoc newInheritDocTree() { 307 DCInheritDoc tree = new DCInheritDoc(); 308 tree.pos = pos; 309 return tree; 310 } 311 312 @Override @DefinedBy(Api.COMPILER_TREE) 313 public DCLink newLinkTree(ReferenceTree ref, List<? extends DocTree> label) { 314 DCLink tree = new DCLink(Kind.LINK, (DCReference) ref, cast(label)); 315 tree.pos = pos; 316 return tree; 317 } 318 319 @Override @DefinedBy(Api.COMPILER_TREE) 320 public DCLink newLinkPlainTree(ReferenceTree ref, List<? extends DocTree> label) { 321 DCLink tree = new DCLink(Kind.LINK_PLAIN, (DCReference) ref, cast(label)); 322 tree.pos = pos; 323 return tree; 324 } 325 326 @Override @DefinedBy(Api.COMPILER_TREE) 327 public DCLiteral newLiteralTree(TextTree text) { 328 DCLiteral tree = new DCLiteral(Kind.LITERAL, (DCText) text); 329 tree.pos = pos; 330 return tree; 331 } 332 333 @Override @DefinedBy(Api.COMPILER_TREE) 334 public DCParam newParamTree(boolean isTypeParameter, IdentifierTree name, List<? extends DocTree> description) { 335 DCParam tree = new DCParam(isTypeParameter, (DCIdentifier) name, cast(description)); 336 tree.pos = pos; 337 return tree; 338 } 339 340 @Override @DefinedBy(Api.COMPILER_TREE) 341 public DCProvides newProvidesTree(ReferenceTree name, List<? extends DocTree> description) { 342 DCProvides tree = new DCProvides((DCReference) name, cast(description)); 343 tree.pos = pos; 344 return tree; 345 } 346 347 @Override @DefinedBy(Api.COMPILER_TREE) 348 public DCReference newReferenceTree(String signature) { 349 try { 350 ReferenceParser.Reference ref = referenceParser.parse(signature); 351 DCReference tree = new DCReference(signature, ref.qualExpr, ref.member, ref.paramTypes); 352 tree.pos = pos; 353 return tree; 354 } catch (ReferenceParser.ParseException e) { 355 throw new IllegalArgumentException("invalid signature", e); 356 } 357 } 358 359 public DCReference newReferenceTree(String signature, JCTree qualExpr, Name member, List<JCTree> paramTypes) { 360 DCReference tree = new DCReference(signature, qualExpr, member, paramTypes); 361 tree.pos = pos; 362 return tree; 363 } 364 365 @Override @DefinedBy(Api.COMPILER_TREE) 366 public DCReturn newReturnTree(List<? extends DocTree> description) { 367 DCReturn tree = new DCReturn(cast(description)); 368 tree.pos = pos; 369 return tree; 370 } 371 372 @Override @DefinedBy(Api.COMPILER_TREE) 373 public DCSee newSeeTree(List<? extends DocTree> reference) { 374 DCSee tree = new DCSee(cast(reference)); 375 tree.pos = pos; 376 return tree; 377 } 378 379 @Override @DefinedBy(Api.COMPILER_TREE) 380 public DCSerial newSerialTree(List<? extends DocTree> description) { 381 DCSerial tree = new DCSerial(cast(description)); 382 tree.pos = pos; 383 return tree; 384 } 385 386 @Override @DefinedBy(Api.COMPILER_TREE) 387 public DCSerialData newSerialDataTree(List<? extends DocTree> description) { 388 DCSerialData tree = new DCSerialData(cast(description)); 389 tree.pos = pos; 390 return tree; 391 } 392 393 @Override @DefinedBy(Api.COMPILER_TREE) 394 public DCSerialField newSerialFieldTree(IdentifierTree name, ReferenceTree type, List<? extends DocTree> description) { 395 DCSerialField tree = new DCSerialField((DCIdentifier) name, (DCReference) type, cast(description)); 396 tree.pos = pos; 397 return tree; 398 } 399 400 @Override @DefinedBy(Api.COMPILER_TREE) 401 public DCSince newSinceTree(List<? extends DocTree> text) { 402 DCSince tree = new DCSince(cast(text)); 403 tree.pos = pos; 404 return tree; 405 } 406 407 @Override @DefinedBy(Api.COMPILER_TREE) 408 public DCStartElement newStartElementTree(Name name, List<? extends DocTree> attrs, boolean selfClosing) { 409 DCStartElement tree = new DCStartElement(name, cast(attrs), selfClosing); 410 tree.pos = pos; 411 return tree; 412 } 413 414 @Override @DefinedBy(Api.COMPILER_TREE) 415 public DCText newTextTree(String text) { 416 DCText tree = new DCText(text); 417 tree.pos = pos; 418 return tree; 419 } 420 421 @Override @DefinedBy(Api.COMPILER_TREE) 422 public DCThrows newThrowsTree(ReferenceTree name, List<? extends DocTree> description) { 423 // TODO: verify the reference is just to a type (not a field or method) 424 DCThrows tree = new DCThrows(Kind.THROWS, (DCReference) name, cast(description)); 425 tree.pos = pos; 426 return tree; 427 } 428 429 @Override @DefinedBy(Api.COMPILER_TREE) 430 public DCUnknownBlockTag newUnknownBlockTagTree(Name name, List<? extends DocTree> content) { 431 DCUnknownBlockTag tree = new DCUnknownBlockTag(name, cast(content)); 432 tree.pos = pos; 433 return tree; 434 } 435 436 @Override @DefinedBy(Api.COMPILER_TREE) 437 public DCUnknownInlineTag newUnknownInlineTagTree(Name name, List<? extends DocTree> content) { 438 DCUnknownInlineTag tree = new DCUnknownInlineTag(name, cast(content)); 439 tree.pos = pos; 440 return tree; 441 } 442 443 @Override @DefinedBy(Api.COMPILER_TREE) 444 public DCUses newUsesTree(ReferenceTree name, List<? extends DocTree> description) { 445 DCUses tree = new DCUses((DCReference) name, cast(description)); 446 tree.pos = pos; 447 return tree; 448 } 449 450 @Override @DefinedBy(Api.COMPILER_TREE) 451 public DCValue newValueTree(ReferenceTree ref) { 452 // TODO: verify the reference is to a constant value 453 DCValue tree = new DCValue((DCReference) ref); 454 tree.pos = pos; 455 return tree; 456 } 457 458 @Override @DefinedBy(Api.COMPILER_TREE) 459 public DCVersion newVersionTree(List<? extends DocTree> text) { 460 DCVersion tree = new DCVersion(cast(text)); 461 tree.pos = pos; 462 return tree; 463 } 464 465 @Override @DefinedBy(Api.COMPILER_TREE) 466 public java.util.List<DocTree> getFirstSentence(java.util.List<? extends DocTree> list) { 467 Pair<List<DCTree>, List<DCTree>> pair = splitBody(list); 468 return new ArrayList<>(pair.fst); 469 } 470 471 /* 472 * Breaks up the body tags into the first sentence and its successors. 473 * The first sentence is determined with the presence of a period, 474 * block tag, or a sentence break, as returned by the BreakIterator. 475 * Trailing whitespaces are trimmed. 476 */ 477 private Pair<List<DCTree>, List<DCTree>> splitBody(Collection<? extends DocTree> list) { 478 // pos is modified as we create trees, therefore 479 // we save the pos and restore it later. 480 final int savedpos = this.pos; 481 try { 482 ListBuffer<DCTree> body = new ListBuffer<>(); 483 // split body into first sentence and body 484 ListBuffer<DCTree> fs = new ListBuffer<>(); 485 if (list.isEmpty()) { 486 return new Pair<>(fs.toList(), body.toList()); 487 } 488 boolean foundFirstSentence = false; 489 ArrayList<DocTree> alist = new ArrayList<>(list); 490 ListIterator<DocTree> itr = alist.listIterator(); 491 while (itr.hasNext()) { 492 boolean isFirst = !itr.hasPrevious(); 493 DocTree dt = itr.next(); 494 int spos = ((DCTree) dt).pos; 495 if (foundFirstSentence) { 496 body.add((DCTree) dt); 497 continue; 498 } 499 switch (dt.getKind()) { 500 case TEXT: 501 DCText tt = (DCText) dt; 502 String s = tt.getBody(); 503 DocTree peekedNext = itr.hasNext() 504 ? alist.get(itr.nextIndex()) 505 : null; 506 int sbreak = getSentenceBreak(s, peekedNext); 507 if (sbreak > 0) { 508 s = removeTrailingWhitespace(s.substring(0, sbreak)); 509 DCText text = this.at(spos).newTextTree(s); 510 fs.add(text); 511 foundFirstSentence = true; 512 int nwPos = skipWhiteSpace(tt.getBody(), sbreak); 513 if (nwPos > 0) { 514 DCText text2 = this.at(spos + nwPos).newTextTree(tt.getBody().substring(nwPos)); 515 body.add(text2); 516 } 517 continue; 518 } else if (itr.hasNext()) { 519 // if the next doctree is a break, remove trailing spaces 520 peekedNext = alist.get(itr.nextIndex()); 521 boolean sbrk = isSentenceBreak(peekedNext, false); 522 if (sbrk) { 523 DocTree next = itr.next(); 524 s = removeTrailingWhitespace(s); 525 DCText text = this.at(spos).newTextTree(s); 526 fs.add(text); 527 body.add((DCTree) next); 528 foundFirstSentence = true; 529 continue; 530 } 531 } 532 break; 533 default: 534 if (isSentenceBreak(dt, isFirst)) { 535 body.add((DCTree) dt); 536 foundFirstSentence = true; 537 continue; 538 } 539 break; 540 } 541 fs.add((DCTree) dt); 542 } 543 return new Pair<>(fs.toList(), body.toList()); 544 } finally { 545 this.pos = savedpos; 546 } 547 } 548 549 private boolean isTextTree(DocTree tree) { 550 return tree.getKind() == Kind.TEXT; 551 } 552 553 /* 554 * Computes the first sentence break, a simple dot-space algorithm. 555 */ 556 private int defaultSentenceBreak(String s) { 557 // scan for period followed by whitespace 558 int period = -1; 559 for (int i = 0; i < s.length(); i++) { 560 switch (s.charAt(i)) { 561 case '.': 562 period = i; 563 break; 564 565 case ' ': 566 case '\f': 567 case '\n': 568 case '\r': 569 case '\t': 570 if (period >= 0) { 571 return i; 572 } 573 break; 574 575 default: 576 period = -1; 577 break; 578 } 579 } 580 return -1; 581 } 582 583 /* 584 * Computes the first sentence, if using a default breaker, 585 * the break is returned, if not then a -1, indicating that 586 * more doctree elements are required to be examined. 587 * 588 * BreakIterator.next points to the the start of the following sentence, 589 * and does not provide an easy way to disambiguate between "sentence break", 590 * "possible sentence break" and "not a sentence break" at the end of the input. 591 * For example, BreakIterator.next returns the index for the end 592 * of the string for all of these examples, 593 * using vertical bars to delimit the bounds of the example text 594 * |Abc| (not a valid end of sentence break, if followed by more text) 595 * |Abc.| (maybe a valid end of sentence break, depending on the following text) 596 * |Abc. | (maybe a valid end of sentence break, depending on the following text) 597 * |"Abc." | (maybe a valid end of sentence break, depending on the following text) 598 * |Abc. | (definitely a valid end of sentence break) 599 * |"Abc." | (definitely a valid end of sentence break) 600 * Therefore, we have to probe further to determine whether 601 * there really is a sentence break or not at the end of this run of text. 602 */ 603 private int getSentenceBreak(String s, DocTree dt) { 604 BreakIterator breakIterator = trees.getBreakIterator(); 605 if (breakIterator == null) { 606 return defaultSentenceBreak(s); 607 } 608 breakIterator.setText(s); 609 final int sbrk = breakIterator.next(); 610 // This is the last doctree, found the droid we are looking for 611 if (dt == null) { 612 return sbrk; 613 } 614 615 // If the break is well within the span of the string ie. not 616 // at EOL, then we have a clear break. 617 if (sbrk < s.length() - 1) { 618 return sbrk; 619 } 620 621 if (isTextTree(dt)) { 622 // Two adjacent text trees, a corner case, perhaps 623 // produced by a tool synthesizing a doctree. In 624 // this case, does the break lie within the first span, 625 // then we have the droid, otherwise allow the callers 626 // logic to handle the break in the adjacent doctree. 627 TextTree ttnext = (TextTree) dt; 628 String combined = s + ttnext.getBody(); 629 breakIterator.setText(combined); 630 int sbrk2 = breakIterator.next(); 631 if (sbrk < sbrk2) { 632 return sbrk; 633 } 634 } 635 636 // Is the adjacent tree a sentence breaker ? 637 if (isSentenceBreak(dt, false)) { 638 return sbrk; 639 } 640 641 // At this point the adjacent tree is either a javadoc tag ({@..), 642 // html tag (<..) or an entity (&..). Perform a litmus test, by 643 // concatenating a sentence, to validate the break earlier identified. 644 String combined = s + "Dummy Sentence."; 645 breakIterator.setText(combined); 646 int sbrk2 = breakIterator.next(); 647 if (sbrk2 <= sbrk) { 648 return sbrk2; 649 } 650 return -1; // indeterminate at this time 651 } 652 653 private boolean isSentenceBreak(javax.lang.model.element.Name tagName) { 654 return sentenceBreakTags.contains(get(tagName)); 655 } 656 657 private boolean isSentenceBreak(DocTree dt, boolean isFirstDocTree) { 658 switch (dt.getKind()) { 659 case START_ELEMENT: 660 StartElementTree set = (StartElementTree)dt; 661 return !isFirstDocTree && ((DCTree) dt).pos > 1 && isSentenceBreak(set.getName()); 662 case END_ELEMENT: 663 EndElementTree eet = (EndElementTree)dt; 664 return !isFirstDocTree && ((DCTree) dt).pos > 1 && isSentenceBreak(eet.getName()); 665 default: 666 return false; 667 } 668 } 669 670 /* 671 * Returns the position of the the first non-white space 672 */ 673 private int skipWhiteSpace(String s, int start) { 674 for (int i = start; i < s.length(); i++) { 675 char c = s.charAt(i); 676 if (!Character.isWhitespace(c)) { 677 return i; 678 } 679 } 680 return -1; 681 } 682 683 private String removeTrailingWhitespace(String s) { 684 for (int i = s.length() - 1 ; i >= 0 ; i--) { 685 char ch = s.charAt(i); 686 if (!Character.isWhitespace(ch)) { 687 return s.substring(0, i + 1); 688 } 689 } 690 return s; 691 } 692 693 @SuppressWarnings("unchecked") 694 private List<DCTree> cast(List<? extends DocTree> list) { 695 return (List<DCTree>) list; 696 } 697} 698