All Downloads are FREE. Search and download functionalities are using the official Maven repository.

org.netbeans.api.java.source.ElementHandle Maven / Gradle / Ivy

The newest version!
/*
 * Licensed to the Apache Software Foundation (ASF) under one
 * or more contributor license agreements.  See the NOTICE file
 * distributed with this work for additional information
 * regarding copyright ownership.  The ASF licenses this file
 * to you under the Apache License, Version 2.0 (the
 * "License"); you may not use this file except in compliance
 * with the License.  You may obtain a copy of the License at
 *
 *   http://www.apache.org/licenses/LICENSE-2.0
 *
 * Unless required by applicable law or agreed to in writing,
 * software distributed under the License is distributed on an
 * "AS IS" BASIS, WITHOUT WARRANTIES OR CONDITIONS OF ANY
 * KIND, either express or implied.  See the License for the
 * specific language governing permissions and limitations
 * under the License.
 */

package org.netbeans.api.java.source;

import com.sun.tools.javac.api.JavacTaskImpl;
import com.sun.tools.javac.code.ModuleFinder;
import com.sun.tools.javac.code.Symbol;
import com.sun.tools.javac.code.Symtab;
import com.sun.tools.javac.jvm.Target;
import com.sun.tools.javac.tree.JCTree;
import com.sun.tools.javac.util.Name;
import java.util.Arrays;
import java.util.List;
import java.util.logging.Logger;
import java.util.logging.Level;
import javax.lang.model.element.Element;
import javax.lang.model.element.ElementKind;
import javax.lang.model.element.ExecutableElement;
import javax.lang.model.element.ModuleElement;
import javax.lang.model.element.PackageElement;
import javax.lang.model.element.TypeElement;
import javax.lang.model.element.TypeParameterElement;
import javax.lang.model.element.VariableElement;
import javax.lang.model.type.DeclaredType;
import javax.lang.model.type.TypeKind;

import org.netbeans.api.annotations.common.CheckForNull;
import org.netbeans.api.annotations.common.NonNull;
import org.netbeans.modules.java.source.ElementHandleAccessor;
import org.netbeans.modules.java.source.ElementUtils;
import org.netbeans.modules.java.source.usages.ClassFileUtil;
import org.openide.util.Parameters;
import org.openide.util.WeakSet;

