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