Many resources are needed to download a project. Please understand that we have to compensate our server costs. Thank you in advance. Project price only 1 $
You can buy this project and download/modify it how often you want.
/*******************************************************************************
* Copyright (c) 2006, 2008 BEA Systems, Inc.
* All rights reserved. This program and the accompanying materials
* are made available under the terms of the Eclipse Public License v1.0
* which accompanies this distribution, and is available at
* http://www.eclipse.org/legal/epl-v10.html
*
* Contributors:
* [email protected] - initial API and implementation
*
*******************************************************************************/
package org.eclipse.jdt.internal.compiler.apt.model;
import java.io.IOException;
import java.io.Writer;
import java.util.ArrayList;
import java.util.Collections;
import java.util.HashMap;
import java.util.HashSet;
import java.util.LinkedHashSet;
import java.util.List;
import java.util.Map;
import java.util.Set;
import java.util.regex.Matcher;
import java.util.regex.Pattern;
import javax.lang.model.element.AnnotationMirror;
import javax.lang.model.element.AnnotationValue;
import javax.lang.model.element.Element;
import javax.lang.model.element.ElementKind;
import javax.lang.model.element.ExecutableElement;
import javax.lang.model.element.Name;
import javax.lang.model.element.PackageElement;
import javax.lang.model.element.TypeElement;
import javax.lang.model.util.Elements;
import org.eclipse.jdt.core.compiler.CharOperation;
import org.eclipse.jdt.internal.compiler.apt.dispatch.BaseProcessingEnvImpl;
import org.eclipse.jdt.internal.compiler.ast.AbstractMethodDeclaration;
import org.eclipse.jdt.internal.compiler.ast.FieldDeclaration;
import org.eclipse.jdt.internal.compiler.ast.Javadoc;
import org.eclipse.jdt.internal.compiler.ast.TypeDeclaration;
import org.eclipse.jdt.internal.compiler.impl.ReferenceContext;
import org.eclipse.jdt.internal.compiler.lookup.AnnotationBinding;
import org.eclipse.jdt.internal.compiler.lookup.FieldBinding;
import org.eclipse.jdt.internal.compiler.lookup.LocalVariableBinding;
import org.eclipse.jdt.internal.compiler.lookup.LookupEnvironment;
import org.eclipse.jdt.internal.compiler.lookup.MethodBinding;
import org.eclipse.jdt.internal.compiler.lookup.MethodVerifier;
import org.eclipse.jdt.internal.compiler.lookup.PackageBinding;
import org.eclipse.jdt.internal.compiler.lookup.ReferenceBinding;
import org.eclipse.jdt.internal.compiler.lookup.SourceTypeBinding;
import org.eclipse.jdt.internal.compiler.lookup.TagBits;
import org.eclipse.jdt.internal.compiler.lookup.TypeConstants;
/**
* Utilities for working with language elements.
* There is one of these for every ProcessingEnvironment.
*/
public class ElementsImpl implements Elements {
// Used for parsing Javadoc comments: matches initial delimiter, followed by whitespace
private static final Pattern INITIAL_DELIMITER = Pattern.compile("^\\s*/\\*+"); //$NON-NLS-1$
private final BaseProcessingEnvImpl _env;
/*
* The processing env creates and caches an ElementsImpl. Other clients should
* not create their own; they should ask the env for it.
*/
public ElementsImpl(BaseProcessingEnvImpl env) {
_env = env;
}
/**
* Return all the annotation mirrors on this element, including inherited annotations.
* Annotations are inherited only if the annotation type is meta-annotated with @Inherited,
* and the annotation is on a class: e.g., annotations are not inherited for interfaces, methods,
* or fields.
*/
@Override
public List extends AnnotationMirror> getAllAnnotationMirrors(Element e) {
// if e is a class, walk up its superclass hierarchy looking for @Inherited annotations not already in the list
if (e.getKind() == ElementKind.CLASS && e instanceof TypeElementImpl) {
List annotations = new ArrayList();
// A class can only have one annotation of a particular annotation type.
Set annotationTypes = new HashSet();
ReferenceBinding binding = (ReferenceBinding)((TypeElementImpl)e)._binding;
while (null != binding) {
for (AnnotationBinding annotation : binding.getAnnotations()) {
if (annotation == null) continue;
ReferenceBinding annotationType = annotation.getAnnotationType();
if (!annotationTypes.contains(annotationType)) {
annotationTypes.add(annotationType);
annotations.add(annotation);
}
}
binding = binding.superclass();
}
List list = new ArrayList(annotations.size());
for (AnnotationBinding annotation : annotations) {
list.add(_env.getFactory().newAnnotationMirror(annotation));
}
return Collections.unmodifiableList(list);
}
else {
return e.getAnnotationMirrors();
}
}
/**
* Compute a list of all the visible entities in this type. Specifically:
*
*
All nested types declared in this type, including interfaces and enums
*
All protected or public nested types declared in this type's superclasses
* and superinterfaces, that are not hidden by a name collision
*
All methods declared in this type, including constructors but not
* including static or instance initializers, and including abstract
* methods and unimplemented methods declared in interfaces
*
All protected or public methods declared in this type's superclasses,
* that are not overridden by another method, but not including constructors
* or initializers. Includes abstract methods and methods declared in
* superinterfaces but not implemented
*
All fields declared in this type, including constants
*
All non-private fields declared in this type's superclasses and
* superinterfaces, that are not hidden by a name collision.
*
*/
@Override
public List extends Element> getAllMembers(TypeElement type) {
if (null == type || !(type instanceof TypeElementImpl)) {
return Collections.emptyList();
}
ReferenceBinding binding = (ReferenceBinding)((TypeElementImpl)type)._binding;
// Map of element simple name to binding
Map types = new HashMap();
// Javac implementation does not take field name collisions into account
List fields = new ArrayList();
// For methods, need to compare parameters, not just names
Map> methods = new HashMap>();
Set superinterfaces = new LinkedHashSet();
boolean ignoreVisibility = true;
while (null != binding) {
addMembers(binding, ignoreVisibility, types, fields, methods);
Set newfound = new LinkedHashSet();
collectSuperInterfaces(binding, superinterfaces, newfound);
for (ReferenceBinding superinterface : newfound) {
addMembers(superinterface, false, types, fields, methods);
}
superinterfaces.addAll(newfound);
binding = binding.superclass();
ignoreVisibility = false;
}
List allMembers = new ArrayList();
for (ReferenceBinding nestedType : types.values()) {
allMembers.add(_env.getFactory().newElement(nestedType));
}
for (FieldBinding field : fields) {
allMembers.add(_env.getFactory().newElement(field));
}
for (Set sameNamedMethods : methods.values()) {
for (MethodBinding method : sameNamedMethods) {
allMembers.add(_env.getFactory().newElement(method));
}
}
return allMembers;
}
/**
* Recursively depth-first walk the tree of superinterfaces of a type, collecting
* all the unique superinterface bindings. (Note that because of generics, a type may
* have multiple unique superinterface bindings corresponding to the same interface
* declaration.)
* @param existing bindings already in this set will not be re-added or recursed into
* @param newfound newly found bindings will be added to this set
*/
private void collectSuperInterfaces(ReferenceBinding type,
Set existing, Set newfound) {
for (ReferenceBinding superinterface : type.superInterfaces()) {
if (!existing.contains(superinterface) && !newfound.contains(superinterface)) {
newfound.add(superinterface);
collectSuperInterfaces(superinterface, existing, newfound);
}
}
}
/**
* Add the members of a type to the maps of subtypes, fields, and methods. Add only those
* which are non-private and which are not overridden by an already-discovered member.
* For fields, add them all; javac implementation does not take field hiding into account.
* @param binding the type whose members will be added to the lists
* @param ignoreVisibility if true, all members will be added regardless of whether they
* are private, overridden, etc.
* @param types a map of type simple name to type binding
* @param fields a list of field bindings
* @param methods a map of method simple name to set of method bindings with that name
*/
private void addMembers(ReferenceBinding binding, boolean ignoreVisibility, Map types,
List fields, Map> methods)
{
for (ReferenceBinding subtype : binding.memberTypes()) {
if (ignoreVisibility || !subtype.isPrivate()) {
String name = new String(subtype.sourceName());
if (null == types.get(name)) {
types.put(name, subtype);
}
}
}
for (FieldBinding field : binding.fields()) {
if (ignoreVisibility || !field.isPrivate()) {
fields.add(field);
}
}
for (MethodBinding method : binding.methods()) {
if (!method.isSynthetic() && (ignoreVisibility || (!method.isPrivate() && !method.isConstructor()))) {
String methodName = new String(method.selector);
Set sameNamedMethods = methods.get(methodName);
if (null == sameNamedMethods) {
// New method name. Create a set for it and add it to the list.
// We don't expect many methods with same name, so only 4 slots:
sameNamedMethods = new HashSet(4);
methods.put(methodName, sameNamedMethods);
sameNamedMethods.add(method);
}
else {
// We already have a method with this name. Is this method overridden?
boolean unique = true;
if (!ignoreVisibility) {
for (MethodBinding existing : sameNamedMethods) {
MethodVerifier verifier = _env.getLookupEnvironment().methodVerifier();
if (verifier.doesMethodOverride(existing, method)) {
unique = false;
break;
}
}
}
if (unique) {
sameNamedMethods.add(method);
}
}
}
}
}
/* (non-Javadoc)
* @see javax.lang.model.util.Elements#getBinaryName(javax.lang.model.element.TypeElement)
*/
@Override
public Name getBinaryName(TypeElement type) {
TypeElementImpl typeElementImpl = (TypeElementImpl) type;
ReferenceBinding referenceBinding = (ReferenceBinding) typeElementImpl._binding;
return new NameImpl(
CharOperation.replaceOnCopy(referenceBinding.constantPoolName(), '/', '.'));
}
/* (non-Javadoc)
* @see javax.lang.model.util.Elements#getConstantExpression(java.lang.Object)
*/
@Override
public String getConstantExpression(Object value) {
if (!(value instanceof Integer)
&& !(value instanceof Byte)
&& !(value instanceof Float)
&& !(value instanceof Double)
&& !(value instanceof Long)
&& !(value instanceof Short)
&& !(value instanceof Character)
&& !(value instanceof String)
&& !(value instanceof Boolean)) {
throw new IllegalArgumentException("Not a valid wrapper type : " + value.getClass()); //$NON-NLS-1$
}
if (value instanceof Character) {
StringBuilder builder = new StringBuilder();
builder.append('\'').append(value).append('\'');
return String.valueOf(builder);
}
return String.valueOf(value);
}
/* (non-Javadoc)
* @see javax.lang.model.util.Elements#getDocComment(javax.lang.model.element.Element)
*/
@Override
public String getDocComment(Element e) {
char[] unparsed = getUnparsedDocComment(e);
return formatJavadoc(unparsed);
}
/**
* Return the entire javadoc comment on e, including the comment characters and whitespace
* @param e an Element of any sort, possibly with a javadoc comment.
* @return a String, or null if the comment is not available
*/
private char[] getUnparsedDocComment(Element e)
{
Javadoc javadoc = null;
ReferenceContext referenceContext = null;
switch(e.getKind()) {
case ANNOTATION_TYPE :
case CLASS :
case ENUM :
case INTERFACE :
TypeElementImpl typeElementImpl = (TypeElementImpl) e;
ReferenceBinding referenceBinding = (ReferenceBinding)typeElementImpl._binding;
if (referenceBinding instanceof SourceTypeBinding) {
SourceTypeBinding sourceTypeBinding = (SourceTypeBinding) referenceBinding;
referenceContext = sourceTypeBinding.scope.referenceContext;
javadoc = ((TypeDeclaration) referenceContext).javadoc;
}
break;
case PACKAGE :
// might need to handle javadoc of package-info.java file
PackageElementImpl packageElementImpl = (PackageElementImpl) e;
PackageBinding packageBinding = (PackageBinding) packageElementImpl._binding;
char[][] compoundName = CharOperation.arrayConcat(packageBinding.compoundName, TypeConstants.PACKAGE_INFO_NAME);
ReferenceBinding type = this._env.getLookupEnvironment().getType(compoundName);
if (type != null && type.isValidBinding() && (type instanceof SourceTypeBinding)) {
SourceTypeBinding sourceTypeBinding = (SourceTypeBinding) type;
referenceContext = sourceTypeBinding.scope.referenceContext;
javadoc = ((TypeDeclaration) referenceContext).javadoc;
}
break;
case CONSTRUCTOR :
case METHOD :
ExecutableElementImpl executableElementImpl = (ExecutableElementImpl) e;
MethodBinding methodBinding = (MethodBinding) executableElementImpl._binding;
AbstractMethodDeclaration sourceMethod = methodBinding.sourceMethod();
if (sourceMethod != null) {
javadoc = sourceMethod.javadoc;
referenceContext = sourceMethod;
}
break;
case ENUM_CONSTANT :
case FIELD :
VariableElementImpl variableElementImpl = (VariableElementImpl) e;
FieldBinding fieldBinding = (FieldBinding) variableElementImpl._binding;
FieldDeclaration sourceField = fieldBinding.sourceField();
if (sourceField != null) {
javadoc = sourceField.javadoc;
if (fieldBinding.declaringClass instanceof SourceTypeBinding) {
SourceTypeBinding sourceTypeBinding = (SourceTypeBinding) fieldBinding.declaringClass;
referenceContext = sourceTypeBinding.scope.referenceContext;
}
}
}
if (javadoc != null && referenceContext != null) {
char[] contents = referenceContext.compilationResult().getCompilationUnit().getContents();
if (contents != null) {
return CharOperation.subarray(contents, javadoc.sourceStart, javadoc.sourceEnd - 1);
}
}
return null;
}
/**
* Strip the comment characters from a javadoc comment. Assume the comment is already
* missing its closing delimiter.
*
* Javac's behavior with regard to tab expansion and trimming of whitespace and
* asterisks is bizarre and undocumented. We do our best here to emulate it.
*/
private static String formatJavadoc(char[] unparsed)
{
if (unparsed == null || unparsed.length < 5) { // delimiters take 5 chars
return null;
}
String[] lines = new String(unparsed).split("\n"); //$NON-NLS-1$
Matcher delimiterMatcher = INITIAL_DELIMITER.matcher(lines[0]);
if (!delimiterMatcher.find()) {
return null;
}
int iOpener = delimiterMatcher.end();
lines[0] = lines[0].substring(iOpener);
if (lines.length == 1) {
// single-line comment. Should trim(), but javac doesn't.
// we should however remove the starting whitespaces
StringBuilder sb = new StringBuilder();
char[] chars = lines[0].toCharArray();
boolean startingWhitespaces = true;
for (char c : chars) {
if (Character.isWhitespace(c))
if (startingWhitespaces) {
continue;
} else {
sb.append(c);
} else {
startingWhitespaces = false;
sb.append(c);
}
}
return sb.toString();
}
// if the first line ends with spaces after the /** then we want to insert a line separator
int firstLine = lines[0].trim().length() > 0 ? 0 : 1;
// If the last line is now empty, skip it
int lastLine = lines[lines.length - 1].trim().length() > 0 ? lines.length - 1 : lines.length - 2;
StringBuilder sb = new StringBuilder();
if (lines[0].length() != 0 && firstLine == 1) {
// insert a line separator only if the remaining chars on the line are whitespaces
sb.append('\n');
}
boolean preserveLineSeparator = lines[0].length() == 0;
for (int line = firstLine; line <= lastLine; ++line) {
char[] chars = lines[line].toCharArray();
int starsIndex = getStars(chars);
int leadingWhitespaces = 0;
boolean recordLeadingWhitespaces = true;
for (int i = 0, max = chars.length; i < max; i++) {
char c = chars[i];
switch(c) {
case '\t' :
if (starsIndex == -1) {
if (recordLeadingWhitespaces) {
leadingWhitespaces += 8;
} else {
sb.append(c);
}
} else if (i >= starsIndex) {
sb.append(c);
}
break;
case ' ' :
if (starsIndex == -1) {
if (recordLeadingWhitespaces) {
leadingWhitespaces++;
} else {
sb.append(c);
}
} else if (i >= starsIndex) {
sb.append(c);
}
break;
default :
// convert leadingwhitespaces to spaces
recordLeadingWhitespaces = false;
if (leadingWhitespaces != 0) {
int numberOfTabs = leadingWhitespaces / 8;
if (numberOfTabs != 0) {
for (int j = 0, max2 = numberOfTabs; j < max2; j++) {
sb.append(" "); //$NON-NLS-1$
}
if ((leadingWhitespaces % 8) >= 1) {
sb.append(' ');
}
} else if (line != 0) {
// we don't want to preserve the leading spaces for the first line
for (int j = 0, max2 = leadingWhitespaces; j < max2; j++) {
sb.append(' ');
}
}
leadingWhitespaces = 0;
sb.append(c);
} else if (c != '*' || i > starsIndex) {
sb.append(c);
}
}
}
// append a newline at the end of each line except the last, even if we skipped the last entirely
int end = lines.length - 1;
if (line < end) {
sb.append('\n');
} else if (preserveLineSeparator && line == end) {
sb.append('\n');
}
}
return sb.toString();
}
/**
* Returns the index of the last leading stars on this line, -1 if none.
*
* @param line the given line
* @return the computed index
*/
private static int getStars(char[] line) {
loop: for (int i = 0, max = line.length; i < max; i++) {
char c = line[i];
if (!Character.isWhitespace(c)) {
if (c == '*') {
// only whitespaces before the first star
// consume all stars and return the last index
for (int j = i + 1; j < max; j++) {
if (line[j] != '*') {
return j;
}
}
return max - 1;
}
// no need to continue
break loop;
}
}
return -1;
}
/**
* @return all the annotation instance's explicitly set values, plus default values
* for all the annotation members that are not explicitly set but that have
* defaults. By comparison, {@link AnnotationMirror#getElementValues()} only
* returns the explicitly set values.
* @see javax.lang.model.util.Elements#getElementValuesWithDefaults(javax.lang.model.element.AnnotationMirror)
*/
@Override
public Map extends ExecutableElement, ? extends AnnotationValue> getElementValuesWithDefaults(
AnnotationMirror a) {
return ((AnnotationMirrorImpl)a).getElementValuesWithDefaults();
}
/* (non-Javadoc)
* @see javax.lang.model.util.Elements#getName(java.lang.CharSequence)
*/
@Override
public Name getName(CharSequence cs) {
return new NameImpl(cs);
}
@Override
public PackageElement getPackageElement(CharSequence name) {
LookupEnvironment le = _env.getLookupEnvironment();
if (name.length() == 0) {
return new PackageElementImpl(_env, le.defaultPackage);
}
char[] packageName = name.toString().toCharArray();
PackageBinding packageBinding = le.createPackage(CharOperation.splitOn('.', packageName));
if (packageBinding == null) {
return null;
}
return new PackageElementImpl(_env, packageBinding);
}
@Override
public PackageElement getPackageOf(Element type) {
switch(type.getKind()) {
case ANNOTATION_TYPE :
case CLASS :
case ENUM :
case INTERFACE :
TypeElementImpl typeElementImpl = (TypeElementImpl) type;
ReferenceBinding referenceBinding = (ReferenceBinding)typeElementImpl._binding;
return (PackageElement) _env.getFactory().newElement(referenceBinding.fPackage);
case PACKAGE :
return (PackageElement) type;
case CONSTRUCTOR :
case METHOD :
ExecutableElementImpl executableElementImpl = (ExecutableElementImpl) type;
MethodBinding methodBinding = (MethodBinding) executableElementImpl._binding;
return (PackageElement) _env.getFactory().newElement(methodBinding.declaringClass.fPackage);
case ENUM_CONSTANT :
case FIELD :
VariableElementImpl variableElementImpl = (VariableElementImpl) type;
FieldBinding fieldBinding = (FieldBinding) variableElementImpl._binding;
return (PackageElement) _env.getFactory().newElement(fieldBinding.declaringClass.fPackage);
case PARAMETER :
variableElementImpl = (VariableElementImpl) type;
LocalVariableBinding localVariableBinding = (LocalVariableBinding) variableElementImpl._binding;
return (PackageElement) _env.getFactory().newElement(localVariableBinding.declaringScope.classScope().referenceContext.binding.fPackage);
case EXCEPTION_PARAMETER :
case INSTANCE_INIT :
case OTHER :
case STATIC_INIT :
case TYPE_PARAMETER :
case LOCAL_VARIABLE :
return null;
}
// unreachable
return null;
}
/* (non-Javadoc)
* @see javax.lang.model.util.Elements#getTypeElement(java.lang.CharSequence)
*/
@Override
public TypeElement getTypeElement(CharSequence name) {
LookupEnvironment le = _env.getLookupEnvironment();
final char[][] compoundName = CharOperation.splitOn('.', name.toString().toCharArray());
ReferenceBinding binding = le.getType(compoundName);
// If we didn't find the binding, maybe it's a nested type;
// try finding the top-level type and then working downwards.
if (null == binding) {
ReferenceBinding topLevelBinding = null;
int topLevelSegments = compoundName.length;
while (--topLevelSegments > 0) {
char[][] topLevelName = new char[topLevelSegments][];
for (int i = 0; i < topLevelSegments; ++i) {
topLevelName[i] = compoundName[i];
}
topLevelBinding = le.getType(topLevelName);
if (null != topLevelBinding) {
break;
}
}
if (null == topLevelBinding) {
return null;
}
binding = topLevelBinding;
for (int i = topLevelSegments; null != binding && i < compoundName.length; ++i) {
binding = binding.getMemberType(compoundName[i]);
}
}
if (null == binding) {
return null;
}
return new TypeElementImpl(_env, binding, null);
}
/* (non-Javadoc)
* Element A hides element B if: A and B are both fields, both nested types, or both methods; and
* the enclosing element of B is a superclass or superinterface of the enclosing element of A.
* See JLS 8.3 (for hiding of fields), 8.4.8.2 (hiding of class methods), and 8.5 (for hiding of member types).
* @see javax.lang.model.util.Elements#hides(javax.lang.model.element.Element, javax.lang.model.element.Element)
*/
@Override
public boolean hides(Element hider, Element hidden) {
if (hidden == null) {
// required by API spec
throw new NullPointerException();
}
return ((ElementImpl)hider).hides(hidden);
}
/* (non-Javadoc)
* @see javax.lang.model.util.Elements#isDeprecated(javax.lang.model.element.Element)
*/
@Override
public boolean isDeprecated(Element e) {
if (!(e instanceof ElementImpl)) {
return false;
}
return (((ElementImpl)e)._binding.getAnnotationTagBits() & TagBits.AnnotationDeprecated) != 0;
}
/* (non-Javadoc)
* See JLS 8.4.8.1 for discussion of hiding of methods
* @see javax.lang.model.util.Elements#overrides(javax.lang.model.element.ExecutableElement, javax.lang.model.element.ExecutableElement, javax.lang.model.element.TypeElement)
*/
@Override
public boolean overrides(ExecutableElement overrider, ExecutableElement overridden,
TypeElement type) {
if (overridden == null || type == null) {
throw new NullPointerException();
}
return ((ExecutableElementImpl)overrider).overrides(overridden, type);
}
/* (non-Javadoc)
* @see javax.lang.model.util.Elements#printElements(java.io.Writer, javax.lang.model.element.Element[])
*/
@Override
public void printElements(Writer w, Element... elements) {
String lineSeparator = System.getProperty("line.separator"); //$NON-NLS-1$
for (Element element : elements) {
try {
w.write(element.toString());
w.write(lineSeparator);
} catch (IOException e) {
// ignore
}
}
try {
w.flush();
} catch (IOException e) {
// ignore
}
}
}