001: /*
002: * DO NOT ALTER OR REMOVE COPYRIGHT NOTICES OR THIS HEADER.
003: *
004: * Copyright 1997-2007 Sun Microsystems, Inc. All rights reserved.
005: *
006: * The contents of this file are subject to the terms of either the GNU
007: * General Public License Version 2 only ("GPL") or the Common
008: * Development and Distribution License("CDDL") (collectively, the
009: * "License"). You may not use this file except in compliance with the
010: * License. You can obtain a copy of the License at
011: * http://www.netbeans.org/cddl-gplv2.html
012: * or nbbuild/licenses/CDDL-GPL-2-CP. See the License for the
013: * specific language governing permissions and limitations under the
014: * License. When distributing the software, include this License Header
015: * Notice in each file and include the License file at
016: * nbbuild/licenses/CDDL-GPL-2-CP. Sun designates this
017: * particular file as subject to the "Classpath" exception as provided
018: * by Sun in the GPL Version 2 section of the License file that
019: * accompanied this code. If applicable, add the following below the
020: * License Header, with the fields enclosed by brackets [] replaced by
021: * your own identifying information:
022: * "Portions Copyrighted [year] [name of copyright owner]"
023: *
024: * Contributor(s):
025: *
026: * The Original Software is NetBeans. The Initial Developer of the Original
027: * Software is Sun Microsystems, Inc. Portions Copyright 1997-2007 Sun
028: * Microsystems, Inc. All Rights Reserved.
029: *
030: * If you wish your version of this file to be governed by only the CDDL
031: * or only the GPL Version 2, indicate your decision by adding
032: * "[Contributor] elects to include this software in this distribution
033: * under the [CDDL or GPL Version 2] license." If you do not indicate a
034: * single choice of license, a recipient has the option to distribute
035: * your version of this file under either the CDDL, the GPL Version 2 or
036: * to extend the choice of license to its licensees as provided above.
037: * However, if you add GPL Version 2 code and therefore, elected the GPL
038: * Version 2 license, then the option applies only if the new code is
039: * made subject to such option by the copyright holder.
040: */
041:
042: package org.netbeans.api.java.source;
043:
044: import com.sun.tools.javac.api.JavacTaskImpl;
045: import com.sun.tools.javac.code.Symtab;
046: import com.sun.tools.javac.jvm.Target;
047: import com.sun.tools.javac.model.JavacElements;
048: import java.util.List;
049: import javax.lang.model.element.Element;
050: import javax.lang.model.element.ElementKind;
051: import javax.lang.model.element.ExecutableElement;
052: import javax.lang.model.element.PackageElement;
053: import javax.lang.model.element.TypeElement;
054: import javax.lang.model.element.TypeParameterElement;
055: import javax.lang.model.element.VariableElement;
056: import javax.lang.model.type.DeclaredType;
057: import javax.lang.model.type.TypeKind;
058: import org.netbeans.modules.java.source.ElementHandleAccessor;
059: import org.netbeans.modules.java.source.usages.ClassFileUtil;
060:
061: /**
062: * Represents a handle for {@link Element} which can be kept and later resolved
063: * by another javac. The javac {@link Element}s are valid only in a single
064: * {@link javax.tools.CompilationTask} or a single run of a
065: * {@link CancellableTask}. A client needing to
066: * keep a reference to an {@link Element} and use it in another {@link CancellableTask}
067: * must serialize it into an {@link ElementHandle}.
068: * Currently not all {@link Element}s can be serialized. See {@link #create} for details.
069: * <div class="nonnormative">
070: * <p>
071: * Typical usage of {@link ElementHandle} is as follows:
072: * </p>
073: * <pre>
074: * final ElementHandle[] elementHandle = new ElementHandle[1];
075: * javaSource.runUserActionTask(new Task<CompilationController>() {
076: * public void run(CompilationController compilationController) {
077: * compilationController.toPhase(Phase.RESOLVED);
078: * CompilationUnitTree cu = compilationController.getTree();
079: * List<? extends Tree> types = getTypeDecls(cu);
080: * Tree tree = getInterestingElementTree(types);
081: * Element element = compilationController.getElement(tree);
082: * elementHandle[0] = ElementHandle.create(element);
083: * }
084: * }, true);
085: *
086: * otherJavaSource.runUserActionTask(new Task<CompilationController>() {
087: * public void run(CompilationController compilationController) {
088: * compilationController.toPhase(Phase.RESOLVED);
089: * Element element = elementHandle[0].resolve(compilationController);
090: * // ....
091: * }
092: * }, true);
093: * </pre>
094: * </div>
095: * @author Tomas Zezula
096: */
097: public final class ElementHandle<T extends Element> {
098:
099: static {
100: ElementHandleAccessor.INSTANCE = new ElementHandleAccessorImpl();
101: }
102:
103: private ElementKind kind;
104: private String[] signatures;
105:
106: private ElementHandle(final ElementKind kind, String[] signatures) {
107: assert kind != null;
108: assert signatures != null;
109: this .kind = kind;
110: this .signatures = signatures;
111: }
112:
113: /**
114: * Resolves an {@link Element} from the {@link ElementHandle}.
115: * @param compilationInfo representing the {@link javax.tools.CompilationTask}
116: * in which the {@link Element} should be resolved.
117: * @return resolved subclass of {@link Element} or null if the elment does not exist on
118: * the classpath/sourcepath of {@link javax.tools.CompilationTask}.
119: */
120: @SuppressWarnings("unchecked")
121: // NOI18N
122: public T resolve(final CompilationInfo compilationInfo) {
123: assert compilationInfo != null;
124: return resolveImpl(compilationInfo.impl.getJavacTask());
125: }
126:
127: private T resolveImpl(final JavacTaskImpl jt) {
128:
129: switch (this .kind) {
130: case PACKAGE:
131: assert signatures.length == 1;
132: return (T) jt.getElements()
133: .getPackageElement(signatures[0]);
134: case CLASS:
135: case INTERFACE:
136: case ENUM:
137: case ANNOTATION_TYPE:
138: case OTHER:
139: assert signatures.length == 1;
140: return (T) getTypeElementByBinaryName(signatures[0], jt);
141: case METHOD:
142: case CONSTRUCTOR: {
143: assert signatures.length == 3;
144: final TypeElement type = getTypeElementByBinaryName(
145: signatures[0], jt);
146: if (type != null) {
147: final List<? extends Element> members = type
148: .getEnclosedElements();
149: for (Element member : members) {
150: if (this .kind == member.getKind()) {
151: String[] desc = ClassFileUtil
152: .createExecutableDescriptor((ExecutableElement) member);
153: assert desc.length == 3;
154: if (this .signatures[1].equals(desc[1])
155: && this .signatures[2].equals(desc[2])) {
156: return (T) member;
157: }
158: }
159: }
160: }
161: break;
162: }
163: case INSTANCE_INIT:
164: case STATIC_INIT: {
165: assert signatures.length == 2;
166: final TypeElement type = getTypeElementByBinaryName(
167: signatures[0], jt);
168: if (type != null) {
169: final List<? extends Element> members = type
170: .getEnclosedElements();
171: for (Element member : members) {
172: if (this .kind == member.getKind()) {
173: String[] desc = ClassFileUtil
174: .createExecutableDescriptor((ExecutableElement) member);
175: assert desc.length == 2;
176: if (this .signatures[1].equals(desc[1])) {
177: return (T) member;
178: }
179: }
180: }
181: }
182: break;
183: }
184: case FIELD:
185: case ENUM_CONSTANT: {
186: assert signatures.length == 3;
187: final TypeElement type = getTypeElementByBinaryName(
188: signatures[0], jt);
189: if (type != null) {
190: final List<? extends Element> members = type
191: .getEnclosedElements();
192: for (Element member : members) {
193: if (this .kind == member.getKind()) {
194: String[] desc = ClassFileUtil
195: .createFieldDescriptor((VariableElement) member);
196: assert desc.length == 3;
197: if (this .signatures[1].equals(desc[1])
198: && this .signatures[2].equals(desc[2])) {
199: return (T) member;
200: }
201: }
202: }
203: }
204: break;
205: }
206: case TYPE_PARAMETER: {
207: if (signatures.length == 2) {
208: TypeElement type = getTypeElementByBinaryName(
209: signatures[0], jt);
210: if (type != null) {
211: List<? extends TypeParameterElement> tpes = type
212: .getTypeParameters();
213: for (TypeParameterElement tpe : tpes) {
214: if (tpe.getSimpleName().contentEquals(
215: signatures[1])) {
216: return (T) tpe;
217: }
218: }
219: }
220: } else if (signatures.length == 4) {
221: final TypeElement type = getTypeElementByBinaryName(
222: signatures[0], jt);
223: if (type != null) {
224: final List<? extends Element> members = type
225: .getEnclosedElements();
226: for (Element member : members) {
227: if (member.getKind() == ElementKind.METHOD
228: || member.getKind() == ElementKind.CONSTRUCTOR) {
229: String[] desc = ClassFileUtil
230: .createExecutableDescriptor((ExecutableElement) member);
231: assert desc.length == 3;
232: if (this .signatures[1].equals(desc[1])
233: && this .signatures[2]
234: .equals(desc[2])) {
235: assert member instanceof ExecutableElement;
236: List<? extends TypeParameterElement> tpes = ((ExecutableElement) member)
237: .getTypeParameters();
238: for (TypeParameterElement tpe : tpes) {
239: if (tpe.getSimpleName()
240: .contentEquals(
241: signatures[3])) {
242: return (T) tpe;
243: }
244: }
245: }
246: }
247: }
248: }
249: } else {
250: throw new IllegalStateException();
251: }
252: break;
253: }
254: default:
255: throw new IllegalStateException();
256: }
257: return null;
258: }
259:
260: /**
261: * Tests if the handle has the same signature as the parameter.
262: * The handles with the same signatures are resolved into the same
263: * element in the same {@link javax.tools.JavaCompiler} task, but may be resolved into
264: * the different {@link Element}s in the different {@link javax.tools.JavaCompiler} tasks.
265: * @param handle to be checked
266: * @return true if the handles resolve into the same {@link Element}s
267: * in the same {@link javax.tools.JavaCompiler} task.
268: */
269: public boolean signatureEquals(
270: final ElementHandle<? extends Element> handle) {
271: if (!isSameKind(this .kind, handle.kind)
272: || this .signatures.length != handle.signatures.length) {
273: return false;
274: }
275: for (int i = 0; i < signatures.length; i++) {
276: if (!signatures[i].equals(handle.signatures[i])) {
277: return false;
278: }
279: }
280: return true;
281: }
282:
283: private static boolean isSameKind(ElementKind k1, ElementKind k2) {
284: if ((k1 == k2)
285: || (k1 == ElementKind.OTHER && (k2.isClass() || k2
286: .isInterface()))
287: || (k2 == ElementKind.OTHER && (k1.isClass() || k1
288: .isInterface()))) {
289: return true;
290: }
291: return false;
292: }
293:
294: /**
295: * Returns a binary name of the {@link TypeElement} represented by this
296: * {@link ElementHandle}. When the {@link ElementHandle} doesn't represent
297: * a {@link TypeElement} it throws a {@link IllegalStateException}
298: * @return the qualified name
299: * @throws an {@link IllegalStateException} when this {@link ElementHandle}
300: * isn't creatred for the {@link TypeElement}.
301: */
302: public String getBinaryName() throws IllegalStateException {
303: if ((this .kind.isClass() && !isArray(signatures[0]))
304: || this .kind.isInterface()
305: || this .kind == ElementKind.OTHER) {
306: return this .signatures[0];
307: } else {
308: throw new IllegalStateException();
309: }
310: }
311:
312: /**
313: * Returns a qualified name of the {@link TypeElement} represented by this
314: * {@link ElementHandle}. When the {@link ElementHandle} doesn't represent
315: * a {@link TypeElement} it throws a {@link IllegalStateException}
316: * @return the qualified name
317: * @throws an {@link IllegalStateException} when this {@link ElementHandle}
318: * isn't creatred for the {@link TypeElement}.
319: */
320: public String getQualifiedName() throws IllegalStateException {
321: if ((this .kind.isClass() && !isArray(signatures[0]))
322: || this .kind.isInterface()
323: || this .kind == ElementKind.OTHER) {
324: return this .signatures[0].replace(Target.DEFAULT
325: .syntheticNameChar(), '.'); //NOI18N
326: } else {
327: throw new IllegalStateException();
328: }
329: }
330:
331: /**
332: * Tests if the handle has this same signature as the parameter.
333: * The handles has the same signatures if it is resolved into the same
334: * element in the same {@link javax.tools.JavaCompiler} task, but may be resolved into
335: * the different {@link Element} in the different {@link javax.tools.JavaCompiler} task.
336: * @param element to be checked
337: * @return true if this handle resolves into the same {@link Element}
338: * in the same {@link javax.tools.JavaCompiler} task.
339: */
340: public boolean signatureEquals(final T element) {
341: final ElementKind ek = element.getKind();
342: final ElementKind this Kind = getKind();
343: if ((ek != this Kind)
344: && !(this Kind == ElementKind.OTHER && (ek.isClass() || ek
345: .isInterface()))) {
346: return false;
347: }
348: final ElementHandle<T> handle = create(element);
349: return signatureEquals(handle);
350: }
351:
352: /**
353: * Returns the {@link ElementKind} of this element handle,
354: * it is the kind of the {@link Element} from which the handle
355: * was created.
356: * @return {@link ElementKind}
357: *
358: */
359: public ElementKind getKind() {
360: return this .kind;
361: }
362:
363: /**
364: * Factory method for creating {@link ElementHandle}.
365: * @param element for which the {@link ElementHandle} should be created. Permitted
366: * {@link ElementKind}s
367: * are: {@link ElementKind#PACKAGE}, {@link ElementKind#CLASS},
368: * {@link ElementKind#INTERFACE}, {@link ElementKind#ENUM}, {@link ElementKind#ANNOTATION_TYPE}, {@link ElementKind#METHOD},
369: * {@link ElementKind#CONSTRUCTOR}, {@link ElementKind#INSTANCE_INIT}, {@link ElementKind#STATIC_INIT},
370: * {@link ElementKind#FIELD}, and {@link ElementKind#ENUM_CONSTANT}.
371: * @return a new {@link ElementHandle}
372: * @throws IllegalArgumentException if the element is of an unsupported {@link ElementKind}
373: */
374: public static <T extends Element> ElementHandle<T> create(
375: final T element) throws IllegalArgumentException {
376: assert element != null;
377: ElementKind kind = element.getKind();
378: String[] signatures;
379: switch (kind) {
380: case PACKAGE:
381: assert element instanceof PackageElement;
382: signatures = new String[] { ((PackageElement) element)
383: .getQualifiedName().toString() };
384: break;
385: case CLASS:
386: case INTERFACE:
387: case ENUM:
388: case ANNOTATION_TYPE:
389: assert element instanceof TypeElement;
390: signatures = new String[] { ClassFileUtil
391: .encodeClassNameOrArray((TypeElement) element) };
392: break;
393: case METHOD:
394: case CONSTRUCTOR:
395: case INSTANCE_INIT:
396: case STATIC_INIT:
397: assert element instanceof ExecutableElement;
398: signatures = ClassFileUtil
399: .createExecutableDescriptor((ExecutableElement) element);
400: break;
401: case FIELD:
402: case ENUM_CONSTANT:
403: assert element instanceof VariableElement;
404: signatures = ClassFileUtil
405: .createFieldDescriptor((VariableElement) element);
406: break;
407: case TYPE_PARAMETER:
408: assert element instanceof TypeParameterElement;
409: TypeParameterElement tpe = (TypeParameterElement) element;
410: Element ge = tpe.getGenericElement();
411: ElementKind gek = ge.getKind();
412: if (gek.isClass() || gek.isInterface()) {
413: assert ge instanceof TypeElement;
414: signatures = new String[2];
415: signatures[0] = ClassFileUtil
416: .encodeClassNameOrArray((TypeElement) ge);
417: signatures[1] = tpe.getSimpleName().toString();
418: } else if (gek == ElementKind.METHOD
419: || gek == ElementKind.CONSTRUCTOR) {
420: assert ge instanceof ExecutableElement;
421: String[] _sigs = ClassFileUtil
422: .createExecutableDescriptor((ExecutableElement) ge);
423: signatures = new String[_sigs.length + 1];
424: System.arraycopy(_sigs, 0, signatures, 0, _sigs.length);
425: signatures[_sigs.length] = tpe.getSimpleName()
426: .toString();
427: } else {
428: throw new IllegalArgumentException(gek.toString());
429: }
430: break;
431: default:
432: throw new IllegalArgumentException(kind.toString());
433: }
434: return new ElementHandle<T>(kind, signatures);
435: }
436:
437: /**
438: * Gets {@link ElementHandle} from {@link TypeMirrorHandle} representing {@link DeclaredType}.
439: * @param typeMirrorHandle from which the {@link ElementHandle} should be retrieved. Permitted
440: * {@link TypeKind} is {@link TypeKind#DECLARED}.
441: * @return an {@link ElementHandle}
442: * @since 0.29.0
443: */
444: public static ElementHandle<? extends TypeElement> from(
445: final TypeMirrorHandle<? extends DeclaredType> typeMirrorHandle) {
446: assert typeMirrorHandle.getKind() == TypeKind.DECLARED;
447: return (ElementHandle<TypeElement>) typeMirrorHandle
448: .getElementHandle();
449: }
450:
451: public @Override
452: String toString() {
453: final StringBuilder result = new StringBuilder();
454: result.append(this .getClass().getSimpleName());
455: result.append('['); // NOI18N
456: result.append("kind=" + this .kind.toString()); // NOI18N
457: result.append("; sigs="); // NOI18N
458: for (String sig : this .signatures) {
459: result.append(sig);
460: result.append(' '); // NOI18N
461: }
462: result.append(']'); // NOI18N
463: return result.toString();
464: }
465:
466: /**@inheritDoc*/
467: @Override
468: public int hashCode() {
469: int hashCode = 0;
470:
471: for (String sig : signatures) {
472: hashCode = hashCode ^ (sig != null ? sig.hashCode() : 0);
473: }
474:
475: return hashCode;
476: }
477:
478: /**@inheritDoc*/
479: @Override
480: public boolean equals(Object other) {
481: if (other instanceof ElementHandle) {
482: return signatureEquals((ElementHandle) other);
483: }
484: return false;
485: }
486:
487: /**
488: * Returns the element signature.
489: * Package private, used by ClassIndex.
490: */
491: String[] getSignature() {
492: return this .signatures;
493: }
494:
495: private static class ElementHandleAccessorImpl extends
496: ElementHandleAccessor {
497:
498: public ElementHandle create(ElementKind kind,
499: String... descriptors) {
500: assert kind != null;
501: assert descriptors != null;
502: switch (kind) {
503: case PACKAGE:
504: if (descriptors.length != 1) {
505: throw new IllegalArgumentException();
506: }
507: return new ElementHandle<PackageElement>(kind,
508: descriptors);
509: case CLASS:
510: case INTERFACE:
511: case ENUM:
512: case ANNOTATION_TYPE:
513: case OTHER:
514: if (descriptors.length != 1) {
515: throw new IllegalArgumentException();
516: }
517: return new ElementHandle<TypeElement>(kind, descriptors);
518: case METHOD:
519: case CONSTRUCTOR:
520: if (descriptors.length != 3) {
521: throw new IllegalArgumentException();
522: }
523: return new ElementHandle<ExecutableElement>(kind,
524: descriptors);
525: case INSTANCE_INIT:
526: case STATIC_INIT:
527: if (descriptors.length != 2) {
528: throw new IllegalArgumentException();
529: }
530: return new ElementHandle<ExecutableElement>(kind,
531: descriptors);
532: case FIELD:
533: case ENUM_CONSTANT:
534: if (descriptors.length != 3) {
535: throw new IllegalArgumentException();
536: }
537: return new ElementHandle<VariableElement>(kind,
538: descriptors);
539: default:
540: throw new IllegalArgumentException();
541: }
542: }
543:
544: public <T extends Element> T resolve(ElementHandle<T> handle,
545: JavacTaskImpl jti) {
546: return handle.resolveImpl(jti);
547: }
548: }
549:
550: private static TypeElement getTypeElementByBinaryName(
551: final String signature, final JavacTaskImpl jt) {
552: if (isArray(signature)) {
553: return Symtab.instance(jt.getContext()).arrayClass;
554: } else {
555: final JavacElements elements = jt.getElements();
556: return (TypeElement) elements
557: .getTypeElementByBinaryName(signature);
558: }
559: }
560:
561: private static boolean isArray(String signature) {
562: return signature.length() == 1 && signature.charAt(0) == '[';
563: }
564:
565: }
|