1 /*
   2  * Copyright (c) 2015, 2020, 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;
  27 
  28 import java.util.ArrayList;
  29 import java.util.Arrays;
  30 import java.util.HashMap;
  31 import java.util.HashSet;
  32 import java.util.List;
  33 import java.util.Map;
  34 import java.util.Set;
  35 import java.util.SortedSet;
  36 import java.util.TreeSet;
  37 
  38 import javax.lang.model.element.AnnotationMirror;
  39 import javax.lang.model.element.Element;
  40 import javax.lang.model.element.ExecutableElement;
  41 import javax.lang.model.element.ModuleElement;
  42 import javax.lang.model.element.PackageElement;
  43 import javax.lang.model.element.TypeElement;
  44 import javax.lang.model.element.VariableElement;
  45 import javax.lang.model.type.TypeMirror;
  46 import javax.lang.model.util.Elements;
  47 import javax.tools.FileObject;
  48 import javax.tools.JavaFileManager.Location;
  49 
  50 import com.sun.source.tree.CompilationUnitTree;
  51 import com.sun.source.util.JavacTask;
  52 import com.sun.source.util.TreePath;
  53 import com.sun.tools.doclint.DocLint;
  54 import com.sun.tools.javac.api.BasicJavacTask;
  55 import com.sun.tools.javac.code.Attribute;
  56 import com.sun.tools.javac.code.Flags;
  57 import com.sun.tools.javac.code.Scope;
  58 import com.sun.tools.javac.code.Source.Feature;
  59 import com.sun.tools.javac.code.Symbol;
  60 import com.sun.tools.javac.code.Symbol.ClassSymbol;
  61 import com.sun.tools.javac.code.Symbol.MethodSymbol;
  62 import com.sun.tools.javac.code.Symbol.ModuleSymbol;
  63 import com.sun.tools.javac.code.Symbol.PackageSymbol;
  64 import com.sun.tools.javac.code.Symbol.VarSymbol;
  65 import com.sun.tools.javac.code.TypeTag;
  66 import com.sun.tools.javac.comp.AttrContext;
  67 import com.sun.tools.javac.comp.Env;
  68 import com.sun.tools.javac.model.JavacElements;
  69 import com.sun.tools.javac.model.JavacTypes;
  70 import com.sun.tools.javac.util.Names;
  71 
  72 import jdk.javadoc.internal.doclets.toolkit.util.Utils;
  73 import jdk.javadoc.internal.tool.ToolEnvironment;
  74 import jdk.javadoc.internal.tool.DocEnvImpl;
  75 
  76 import static com.sun.tools.javac.code.Kinds.Kind.*;
  77 import static com.sun.tools.javac.code.Scope.LookupKind.NON_RECURSIVE;
  78 
  79 import static javax.lang.model.element.ElementKind.*;
  80 
  81 /**
  82  * A quarantine class to isolate all the workarounds and bridges to
  83  * a locality. This class should eventually disappear once all the
  84  * standard APIs support the needed interfaces.
  85  *
  86  *
  87  *  <p><b>This is NOT part of any supported API.
  88  *  If you write code that depends on this, you do so at your own risk.
  89  *  This code and its internal interfaces are subject to change or
  90  *  deletion without notice.</b>
  91  */
  92 public class WorkArounds {
  93 
  94     public final BaseConfiguration configuration;
  95     public final ToolEnvironment toolEnv;
  96     public final Utils utils;
  97 
  98     private DocLint doclint;
  99 
 100     public WorkArounds(BaseConfiguration configuration) {
 101         this.configuration = configuration;
 102         this.utils = this.configuration.utils;
 103         this.toolEnv = ((DocEnvImpl)this.configuration.docEnv).toolEnv;
 104     }
 105 
 106     Map<CompilationUnitTree, Boolean> shouldCheck = new HashMap<>();
 107     // TODO: fix this up correctly
 108     public void runDocLint(TreePath path) {
 109         CompilationUnitTree unit = path.getCompilationUnit();
 110         if (doclint != null && shouldCheck.computeIfAbsent(unit, doclint::shouldCheck)) {
 111             doclint.scan(path);
 112         }
 113     }
 114 
 115     /**
 116      * Initializes doclint, if appropriate, depending on options derived
 117      * from the doclet command-line options, and the set of custom tags
 118      * that should be ignored by doclint.
 119      *
 120      * DocLint is not enabled if the option {@code -Xmsgs:none} is given,
 121      * and it is not followed by any options to enable any groups.
 122      * Note that arguments for {@code -Xmsgs:} can be given individually
 123      * in separate {@code -Xmsgs:} options, or in a comma-separated list
 124      * for a single option. For example, the following are equivalent:
 125      * <ul>
 126      *     <li>{@code -Xmsgs:all} {@code -Xmsgs:-html}
 127      *     <li>{@code -Xmsgs:all,-html}
 128      * </ul>
 129      *
 130      * @param opts  options for doclint, derived from the corresponding doclet
 131      *              command-line options
 132      * @param customTagNames the names of custom tags, to be ignored by doclint
 133      */
 134     public void initDocLint(List<String> opts, Set<String> customTagNames) {
 135         List<String> doclintOpts = new ArrayList<>();
 136 
 137         // basic analysis of -Xmsgs and -Xmsgs: options to see if doclint is enabled
 138         Set<String> groups = new HashSet<>();
 139         boolean seenXmsgs = false;
 140         for (String opt : opts) {
 141             if (opt.equals(DocLint.XMSGS_OPTION)) {
 142                 groups.add("all");
 143                 seenXmsgs = true;
 144             } else if (opt.startsWith(DocLint.XMSGS_CUSTOM_PREFIX)) {
 145                 String[] args = opt.substring(DocLint.XMSGS_CUSTOM_PREFIX.length())
 146                         .split(DocLint.SEPARATOR);
 147                 for (String a : args) {
 148                     if (a.equals("none")) {
 149                         groups.clear();
 150                     } else if (a.startsWith("-")) {
 151                         groups.remove(a.substring(1));
 152                     } else {
 153                         groups.add(a);
 154                     }
 155                 }
 156                 seenXmsgs = true;
 157             }
 158             doclintOpts.add(opt);
 159         }
 160 
 161         if (seenXmsgs) {
 162             if (groups.isEmpty()) {
 163                 // no groups enabled; do not init doclint
 164                 return;
 165             }
 166         } else {
 167             // no -Xmsgs options of any kind, use default
 168             doclintOpts.add(DocLint.XMSGS_OPTION);
 169         }
 170 
 171         if (!customTagNames.isEmpty()) {
 172             String customTags = String.join(DocLint.SEPARATOR, customTagNames);
 173             doclintOpts.add(DocLint.XCUSTOM_TAGS_PREFIX + customTags);
 174         }
 175 
 176         doclintOpts.add(DocLint.XHTML_VERSION_PREFIX + "html5");
 177 
 178         JavacTask t = BasicJavacTask.instance(toolEnv.context);
 179         doclint = new DocLint();
 180         doclint.init(t, doclintOpts.toArray(new String[0]), false);
 181     }
 182 
 183     // TODO: fix this up correctly
 184     public boolean haveDocLint() {
 185         return (doclint == null);
 186     }
 187 
 188     /*
 189      * TODO: This method exists because of a bug in javac which does not
 190      * handle "@deprecated tag in package-info.java", when this issue
 191      * is fixed this method and its uses must be jettisoned.
 192      */
 193     public boolean isDeprecated0(Element e) {
 194         if (!utils.getDeprecatedTrees(e).isEmpty()) {
 195             return true;
 196         }
 197         JavacTypes jctypes = ((DocEnvImpl)configuration.docEnv).toolEnv.typeutils;
 198         TypeMirror deprecatedType = utils.getDeprecatedType();
 199         for (AnnotationMirror anno : e.getAnnotationMirrors()) {
 200             if (jctypes.isSameType(anno.getAnnotationType().asElement().asType(), deprecatedType))
 201                 return true;
 202         }
 203         return false;
 204     }
 205 
 206     // TODO: fix jx.l.m add this method.
 207     public boolean isSynthesized(AnnotationMirror aDesc) {
 208         return ((Attribute)aDesc).isSynthesized();
 209     }
 210 
 211     // TODO: fix the caller
 212     public Object getConstValue(VariableElement ve) {
 213         return ((VarSymbol)ve).getConstValue();
 214     }
 215 
 216     // TODO: DocTrees: Trees.getPath(Element e) is slow a factor 4-5 times.
 217     public Map<Element, TreePath> getElementToTreePath() {
 218         return toolEnv.elementToTreePath;
 219     }
 220 
 221     // TODO: we need ElementUtils.getPackage to cope with input strings
 222     // to return the proper unnamedPackage for all supported releases.
 223     PackageElement getUnnamedPackage() {
 224         return (Feature.MODULES.allowedInSource(toolEnv.source))
 225                 ? toolEnv.syms.unnamedModule.unnamedPackage
 226                 : toolEnv.syms.noModule.unnamedPackage;
 227     }
 228 
 229     // TODO: implement in either jx.l.m API (preferred) or DocletEnvironment.
 230     FileObject getJavaFileObject(PackageElement packageElement) {
 231         return ((PackageSymbol)packageElement).sourcefile;
 232     }
 233 
 234     // TODO: needs to ported to jx.l.m.
 235     public TypeElement searchClass(TypeElement klass, String className) {
 236         TypeElement te;
 237 
 238         // search by qualified name in current module first
 239         ModuleElement me = utils.containingModule(klass);
 240         if (me != null) {
 241             te = configuration.docEnv.getElementUtils().getTypeElement(me, className);
 242             if (te != null) {
 243                 return te;
 244             }
 245         }
 246 
 247         // search inner classes
 248         for (TypeElement ite : utils.getClasses(klass)) {
 249             TypeElement innerClass = searchClass(ite, className);
 250             if (innerClass != null) {
 251                 return innerClass;
 252             }
 253         }
 254 
 255         // check in this package
 256         te = utils.findClassInPackageElement(utils.containingPackage(klass), className);
 257         if (te != null) {
 258             return te;
 259         }
 260 
 261         ClassSymbol tsym = (ClassSymbol)klass;
 262         // make sure that this symbol has been completed
 263         // TODO: do we need this anymore ?
 264         if (tsym.completer != null) {
 265             tsym.complete();
 266         }
 267 
 268         // search imports
 269         if (tsym.sourcefile != null) {
 270 
 271             //### This information is available only for source classes.
 272             Env<AttrContext> compenv = toolEnv.getEnv(tsym);
 273             if (compenv == null) {
 274                 return null;
 275             }
 276             Names names = tsym.name.table.names;
 277             Scope s = compenv.toplevel.namedImportScope;
 278             for (Symbol sym : s.getSymbolsByName(names.fromString(className))) {
 279                 if (sym.kind == TYP) {
 280                     return (TypeElement)sym;
 281                 }
 282             }
 283 
 284             s = compenv.toplevel.starImportScope;
 285             for (Symbol sym : s.getSymbolsByName(names.fromString(className))) {
 286                 if (sym.kind == TYP) {
 287                     return (TypeElement)sym;
 288                 }
 289             }
 290         }
 291 
 292         // finally, search by qualified name in all modules
 293         te = configuration.docEnv.getElementUtils().getTypeElement(className);
 294         if (te != null) {
 295             return te;
 296         }
 297 
 298         return null; // not found
 299     }
 300 
 301     // TODO:  need to re-implement this using j.l.m. correctly!, this has
 302     // implications on testInterface, the note here is that javac's supertype
 303     // does the right thing returning Parameters in scope.
 304     /**
 305      * Return the type containing the method that this method overrides.
 306      * It may be a <code>TypeElement</code> or a <code>TypeParameterElement</code>.
 307      * @param method target
 308      * @return a type
 309      */
 310     public TypeMirror overriddenType(ExecutableElement method) {
 311         if (utils.isStatic(method)) {
 312             return null;
 313         }
 314         MethodSymbol sym = (MethodSymbol)method;
 315         ClassSymbol origin = (ClassSymbol) sym.owner;
 316         for (com.sun.tools.javac.code.Type t = toolEnv.getTypes().supertype(origin.type);
 317                 t.hasTag(TypeTag.CLASS);
 318                 t = toolEnv.getTypes().supertype(t)) {
 319             ClassSymbol c = (ClassSymbol) t.tsym;
 320             for (com.sun.tools.javac.code.Symbol sym2 : c.members().getSymbolsByName(sym.name)) {
 321                 if (sym.overrides(sym2, origin, toolEnv.getTypes(), true)) {
 322                     // Ignore those methods that may be a simple override
 323                     // and allow the real API method to be found.
 324                     if (sym2.type.hasTag(TypeTag.METHOD) &&
 325                             utils.isSimpleOverride((MethodSymbol)sym2)) {
 326                         continue;
 327                     }
 328                     return t;
 329                 }
 330             }
 331         }
 332         return null;
 333     }
 334 
 335     // TODO: the method jx.l.m.Elements::overrides does not check
 336     // the return type, see JDK-8174840 until that is resolved,
 337     // use a  copy of the same method, with a return type check.
 338 
 339     // Note: the rider.overrides call in this method *must* be consistent
 340     // with the call in overrideType(....), the method above.
 341     public boolean overrides(ExecutableElement e1, ExecutableElement e2, TypeElement cls) {
 342         MethodSymbol rider = (MethodSymbol)e1;
 343         MethodSymbol ridee = (MethodSymbol)e2;
 344         ClassSymbol origin = (ClassSymbol)cls;
 345 
 346         return rider.name == ridee.name &&
 347 
 348                // not reflexive as per JLS
 349                rider != ridee &&
 350 
 351                // we don't care if ridee is static, though that wouldn't
 352                // compile
 353                !rider.isStatic() &&
 354 
 355                // Symbol.overrides assumes the following
 356                ridee.isMemberOf(origin, toolEnv.getTypes()) &&
 357 
 358                // check access, signatures and check return types
 359                rider.overrides(ridee, origin, toolEnv.getTypes(), true);
 360     }
 361 
 362     // TODO: jx.l.m ?
 363     public Location getLocationForModule(ModuleElement mdle) {
 364         ModuleSymbol msym = (ModuleSymbol)mdle;
 365         return msym.sourceLocation != null
 366                 ? msym.sourceLocation
 367                 : msym.classLocation;
 368     }
 369 
 370     //------------------Start of Serializable Implementation---------------------//
 371     private final Map<TypeElement, NewSerializedForm> serializedForms = new HashMap<>();
 372 
 373     private NewSerializedForm getSerializedForm(TypeElement typeElem) {
 374         return serializedForms.computeIfAbsent(typeElem,
 375                 te -> new NewSerializedForm(utils, configuration.docEnv.getElementUtils(), te));
 376     }
 377 
 378     public SortedSet<VariableElement> getSerializableFields(TypeElement typeElem) {
 379         return getSerializedForm(typeElem).fields;
 380     }
 381 
 382     public SortedSet<ExecutableElement>  getSerializationMethods(TypeElement typeElem) {
 383         return getSerializedForm(typeElem).methods;
 384     }
 385 
 386     public boolean definesSerializableFields(TypeElement typeElem) {
 387         if (!utils.isSerializable(typeElem) || utils.isExternalizable(typeElem)) {
 388             return false;
 389         } else {
 390             return getSerializedForm(typeElem).definesSerializableFields;
 391         }
 392     }
 393 
 394     /* TODO we need a clean port to jx.l.m
 395      * The serialized form is the specification of a class' serialization state.
 396      * <p>
 397      *
 398      * It consists of the following information:
 399      * <p>
 400      *
 401      * <pre>
 402      * 1. Whether class is Serializable or Externalizable.
 403      * 2. Javadoc for serialization methods.
 404      *    a. For Serializable, the optional readObject, writeObject,
 405      *       readResolve and writeReplace.
 406      *       serialData tag describes, in prose, the sequence and type
 407      *       of optional data written by writeObject.
 408      *    b. For Externalizable, writeExternal and readExternal.
 409      *       serialData tag describes, in prose, the sequence and type
 410      *       of optional data written by writeExternal.
 411      * 3. Javadoc for serialization data layout.
 412      *    a. For Serializable, the name,type and description
 413      *       of each Serializable fields.
 414      *    b. For Externalizable, data layout is described by 2(b).
 415      * </pre>
 416      *
 417      */
 418     static class NewSerializedForm {
 419 
 420         final Utils utils;
 421         final Elements elements;
 422 
 423         final SortedSet<ExecutableElement> methods;
 424 
 425         /* List of FieldDocImpl - Serializable fields.
 426          * Singleton list if class defines Serializable fields explicitly.
 427          * Otherwise, list of default serializable fields.
 428          * 0 length list for Externalizable.
 429          */
 430         final SortedSet<VariableElement> fields;
 431 
 432         /* True if class specifies serializable fields explicitly.
 433          * using special static member, serialPersistentFields.
 434          */
 435         boolean definesSerializableFields = false;
 436 
 437         // Specially treated field/method names defined by Serialization.
 438         private static final String SERIALIZABLE_FIELDS = "serialPersistentFields";
 439         private static final String READOBJECT = "readObject";
 440         private static final String WRITEOBJECT = "writeObject";
 441         private static final String READRESOLVE = "readResolve";
 442         private static final String WRITEREPLACE = "writeReplace";
 443         private static final String READOBJECTNODATA = "readObjectNoData";
 444 
 445         NewSerializedForm(Utils utils, Elements elements, TypeElement te) {
 446             this.utils = utils;
 447             this.elements = elements;
 448             methods = new TreeSet<>(utils.makeGeneralPurposeComparator());
 449             fields = new TreeSet<>(utils.makeGeneralPurposeComparator());
 450             if (utils.isExternalizable(te)) {
 451                 /* look up required public accessible methods,
 452                  *   writeExternal and readExternal.
 453                  */
 454                 String[] readExternalParamArr = {"java.io.ObjectInput"};
 455                 String[] writeExternalParamArr = {"java.io.ObjectOutput"};
 456 
 457                 ExecutableElement md = findMethod(te, "readExternal", Arrays.asList(readExternalParamArr));
 458                 if (md != null) {
 459                     methods.add(md);
 460                 }
 461                 md = findMethod(te, "writeExternal", Arrays.asList(writeExternalParamArr));
 462                 if (md != null) {
 463                     methods.add(md);
 464                 }
 465             } else if (utils.isSerializable(te)) {
 466                 VarSymbol dsf = getDefinedSerializableFields((ClassSymbol) te);
 467                 if (dsf != null) {
 468                     /* Define serializable fields with array of ObjectStreamField.
 469                      * Each ObjectStreamField should be documented by a
 470                      * serialField tag.
 471                      */
 472                     definesSerializableFields = true;
 473                     fields.add(dsf);
 474                 } else {
 475 
 476                     /* Calculate default Serializable fields as all
 477                      * non-transient, non-static fields.
 478                      * Fields should be documented by serial tag.
 479                      */
 480                     computeDefaultSerializableFields((ClassSymbol) te);
 481                 }
 482 
 483                 /* Check for optional customized readObject, writeObject,
 484                  * readResolve and writeReplace, which can all contain
 485                  * the serialData tag.        */
 486                 addMethodIfExist((ClassSymbol) te, READOBJECT);
 487                 addMethodIfExist((ClassSymbol) te, WRITEOBJECT);
 488                 addMethodIfExist((ClassSymbol) te, READRESOLVE);
 489                 addMethodIfExist((ClassSymbol) te, WRITEREPLACE);
 490                 addMethodIfExist((ClassSymbol) te, READOBJECTNODATA);
 491             }
 492         }
 493 
 494         private VarSymbol getDefinedSerializableFields(ClassSymbol def) {
 495             Names names = def.name.table.names;
 496 
 497             /* SERIALIZABLE_FIELDS can be private,
 498              */
 499             for (Symbol sym : def.members().getSymbolsByName(names.fromString(SERIALIZABLE_FIELDS))) {
 500                 if (sym.kind == VAR) {
 501                     VarSymbol f = (VarSymbol) sym;
 502                     if ((f.flags() & Flags.STATIC) != 0
 503                             && (f.flags() & Flags.PRIVATE) != 0) {
 504                         return f;
 505                     }
 506                 }
 507             }
 508             return null;
 509         }
 510 
 511         /*
 512          * Catalog Serializable method if it exists in current ClassSymbol.
 513          * Do not look for method in superclasses.
 514          *
 515          * Serialization requires these methods to be non-static.
 516          *
 517          * @param method should be an unqualified Serializable method
 518          *               name either READOBJECT, WRITEOBJECT, READRESOLVE
 519          *               or WRITEREPLACE.
 520          * @param visibility the visibility flag for the given method.
 521          */
 522         private void addMethodIfExist(ClassSymbol def, String methodName) {
 523             Names names = def.name.table.names;
 524 
 525             for (Symbol sym : def.members().getSymbolsByName(names.fromString(methodName))) {
 526                 if (sym.kind == MTH) {
 527                     MethodSymbol md = (MethodSymbol) sym;
 528                     if ((md.flags() & Flags.STATIC) == 0) {
 529                         /*
 530                          * WARNING: not robust if unqualifiedMethodName is overloaded
 531                          *          method. Signature checking could make more robust.
 532                          * READOBJECT takes a single parameter, java.io.ObjectInputStream.
 533                          * WRITEOBJECT takes a single parameter, java.io.ObjectOutputStream.
 534                          */
 535                         methods.add(md);
 536                     }
 537                 }
 538             }
 539         }
 540 
 541         /*
 542          * Compute default Serializable fields from all members of ClassSymbol.
 543          *
 544          * must walk over all members of ClassSymbol.
 545          */
 546         private void computeDefaultSerializableFields(ClassSymbol te) {
 547             for (Symbol sym : te.members().getSymbols(NON_RECURSIVE)) {
 548                 if (sym != null && sym.kind == VAR) {
 549                     VarSymbol f = (VarSymbol) sym;
 550                     if ((f.flags() & Flags.STATIC) == 0
 551                             && (f.flags() & Flags.TRANSIENT) == 0) {
 552                         //### No modifier filtering applied here.
 553                         //### Add to beginning.
 554                         //### Preserve order used by old 'javadoc'.
 555                         fields.add(f);
 556                     }
 557                 }
 558             }
 559         }
 560 
 561         /**
 562          * Find a method in this class scope. Search order: this class, interfaces, superclasses,
 563          * outerclasses. Note that this is not necessarily what the compiler would do!
 564          *
 565          * @param methodName the unqualified name to search for.
 566          * @param paramTypes the array of Strings for method parameter types.
 567          * @return the first MethodDocImpl which matches, null if not found.
 568          */
 569         public ExecutableElement findMethod(TypeElement te, String methodName,
 570                 List<String> paramTypes) {
 571             List<? extends Element> allMembers = this.elements.getAllMembers(te);
 572             loop:
 573             for (Element e : allMembers) {
 574                 if (e.getKind() != METHOD) {
 575                     continue;
 576                 }
 577                 ExecutableElement ee = (ExecutableElement) e;
 578                 if (!ee.getSimpleName().contentEquals(methodName)) {
 579                     continue;
 580                 }
 581                 List<? extends VariableElement> parameters = ee.getParameters();
 582                 if (paramTypes.size() != parameters.size()) {
 583                     continue;
 584                 }
 585                 for (int i = 0; i < parameters.size(); i++) {
 586                     VariableElement ve = parameters.get(i);
 587                     if (!ve.asType().toString().equals(paramTypes.get(i))) {
 588                         break loop;
 589                     }
 590                 }
 591                 return ee;
 592             }
 593             TypeElement encl = utils.getEnclosingTypeElement(te);
 594             if (encl == null) {
 595                 return null;
 596             }
 597             return findMethod(encl, methodName, paramTypes);
 598         }
 599     }
 600 
 601     // TODO: we need to eliminate this, as it is hacky.
 602     /**
 603      * Returns a representation of the package truncated to two levels.
 604      * For instance if the given package represents foo.bar.baz will return
 605      * a representation of foo.bar
 606      * @param pkg the PackageElement
 607      * @return an abbreviated PackageElement
 608      */
 609     public PackageElement getAbbreviatedPackageElement(PackageElement pkg) {
 610         String parsedPackageName = utils.parsePackageName(pkg);
 611         ModuleElement encl = (ModuleElement) pkg.getEnclosingElement();
 612         return encl == null
 613                 ? utils.elementUtils.getPackageElement(parsedPackageName)
 614                 : ((JavacElements) utils.elementUtils).getPackageElement(encl, parsedPackageName);
 615     }
 616 }