/**
 * Represents a handle for {@link Element} which can be kept and later resolved
 * by another javac. The javac {@link Element}s are valid only in a single
 * {@link javax.tools.JavaCompiler.CompilationTask} or a single run of a
 * {@link CancellableTask}. A client needing to
 * keep a reference to an {@link Element} and use it in another {@link CancellableTask}
 * must serialize it into an {@link ElementHandle}.
 * Currently not all {@link Element}s can be serialized. See {@link #create} for details.
 * 
*

* Typical usage of {@link ElementHandle} is as follows: *

*
 * final ElementHandle[] elementHandle = new ElementHandle[1];
 * javaSource.runUserActionTask(new Task<CompilationController>() {
 *     public void run(CompilationController compilationController) {
 *         compilationController.toPhase(Phase.RESOLVED);
 *         CompilationUnitTree cu = compilationController.getTree();
 *         List<? extends Tree> types = getTypeDecls(cu);
 *         Tree tree = getInterestingElementTree(types);
 *         Element element = compilationController.getElement(tree);
 *         elementHandle[0] = ElementHandle.create(element);
 *    }
 * }, true);
 *
 * otherJavaSource.runUserActionTask(new Task<CompilationController>() {
 *     public void run(CompilationController compilationController) {
 *         compilationController.toPhase(Phase.RESOLVED);
 *         Element element = elementHandle[0].resolve(compilationController);
 *         // ....
 *    }
 * }, true);
 * 
*
* @author Tomas Zezula */ public final class ElementHandle { private static final Logger log = Logger.getLogger(ElementHandle.class.getName()); static { ElementHandleAccessor.setInstance(new ElementHandleAccessorImpl ()); } private final ElementKind kind; private final String[] signatures; private ElementHandle(final ElementKind kind, String... signatures) { assert kind != null; assert signatures != null; this.kind = kind; this.signatures = signatures; } /** * Resolves an {@link Element} from the {@link ElementHandle}. * @param compilationInfo representing the {@link javax.tools.JavaCompiler.CompilationTask} * in which the {@link Element} should be resolved. * @return resolved subclass of {@link Element} or null if the elment does not exist on * the classpath/sourcepath of {@link javax.tools.JavaCompiler.CompilationTask}. */ @SuppressWarnings ("unchecked") // NOI18N public @CheckForNull T resolve (@NonNull final CompilationInfo compilationInfo) { Parameters.notNull("compilationInfo", compilationInfo); // NOI18N ModuleElement module; if (compilationInfo.getFileObject() != null) { JCTree.JCCompilationUnit cut = (JCTree.JCCompilationUnit)compilationInfo.getCompilationUnit(); if (cut != null) { module = cut.modle; } else if (compilationInfo.getTopLevelElements().iterator().hasNext()) { module = ((Symbol) compilationInfo.getTopLevelElements().iterator().next()).packge().modle; } else { module = null; } } else { module = null; } T result = resolveImpl (module, compilationInfo.impl.getJavacTask()); if (result == null) { if (log.isLoggable(Level.INFO)) log.log(Level.INFO, "Cannot resolve: {0}", toString()); //NOI18N } else { if (log.isLoggable(Level.FINE)) log.log(Level.FINE, "Resolved element = {0}", result); } return result; } private T resolveImpl (final ModuleElement module, final JavacTaskImpl jt) { if (log.isLoggable(Level.FINE)) log.log(Level.FINE, "Resolving element kind: {0}", this.kind); // NOI18N ElementKind simplifiedKind = this.kind; if (simplifiedKind.name().equals("RECORD")) { simplifiedKind = ElementKind.CLASS; //TODO: test } if (simplifiedKind.name().equals("RECORD_COMPONENT")) { simplifiedKind = ElementKind.FIELD; //TODO: test } switch (simplifiedKind) { case PACKAGE: assert signatures.length == 1; return (T) jt.getElements().getPackageElement(signatures[0]); case CLASS: case INTERFACE: case ENUM: case ANNOTATION_TYPE: { assert signatures.length == 1; final Element type = getTypeElementByBinaryName (module, signatures[0], jt); if (type instanceof TypeElement) { return (T) type; } else { log.log(Level.INFO, "Resolved type is null for kind = {0}", this.kind); // NOI18N } break; } case OTHER: assert signatures.length == 1; return (T) getTypeElementByBinaryName (module, signatures[0], jt); case METHOD: case CONSTRUCTOR: { assert signatures.length == 3; final Element type = getTypeElementByBinaryName (module, signatures[0], jt); if (type instanceof TypeElement) { final List members = type.getEnclosedElements(); for (Element member : members) { if (this.kind == member.getKind()) { String[] desc = ClassFileUtil.createExecutableDescriptor((ExecutableElement)member); assert desc.length == 3; if (this.signatures[1].equals(desc[1]) && this.signatures[2].equals(desc[2])) { return (T) member; } } } } else if (type != null) { return (T) new Symbol.MethodSymbol(0, (Name) jt.getElements().getName(this.signatures[1]), Symtab.instance(jt.getContext()).unknownType, (Symbol)type); } else log.log(Level.INFO, "Resolved type is null for kind = {0}", this.kind); // NOI18N break; } case INSTANCE_INIT: case STATIC_INIT: { assert signatures.length == 2; final Element type = getTypeElementByBinaryName (module, signatures[0], jt); if (type instanceof TypeElement) { final List members = type.getEnclosedElements(); for (Element member : members) { if (this.kind == member.getKind()) { String[] desc = ClassFileUtil.createExecutableDescriptor((ExecutableElement)member); assert desc.length == 2; if (this.signatures[1].equals(desc[1])) { return (T) member; } } } } else log.log(Level.INFO, "Resolved type is null for kind = {0}", this.kind); // NOI18N break; } case FIELD: case ENUM_CONSTANT: { assert signatures.length == 3; final Element type = getTypeElementByBinaryName (module, signatures[0], jt); if (type instanceof TypeElement) { final List members = type.getEnclosedElements(); for (Element member : members) { if (this.kind == member.getKind()) { String[] desc = ClassFileUtil.createFieldDescriptor((VariableElement)member); assert desc.length == 3; if (this.signatures[1].equals(desc[1]) && this.signatures[2].equals(desc[2])) { return (T) member; } } } } else if (type != null) { return (T) new Symbol.VarSymbol(0, (Name) jt.getElements().getName(this.signatures[1]), Symtab.instance(jt.getContext()).unknownType, (Symbol)type); } else log.log(Level.INFO, "Resolved type is null for kind = {0}", this.kind); // NOI18N break; } case PARAMETER: { assert signatures.length == 3; final Element type = getTypeElementByBinaryName (module, signatures[0], jt); if (type instanceof TypeElement) { final List members = type.getEnclosedElements(); for (Element member : members) { if (member.getKind() == ElementKind.METHOD || member.getKind() == ElementKind.CONSTRUCTOR) { String[] desc = ClassFileUtil.createExecutableDescriptor((ExecutableElement)member); assert desc.length == 3; if (this.signatures[1].equals(desc[1]) && this.signatures[2].equals(desc[2])) { assert member instanceof ExecutableElement; List ves =((ExecutableElement)member).getParameters(); for (VariableElement ve : ves) { if (ve.getSimpleName().contentEquals(signatures[3])) { return (T) ve; } } } } } } else log.log(Level.INFO, "Resolved type is null for kind = {0} signatures.length = {1}", new Object[] {this.kind, signatures.length}); // NOI18N break; } case TYPE_PARAMETER: { if (signatures.length == 2) { Element type = getTypeElementByBinaryName (module, signatures[0], jt); if (type instanceof TypeElement) { List tpes = ((TypeElement)type).getTypeParameters(); for (TypeParameterElement tpe : tpes) { if (tpe.getSimpleName().contentEquals(signatures[1])) { return (T)tpe; } } } else log.log(Level.INFO, "Resolved type is null for kind = {0} signatures.length = {1}", new Object[] {this.kind, signatures.length}); // NOI18N } else if (signatures.length == 4) { final Element type = getTypeElementByBinaryName (module, signatures[0], jt); if (type instanceof TypeElement) { final List members = type.getEnclosedElements(); for (Element member : members) { if (member.getKind() == ElementKind.METHOD || member.getKind() == ElementKind.CONSTRUCTOR) { String[] desc = ClassFileUtil.createExecutableDescriptor((ExecutableElement)member); assert desc.length == 3; if (this.signatures[1].equals(desc[1]) && this.signatures[2].equals(desc[2])) { assert member instanceof ExecutableElement; List tpes =((ExecutableElement)member).getTypeParameters(); for (TypeParameterElement tpe : tpes) { if (tpe.getSimpleName().contentEquals(signatures[3])) { return (T) tpe; } } } } } } else log.log(Level.INFO, "Resolved type is null for kind = {0} signatures.length = {1}", new Object[] {this.kind, signatures.length}); // NOI18N } else { throw new IllegalStateException (); } break; } case MODULE: assert signatures.length == 1; final ModuleFinder cml = ModuleFinder.instance(jt.getContext()); final Element me = cml.findModule((Name)jt.getElements().getName(this.signatures[0])); if (me != null) { return (T) me; } else { log.log(Level.INFO, "Cannot resolve module: {0}", this.signatures[0]); // NOI18N } break; default: throw new IllegalStateException (); } if (log.isLoggable(Level.FINE)) log.log(Level.FINE, "All resolvings failed. Returning null."); // NOI18N return null; } /** * Tests if the handle has the same signature as the parameter. * The handles with the same signatures are resolved into the same * element in the same {@link javax.tools.JavaCompiler} task, but may be resolved into * the different {@link Element}s in the different {@link javax.tools.JavaCompiler} tasks. * @param handle to be checked * @return true if the handles resolve into the same {@link Element}s * in the same {@link javax.tools.JavaCompiler} task. */ public boolean signatureEquals (@NonNull final ElementHandle handle) { if (!isSameKind (this.kind, handle.kind) || this.signatures.length != handle.signatures.length) { return false; } for (int i=0; i handle = create (element); return signatureEquals (handle); } /** * Returns the {@link ElementKind} of this element handle, * it is the kind of the {@link Element} from which the handle * was created. * @return {@link ElementKind} * */ public @NonNull ElementKind getKind () { return this.kind; } private static final WeakSet> NORMALIZATION_CACHE = new WeakSet>(); /** * Factory method for creating {@link ElementHandle}. * @param element for which the {@link ElementHandle} should be created. Permitted * {@link ElementKind}s * are: {@link ElementKind#PACKAGE}, {@link ElementKind#CLASS}, * {@link ElementKind#INTERFACE}, {@link ElementKind#ENUM}, {@link ElementKind#ANNOTATION_TYPE}, {@link ElementKind#METHOD}, * {@link ElementKind#CONSTRUCTOR}, {@link ElementKind#INSTANCE_INIT}, {@link ElementKind#STATIC_INIT}, * {@link ElementKind#FIELD}, and {@link ElementKind#ENUM_CONSTANT}. * @return a new {@link ElementHandle} * @throws IllegalArgumentException if the element is of an unsupported {@link ElementKind} */ public static @NonNull ElementHandle create (@NonNull final T element) throws IllegalArgumentException { ElementHandle eh = createImpl(element); return (ElementHandle) NORMALIZATION_CACHE.putIfAbsent(eh); } /** * Creates an {@link ElementHandle} representing a {@link PackageElement}. * @param packageName the name of the package * @return the created {@link ElementHandle} * @since 0.98 */ @NonNull public static ElementHandle createPackageElementHandle ( @NonNull final String packageName) { Parameters.notNull("packageName", packageName); //NOI18N return new ElementHandle(ElementKind.PACKAGE, packageName); } /** * Creates an {@link ElementHandle} representing a {@link TypeElement}. * @param kind the {@link ElementKind} of the {@link TypeElement}, * allowed values are {@link ElementKind#CLASS}, {@link ElementKind#INTERFACE}, * {@link ElementKind#ENUM} and {@link ElementKind#ANNOTATION_TYPE}. * @param binaryName the class binary name as specified by JLS §13.1 * @return the created {@link ElementHandle} * @throws IllegalArgumentException if kind is neither class nor interface * @since 0.98 */ @NonNull public static ElementHandle createTypeElementHandle( @NonNull final ElementKind kind, @NonNull final String binaryName) throws IllegalArgumentException { Parameters.notNull("kind", kind); //NOI18N Parameters.notNull("binaryName", binaryName); //NOI18N if (!kind.isClass() && !kind.isInterface()) { throw new IllegalArgumentException(kind.toString()); } return new ElementHandle(kind, binaryName); } /** * Creates an {@link ElementHandle} representing a {@link ModuleElement}. * @param moduleName the name of the module * @return the created {@link ElementHandle} * @since 2.26 */ @NonNull public static ElementHandle createModuleElementHandle( @NonNull final String moduleName) { Parameters.notNull("moduleName", moduleName); //NOI18N return new ElementHandle<>(ElementKind.MODULE, moduleName); } private static @NonNull ElementHandle createImpl (@NonNull final T element) throws IllegalArgumentException { Parameters.notNull("element", element); ElementKind kind = element.getKind(); ElementKind simplifiedKind = kind; String[] signatures; switch (simplifiedKind) { case PACKAGE: assert element instanceof PackageElement; signatures = new String[]{((PackageElement)element).getQualifiedName().toString()}; break; case CLASS: case INTERFACE: case ENUM: case ANNOTATION_TYPE: case RECORD: assert element instanceof TypeElement; signatures = new String[] {ClassFileUtil.encodeClassNameOrArray((TypeElement)element)}; break; case METHOD: case CONSTRUCTOR: case INSTANCE_INIT: case STATIC_INIT: assert element instanceof ExecutableElement; signatures = ClassFileUtil.createExecutableDescriptor((ExecutableElement)element); break; case FIELD: case ENUM_CONSTANT: case RECORD_COMPONENT: assert element instanceof VariableElement; signatures = ClassFileUtil.createFieldDescriptor((VariableElement)element); break; case PARAMETER: assert element instanceof VariableElement; Element ee = element.getEnclosingElement(); ElementKind eek = ee.getKind(); if (eek == ElementKind.METHOD || eek == ElementKind.CONSTRUCTOR) { assert ee instanceof ExecutableElement; String[] _sigs = ClassFileUtil.createExecutableDescriptor((ExecutableElement)ee); signatures = new String[_sigs.length + 1]; System.arraycopy(_sigs, 0, signatures, 0, _sigs.length); signatures[_sigs.length] = element.getSimpleName().toString(); } else { throw new IllegalArgumentException(eek.toString()); } break; case TYPE_PARAMETER: assert element instanceof TypeParameterElement; TypeParameterElement tpe = (TypeParameterElement) element; Element ge = tpe.getGenericElement(); ElementKind gek = ge.getKind(); if (gek.isClass() || gek.isInterface()) { assert ge instanceof TypeElement; signatures = new String[2]; signatures[0] = ClassFileUtil.encodeClassNameOrArray((TypeElement)ge); signatures[1] = tpe.getSimpleName().toString(); } else if (gek == ElementKind.METHOD || gek == ElementKind.CONSTRUCTOR) { assert ge instanceof ExecutableElement; String[] _sigs = ClassFileUtil.createExecutableDescriptor((ExecutableElement)ge); signatures = new String[_sigs.length + 1]; System.arraycopy(_sigs, 0, signatures, 0, _sigs.length); signatures[_sigs.length] = tpe.getSimpleName().toString(); } else { throw new IllegalArgumentException(gek.toString()); } break; case MODULE: signatures = new String[]{((ModuleElement)element).getQualifiedName().toString()}; break; default: throw new IllegalArgumentException(kind.toString()); } return new ElementHandle (kind, signatures); } /** * Gets {@link ElementHandle} from {@link TypeMirrorHandle} representing {@link DeclaredType}. * @param typeMirrorHandle from which the {@link ElementHandle} should be retrieved. Permitted * {@link TypeKind} is {@link TypeKind#DECLARED}. * @return an {@link ElementHandle} * @since 0.29.0 */ public static @NonNull ElementHandle from (@NonNull final TypeMirrorHandle typeMirrorHandle) { Parameters.notNull("typeMirrorHandle", typeMirrorHandle); if (typeMirrorHandle.getKind() != TypeKind.DECLARED) { throw new IllegalStateException("Incorrect kind: " + typeMirrorHandle.getKind()); } return (ElementHandle)typeMirrorHandle.getElementHandle(); } public @Override String toString () { final StringBuilder result = new StringBuilder (); result.append (this.getClass().getSimpleName()); result.append ('['); // NOI18N result.append ("kind=").append (this.kind.toString()); // NOI18N result.append ("; sigs="); // NOI18N for (String sig : this.signatures) { result.append (sig); result.append (' '); // NOI18N } result.append (']'); // NOI18N return result.toString(); } /**{@inheritDoc}*/ @Override public int hashCode () { int hashCode = 0; for (String sig : signatures) { hashCode = hashCode ^ (sig != null ? sig.hashCode() : 0); } return hashCode; } /**{@inheritDoc}*/ @Override public boolean equals (Object other) { if (other instanceof ElementHandle) { return signatureEquals((ElementHandle)other); } return false; } /** * Returns the element signature. * Package private, used by ClassIndex. */ String[] getSignature () { return this.signatures; } private static class ElementHandleAccessorImpl extends ElementHandleAccessor { @Override public ElementHandle create(ElementKind kind, String... descriptors) { assert kind != null; assert descriptors != null; switch (kind) { case PACKAGE: if (descriptors.length != 1) { throw new IllegalArgumentException (); } return new ElementHandle (kind, descriptors); case MODULE: case CLASS: case INTERFACE: case ENUM: case ANNOTATION_TYPE: case RECORD: case OTHER: if (descriptors.length != 1) { throw new IllegalArgumentException (); } return new ElementHandle (kind, descriptors); case METHOD: case CONSTRUCTOR: if (descriptors.length != 3) { throw new IllegalArgumentException (); } return new ElementHandle (kind, descriptors); case INSTANCE_INIT: case STATIC_INIT: if (descriptors.length != 2) { throw new IllegalArgumentException (); } return new ElementHandle (kind, descriptors); case FIELD: case ENUM_CONSTANT: if (descriptors.length != 3) { throw new IllegalArgumentException (); } return new ElementHandle (kind, descriptors); default: throw new IllegalArgumentException(); } } @Override public T resolve(ElementHandle handle, JavacTaskImpl jti) { return handle.resolveImpl (null, jti); } @Override @NonNull public String[] getJVMSignature(@NonNull final ElementHandle handle) { return Arrays.copyOf(handle.signatures, handle.signatures.length); } } private static Element getTypeElementByBinaryName (final ModuleElement module, final String signature, final JavacTaskImpl jt) { if (log.isLoggable(Level.FINE)) log.log(Level.FINE, "Calling getTypeElementByBinaryName: signature = {0}", signature); if (isNone(signature)) { return Symtab.instance(jt.getContext()).noSymbol; } else if (isArray(signature)) { return Symtab.instance(jt.getContext()).arrayClass; } else { return (TypeElement) (module != null ? ElementUtils.getTypeElementByBinaryName(jt, module, signature) : ElementUtils.getTypeElementByBinaryName(jt, signature)); } } private static boolean isNone (String signature) { return signature.length() == 0; } private static boolean isArray (String signature) { return signature.length() == 1 && signature.charAt(0) == '['; } }




© 2015 - 2024 Weber Informatics LLC | Privacy Policy