1 /*
   2  * Copyright (c) 2015, 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 
  26 package jdk.javadoc.internal.doclets.toolkit.util;
  27 
  28 import java.util.ArrayList;
  29 import java.util.Collections;
  30 import java.util.List;
  31 
  32 import javax.lang.model.element.Element;
  33 import javax.lang.model.element.ExecutableElement;
  34 import javax.lang.model.element.PackageElement;
  35 import javax.lang.model.element.TypeElement;
  36 import javax.lang.model.type.TypeMirror;
  37 
  38 import com.sun.source.doctree.AttributeTree;
  39 import com.sun.source.doctree.AttributeTree.ValueKind;
  40 import com.sun.source.doctree.AuthorTree;
  41 import com.sun.source.doctree.BlockTagTree;
  42 import com.sun.source.doctree.CommentTree;
  43 import com.sun.source.doctree.DeprecatedTree;
  44 import com.sun.source.doctree.DocCommentTree;
  45 import com.sun.source.doctree.DocTree;
  46 import com.sun.source.doctree.EndElementTree;
  47 import com.sun.source.doctree.EntityTree;
  48 import com.sun.source.doctree.IdentifierTree;
  49 import com.sun.source.doctree.InlineTagTree;
  50 import com.sun.source.doctree.LinkTree;
  51 import com.sun.source.doctree.LiteralTree;
  52 import com.sun.source.doctree.ParamTree;
  53 import com.sun.source.doctree.ReferenceTree;
  54 import com.sun.source.doctree.ReturnTree;
  55 import com.sun.source.doctree.SeeTree;
  56 import com.sun.source.doctree.SerialDataTree;
  57 import com.sun.source.doctree.SerialFieldTree;
  58 import com.sun.source.doctree.SerialTree;
  59 import com.sun.source.doctree.SinceTree;
  60 import com.sun.source.doctree.StartElementTree;
  61 import com.sun.source.doctree.TextTree;
  62 import com.sun.source.doctree.ThrowsTree;
  63 import com.sun.source.doctree.UnknownBlockTagTree;
  64 import com.sun.source.doctree.ValueTree;
  65 import com.sun.source.doctree.VersionTree;
  66 import com.sun.source.util.DocTreePath;
  67 import com.sun.source.util.DocTrees;
  68 import com.sun.source.util.SimpleDocTreeVisitor;
  69 import com.sun.source.util.TreePath;
  70 import com.sun.tools.javac.util.DefinedBy;
  71 import com.sun.tools.javac.util.DefinedBy.Api;
  72 import jdk.javadoc.internal.doclets.toolkit.Configuration;
  73 
  74 import static com.sun.source.doctree.DocTree.Kind.*;
  75 
  76 public class CommentHelper {
  77     public final TreePath path;
  78     public final DocCommentTree dctree;
  79     public final Element element;
  80     private Element overriddenElement;
  81 
  82     public static final String SPACER = " ";
  83 
  84     public CommentHelper(Configuration configuration, Element element, TreePath path, DocCommentTree dctree) {
  85         //this.configuration = configuration;
  86         this.element = element;
  87         this.path = path;
  88         this.dctree = dctree;
  89     }
  90 
  91     public void setOverrideElement(Element ove) {
  92         if (this.element == ove) {
  93             throw new AssertionError("cannot set given element as overriden element");
  94         }
  95         overriddenElement = ove;
  96     }
  97 
  98     @SuppressWarnings("fallthrough")
  99     public String getTagName(DocTree dtree) {
 100         switch (dtree.getKind()) {
 101             case AUTHOR:
 102             case DEPRECATED:
 103             case PARAM:
 104             case RETURN:
 105             case SEE:
 106             case SERIAL_DATA:
 107             case SERIAL_FIELD:
 108             case THROWS:
 109             case UNKNOWN_BLOCK_TAG:
 110             case VERSION:
 111                 return ((BlockTagTree)dtree).getTagName();
 112             case UNKNOWN_INLINE_TAG:
 113                 return ((InlineTagTree)dtree).getTagName();
 114             case ERRONEOUS:
 115                 return "erroneous";
 116             default:
 117                 return dtree.getKind().tagName;
 118         }
 119     }
 120 
 121     public boolean isTypeParameter(DocTree dtree) {
 122         if (dtree.getKind() == PARAM) {
 123             return ((ParamTree)dtree).isTypeParameter();
 124         }
 125         return false;
 126     }
 127 
 128     public String getParameterName(DocTree dtree) {
 129         if (dtree.getKind() == PARAM) {
 130             return ((ParamTree) dtree).getName().toString();
 131         } else {
 132             return null;
 133         }
 134     }
 135 
 136     Element getElement(Configuration c, ReferenceTree rtree) {
 137         // likely a synthesized tree
 138         if (path == null) {
 139             TypeMirror symbol = c.utils.getSymbol(rtree.getSignature());
 140             if (symbol == null) {
 141                 return null;
 142             }
 143             return  c.root.getTypeUtils().asElement(symbol);
 144         }
 145         // case A: the element contains no comments associated and
 146         // the comments need to be copied from ancestor
 147         // case B: the element has @inheritDoc, then the ancestral comment
 148         // as appropriate has to be copied over.
 149 
 150         // Case A.
 151         if (dctree == null && overriddenElement != null) {
 152             CommentHelper ovch = c.utils.getCommentHelper(overriddenElement);
 153             return ovch.getElement(c, rtree);
 154         }
 155         if (dctree == null) {
 156             return null;
 157         }
 158         DocTreePath docTreePath = DocTreePath.getPath(path, dctree, rtree);
 159         if (docTreePath == null) {
 160             // Case B.
 161             if (overriddenElement != null) {
 162                 CommentHelper ovch = c.utils.getCommentHelper(overriddenElement);
 163                 return ovch.getElement(c, rtree);
 164             }
 165             return null;
 166         }
 167         DocTrees doctrees = c.root.getDocTrees();
 168         return doctrees.getElement(docTreePath);
 169     }
 170 
 171     public Element getException(Configuration c, DocTree dtree) {
 172         if (dtree.getKind() == THROWS || dtree.getKind() == EXCEPTION) {
 173             ThrowsTree tt = (ThrowsTree)dtree;
 174             ReferenceTree exceptionName = tt.getExceptionName();
 175             return getElement(c, exceptionName);
 176         }
 177         return null;
 178     }
 179 
 180     public List<? extends DocTree> getDescription(Configuration c, DocTree dtree) {
 181         return getTags(c, dtree);
 182     }
 183 
 184     public String getText(List<? extends DocTree> list) {
 185         StringBuilder sb = new StringBuilder();
 186         for (DocTree dt : list) {
 187             sb.append(getText0(dt));
 188         }
 189         return sb.toString();
 190     }
 191 
 192     public String getText(DocTree dt) {
 193         return getText0(dt).toString();
 194     }
 195 
 196     private StringBuilder getText0(DocTree dt) {
 197         final StringBuilder sb = new StringBuilder();
 198         new SimpleDocTreeVisitor<Void, Void>() {
 199             @Override @DefinedBy(Api.COMPILER_TREE)
 200             public Void visitAttribute(AttributeTree node, Void p) {
 201                 sb.append(SPACER).append(node.getName());
 202                 if (node.getValueKind() == ValueKind.EMPTY) {
 203                     return null;
 204                 }
 205 
 206                 sb.append("=");
 207                 String quote;
 208                 switch (node.getValueKind()) {
 209                     case DOUBLE:
 210                         quote = "\"";
 211                         break;
 212                     case SINGLE:
 213                         quote = "\'";
 214                         break;
 215                     default:
 216                         quote = "";
 217                         break;
 218                 }
 219                 sb.append(quote);
 220                 node.getValue().stream().forEach((dt) -> {
 221                     dt.accept(this, null);
 222                 });
 223                 sb.append(quote);
 224                 return null;
 225             }
 226 
 227             @Override @DefinedBy(Api.COMPILER_TREE)
 228             public Void visitEndElement(EndElementTree node, Void p) {
 229                 sb.append("</")
 230                         .append(node.getName())
 231                         .append(">");
 232                 return null;
 233             }
 234 
 235             @Override @DefinedBy(Api.COMPILER_TREE)
 236             public Void visitEntity(EntityTree node, Void p) {
 237                 sb.append(node.toString());
 238                 return null;
 239             }
 240 
 241             @Override @DefinedBy(Api.COMPILER_TREE)
 242             public Void visitLink(LinkTree node, Void p) {
 243                 if (node.getReference() == null) {
 244                     return null;
 245                 }
 246 
 247                 node.getReference().accept(this, null);
 248                 node.getLabel().stream().forEach((dt) -> {
 249                     dt.accept(this, null);
 250                 });
 251                 return null;
 252             }
 253 
 254             @Override @DefinedBy(Api.COMPILER_TREE)
 255             public Void visitLiteral(LiteralTree node, Void p) {
 256                 if (node.getKind() == CODE) {
 257                     sb.append("<").append(node.getKind().tagName).append(">");
 258                 }
 259                 sb.append(node.getBody().toString());
 260                 if (node.getKind() == CODE) {
 261                     sb.append("</").append(node.getKind().tagName).append(">");
 262                 }
 263                 return null;
 264             }
 265 
 266             @Override @DefinedBy(Api.COMPILER_TREE)
 267             public Void visitReference(ReferenceTree node, Void p) {
 268                 sb.append(node.getSignature());
 269                 return null;
 270             }
 271 
 272             @Override @DefinedBy(Api.COMPILER_TREE)
 273             public Void visitSee(SeeTree node, Void p) {
 274                 node.getReference().stream().forEach((dt) -> {
 275                     dt.accept(this, null);
 276                 });
 277                 return null;
 278             }
 279 
 280             @Override @DefinedBy(Api.COMPILER_TREE)
 281             public Void visitSerial(SerialTree node, Void p) {
 282                 node.getDescription().stream().forEach((dt) -> {
 283                     dt.accept(this, null);
 284                 });
 285                 return null;
 286             }
 287 
 288             @Override @DefinedBy(Api.COMPILER_TREE)
 289             public Void visitStartElement(StartElementTree node, Void p) {
 290                 sb.append("<");
 291                 sb.append(node.getName());
 292                 node.getAttributes().stream().forEach((dt) -> {
 293                     dt.accept(this, null);
 294                 });
 295                 sb.append((node.isSelfClosing() ? "/>" : ">"));
 296                 return null;
 297             }
 298 
 299             @Override @DefinedBy(Api.COMPILER_TREE)
 300             public Void visitText(TextTree node, Void p) {
 301                 sb.append(node.getBody());
 302                 return null;
 303             }
 304 
 305             @Override @DefinedBy(Api.COMPILER_TREE)
 306             public Void visitUnknownBlockTag(UnknownBlockTagTree node, Void p) {
 307                 node.getContent().stream().forEach((dt) -> {
 308                     dt.accept(this, null);
 309                 });
 310                 return null;
 311             }
 312 
 313             @Override @DefinedBy(Api.COMPILER_TREE)
 314             public Void visitValue(ValueTree node, Void p) {
 315                 return node.getReference().accept(this, null);
 316             }
 317 
 318             @Override @DefinedBy(Api.COMPILER_TREE)
 319             protected Void defaultAction(DocTree node, Void p) {
 320                 sb.append(node.toString());
 321                 return null;
 322             }
 323         }.visit(dt, null);
 324         return sb;
 325     }
 326 
 327     public String getLabel(Configuration c, DocTree dtree) {
 328         return new SimpleDocTreeVisitor<String, Void>() {
 329             @Override @DefinedBy(Api.COMPILER_TREE)
 330             public String visitLink(LinkTree node, Void p) {
 331                 StringBuilder sb = new StringBuilder();
 332                 node.getLabel().stream().forEach((dt) -> {
 333                     sb.append(getText(dt));
 334                 });
 335                 return sb.toString();
 336             }
 337 
 338             @Override @DefinedBy(Api.COMPILER_TREE)
 339             public String visitSee(SeeTree node, Void p) {
 340                 StringBuilder sb = new StringBuilder();
 341                 node.getReference().stream().filter((dt) -> (c.utils.isText(dt))).forEach((dt) -> {
 342                     sb.append(((TextTree)dt).getBody());
 343                 });
 344                 return sb.toString();
 345             }
 346 
 347             @Override @DefinedBy(Api.COMPILER_TREE)
 348             protected String defaultAction(DocTree node, Void p) {
 349                 return "";
 350             }
 351         }.visit(dtree, null);
 352     }
 353 
 354     public TypeElement getReferencedClass(Configuration c, DocTree dtree) {
 355         Element e = getReferencedElement(c, dtree);
 356         if (e == null) {
 357             return null;
 358         } else if (c.utils.isTypeElement(e)) {
 359             return (TypeElement) e;
 360         } else if (!c.utils.isPackage(e)) {
 361             return c.utils.getEnclosingTypeElement(e);
 362         }
 363         return null;
 364     }
 365 
 366     public String getReferencedClassName(Configuration c, DocTree dtree) {
 367         Element e = getReferencedClass(c, dtree);
 368         if (e != null) {
 369             return c.utils.isTypeElement(e) ? c.utils.getSimpleName(e) : null;
 370         }
 371         String s = getReferencedSignature(dtree);
 372         if (s == null) {
 373             return null;
 374         }
 375         int n = s.indexOf("#");
 376         return (n == -1) ? s : s.substring(0, n);
 377     }
 378 
 379     public Element getReferencedMember(Configuration c, DocTree dtree) {
 380         Element e = getReferencedElement(c, dtree);
 381         if (e == null) {
 382             return null;
 383         }
 384         return (c.utils.isExecutableElement(e) || c.utils.isVariableElement(e)) ? e : null;
 385     }
 386 
 387     public String getReferencedMemberName(DocTree dtree) {
 388         String s = getReferencedSignature(dtree);
 389         if (s == null) {
 390             return null;
 391         }
 392         int n = s.indexOf("#");
 393         return (n == -1) ? null : s.substring(n + 1);
 394     }
 395 
 396     public String getReferencedMemberName(Configuration c, Element e) {
 397         if (e == null) {
 398             return null;
 399         }
 400         return c.utils.isExecutableElement(e)
 401                 ? c.utils.getSimpleName(e) + c.utils.makeSignature((ExecutableElement) e, true, true)
 402                 : c.utils.getSimpleName(e);
 403     }
 404 
 405     public PackageElement getReferencedPackage(Configuration c, DocTree dtree) {
 406         Element e = getReferencedElement(c, dtree);
 407         if (e != null) {
 408             return c.utils.containingPackage(e);
 409         }
 410         return null;
 411     }
 412 
 413     public List<? extends DocTree> getFirstSentenceTrees(Configuration c, List<? extends DocTree> body) {
 414         List<DocTree> firstSentence = c.root.getDocTrees().getFirstSentence(body);
 415         return firstSentence;
 416     }
 417 
 418     public List<? extends DocTree> getFirstSentenceTrees(Configuration c, DocTree dtree) {
 419         return getFirstSentenceTrees(c, getBody(c, dtree));
 420     }
 421 
 422     private Element getReferencedElement(Configuration c, DocTree dtree) {
 423         return new SimpleDocTreeVisitor<Element, Void>() {
 424             @Override @DefinedBy(Api.COMPILER_TREE)
 425             public Element visitSee(SeeTree node, Void p) {
 426                 for (DocTree dt : node.getReference()) {
 427                     return visit(dt, null);
 428                 }
 429                 return null;
 430             }
 431 
 432             @Override @DefinedBy(Api.COMPILER_TREE)
 433             public Element visitLink(LinkTree node, Void p) {
 434                 return visit(node.getReference(), null);
 435             }
 436 
 437             @Override @DefinedBy(Api.COMPILER_TREE)
 438             public Element visitValue(ValueTree node, Void p) {
 439                 return visit(node.getReference(), null);
 440             }
 441 
 442             @Override @DefinedBy(Api.COMPILER_TREE)
 443             public Element visitReference(ReferenceTree node, Void p) {
 444                 return getElement(c, node);
 445             }
 446 
 447             @Override @DefinedBy(Api.COMPILER_TREE)
 448             public Element visitSerialField(SerialFieldTree node, Void p) {
 449                 return visit(node.getType(), null);
 450             }
 451 
 452             @Override @DefinedBy(Api.COMPILER_TREE)
 453             protected Element defaultAction(DocTree node, Void p) {
 454                return null;
 455             }
 456         }.visit(dtree, null);
 457     }
 458 
 459     public  String getReferencedSignature(DocTree dtree) {
 460         return new SimpleDocTreeVisitor<String, Void>() {
 461             @Override @DefinedBy(Api.COMPILER_TREE)
 462             public String visitSee(SeeTree node, Void p) {
 463                 for (DocTree dt : node.getReference()) {
 464                     return visit(dt, null);
 465                 }
 466                 return null;
 467             }
 468 
 469             @Override @DefinedBy(Api.COMPILER_TREE)
 470             public String visitLink(LinkTree node, Void p) {
 471                 return visit(node.getReference(), null);
 472             }
 473 
 474             @Override @DefinedBy(Api.COMPILER_TREE)
 475             public String visitValue(ValueTree node, Void p) {
 476                 return visit(node.getReference(), null);
 477             }
 478 
 479             @Override @DefinedBy(Api.COMPILER_TREE)
 480             public String visitReference(ReferenceTree node, Void p) {
 481                 return node.getSignature();
 482             }
 483 
 484             @Override @DefinedBy(Api.COMPILER_TREE)
 485             public String visitSerialField(SerialFieldTree node, Void p) {
 486                 return visit(node.getType(), null);
 487             }
 488 
 489             @Override @DefinedBy(Api.COMPILER_TREE)
 490             protected String defaultAction(DocTree node, Void p) {
 491                return null;
 492             }
 493         }.visit(dtree, null);
 494     }
 495 
 496     public List<? extends DocTree> getReference(DocTree dtree) {
 497         return dtree.getKind() == SEE ? ((SeeTree)dtree).getReference() : null;
 498     }
 499 
 500     public ReferenceTree getExceptionName(DocTree dtree) {
 501         return (dtree.getKind() == THROWS || dtree.getKind() == EXCEPTION)
 502                 ? ((ThrowsTree)dtree).getExceptionName()
 503                 : null;
 504     }
 505 
 506     public IdentifierTree getName(DocTree dtree) {
 507         switch (dtree.getKind()) {
 508             case PARAM:
 509                 return ((ParamTree)dtree).getName();
 510             case SERIAL_FIELD:
 511                 return ((SerialFieldTree)dtree).getName();
 512             default:
 513                 return null;
 514             }
 515     }
 516 
 517     public List<? extends DocTree> getTags(Configuration c, DocTree dtree) {
 518         return new SimpleDocTreeVisitor<List<? extends DocTree>, Void>() {
 519             List<? extends DocTree> asList(String content) {
 520                 List<DocTree> out = new ArrayList<>();
 521                 out.add((TextTree)c.cmtutils.makeTextTree(content));
 522                 return out;
 523             }
 524 
 525             @Override @DefinedBy(Api.COMPILER_TREE)
 526             public List<? extends DocTree> visitAuthor(AuthorTree node, Void p) {
 527                 return node.getName();
 528             }
 529 
 530             @Override @DefinedBy(Api.COMPILER_TREE)
 531             public List<? extends DocTree> visitComment(CommentTree node, Void p) {
 532                 return asList(node.getBody());
 533             }
 534 
 535             @Override @DefinedBy(Api.COMPILER_TREE)
 536             public List<? extends DocTree> visitDeprecated(DeprecatedTree node, Void p) {
 537                 return node.getBody();
 538             }
 539 
 540             @Override @DefinedBy(Api.COMPILER_TREE)
 541             public List<? extends DocTree> visitDocComment(DocCommentTree node, Void p) {
 542                 return node.getBody();
 543             }
 544 
 545             @Override @DefinedBy(Api.COMPILER_TREE)
 546             public List<? extends DocTree> visitLiteral(LiteralTree node, Void p) {
 547                 return asList(node.getBody().getBody());
 548             }
 549 
 550             @Override @DefinedBy(Api.COMPILER_TREE)
 551             public List<? extends DocTree> visitSince(SinceTree node, Void p) {
 552                 return node.getBody();
 553             }
 554 
 555             @Override @DefinedBy(Api.COMPILER_TREE)
 556             public List<? extends DocTree> visitText(TextTree node, Void p) {
 557                 return asList(node.getBody());
 558             }
 559 
 560             @Override @DefinedBy(Api.COMPILER_TREE)
 561             public List<? extends DocTree> visitVersion(VersionTree node, Void p) {
 562                 return node.getBody();
 563             }
 564 
 565             @Override @DefinedBy(Api.COMPILER_TREE)
 566             public List<? extends DocTree> visitParam(ParamTree node, Void p) {
 567                return node.getDescription();
 568             }
 569 
 570             @Override @DefinedBy(Api.COMPILER_TREE)
 571             public List<? extends DocTree> visitReturn(ReturnTree node, Void p) {
 572                 return node.getDescription();
 573             }
 574 
 575             @Override @DefinedBy(Api.COMPILER_TREE)
 576             public List<? extends DocTree> visitSee(SeeTree node, Void p) {
 577                 return node.getReference();
 578             }
 579 
 580             @Override @DefinedBy(Api.COMPILER_TREE)
 581             public List<? extends DocTree> visitSerial(SerialTree node, Void p) {
 582                 return node.getDescription();
 583             }
 584 
 585             @Override @DefinedBy(Api.COMPILER_TREE)
 586             public List<? extends DocTree> visitSerialData(SerialDataTree node, Void p) {
 587                 return node.getDescription();
 588             }
 589 
 590             @Override @DefinedBy(Api.COMPILER_TREE)
 591             public List<? extends DocTree> visitSerialField(SerialFieldTree node, Void p) {
 592                 return node.getDescription();
 593             }
 594 
 595             @Override @DefinedBy(Api.COMPILER_TREE)
 596             public List<? extends DocTree> visitThrows(ThrowsTree node, Void p) {
 597                  return node.getDescription();
 598             }
 599 
 600             @Override @DefinedBy(Api.COMPILER_TREE)
 601             public List<? extends DocTree> visitUnknownBlockTag(UnknownBlockTagTree node, Void p) {
 602                 return node.getContent();
 603             }
 604 
 605             @Override @DefinedBy(Api.COMPILER_TREE)
 606             protected List<? extends DocTree> defaultAction(DocTree node, Void p) {
 607                return Collections.emptyList();
 608             }
 609         }.visit(dtree, null);
 610     }
 611 
 612     public List<? extends DocTree> getBody(Configuration c, DocTree dtree) {
 613         return getTags(c, dtree);
 614     }
 615 
 616     public ReferenceTree getType(DocTree dtree) {
 617         if (dtree.getKind() == SERIAL_FIELD) {
 618             return ((SerialFieldTree)dtree).getType();
 619         } else {
 620             return null;
 621         }
 622     }
 623 
 624     public DocTreePath getDocTreePath(DocTree dtree) {
 625         if (path == null || dctree == null || dtree == null)
 626             return null;
 627         return DocTreePath.getPath(path, dctree, dtree);
 628     }
 629 
 630     public Element getOverriddenElement() {
 631         return overriddenElement;
 632     }
 633 
 634 
 635     /**
 636      * For debugging purposes only. Do not rely on this for other things.
 637      * @return a string representation.
 638      */
 639     @Override
 640     public String toString() {
 641         StringBuilder sb = new StringBuilder("CommentHelper{" + "path=" + path + ", dctree=" + dctree);
 642         sb.append(", element=");
 643         sb.append(element.getEnclosingElement());
 644         sb.append("::");
 645         sb.append(element);
 646         sb.append(", overriddenElement=");
 647         if (overriddenElement != null) {
 648             sb.append(overriddenElement.getEnclosingElement());
 649             sb.append("::");
 650             sb.append(overriddenElement);
 651         } else {
 652             sb.append("<none>");
 653         }
 654         sb.append('}');
 655         return sb.toString();
 656     }
 657 }