/*
* Copyright 2015 Lukas Krejci
*
* Licensed 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.revapi.java.model;
import java.util.Collections;
import java.util.List;
import java.util.Set;
import java.util.stream.Collectors;
import javax.annotation.Nonnull;
import javax.lang.model.element.Element;
import javax.lang.model.element.ExecutableElement;
import javax.lang.model.element.VariableElement;
import javax.lang.model.type.DeclaredType;
import javax.lang.model.type.TypeMirror;
import javax.lang.model.util.SimpleElementVisitor8;
import org.revapi.Archive;
import org.revapi.java.FlatFilter;
import org.revapi.java.compilation.ClassPathUseSite;
import org.revapi.java.compilation.ProbingEnvironment;
import org.revapi.java.spi.JavaModelElement;
import org.revapi.java.spi.JavaTypeElement;
import org.revapi.java.spi.UseSite;
import org.revapi.java.spi.Util;
/**
* @author Lukas Krejci
* @since 0.1
*/
public class TypeElement extends JavaElementBase<javax.lang.model.element.TypeElement, DeclaredType> implements JavaTypeElement {
private final String binaryName;
private final String canonicalName;
private Set<UseSite> useSites;
private Set<ClassPathUseSite> rawUseSites;
private boolean inApi;
private boolean inApiThroughUse;
/**
* This is a helper constructor used only during probing the class files. This is to ensure that we have a
* "bare bones" type element available even before we have functioning compilation infrastructure in
* the environment.
*
* @param env probing environment
* @param binaryName the binary name of the class
* @param canonicalName the canonical name of the class
*/
public TypeElement(ProbingEnvironment env, Archive archive, String binaryName, String canonicalName) {
super(env, archive, null, null);
this.binaryName = binaryName;
this.canonicalName = canonicalName;
}
/**
* This constructor is used under "normal working conditions" when the probing environment already has
* the compilation infrastructure available (which is assumed since otherwise it would not be possible to obtain
* instances of the javax.lang.model.element.TypeElement interface).
*
* @param env the probing environment
* @param element the model element to be represented
*/
public TypeElement(ProbingEnvironment env, Archive archive, javax.lang.model.element.TypeElement element, DeclaredType type) {
super(env, archive, element, type);
binaryName = env.getElementUtils().getBinaryName(element).toString();
canonicalName = element.getQualifiedName().toString();
}
@Nonnull
@Override
protected String getHumanReadableElementType() {
return "class";
}
public String getBinaryName() {
return binaryName;
}
public String getCanonicalName() {
return canonicalName;
}
@Override public boolean isInAPI() {
return inApi;
}
@Override public boolean isInApiThroughUse() {
return inApiThroughUse;
}
@Override public Set<UseSite> getUseSites() {
if (useSites == null) {
if (rawUseSites == null) {
useSites = Collections.emptySet();
} else {
useSites = rawUseSites.stream()
.map(u -> new UseSite(u.useType, getModel(u.site, u.indexInParent)))
.collect(Collectors.toSet());
}
rawUseSites = null;
}
return useSites;
}
public void setInApi(boolean inApi) {
this.inApi = inApi;
}
public void setInApiThroughUse(boolean inApiThroughUse) {
this.inApiThroughUse = inApiThroughUse;
}
public void setRawUseSites(Set<ClassPathUseSite> rawUseSites) {
this.rawUseSites = rawUseSites;
}
@Override
public int compareTo(@Nonnull org.revapi.Element o) {
if (!(o.getClass().equals(TypeElement.class))) {
return JavaElementFactory.compareByType(this, o);
}
return binaryName.compareTo(((TypeElement) o).binaryName);
}
@Override protected String createFullHumanReadableString() {
TypeMirror rep = getModelRepresentation();
return getHumanReadableElementType() + " " + (rep == null ? canonicalName : Util.toHumanReadableString(rep));
}
@Override
protected String createComparableSignature() {
//this isn't used, because compareTo is implemented differently
return null;
}
private JavaModelElement getModel(Element element, int indexInParent) {
return element.accept(new SimpleElementVisitor8<JavaModelElement, Void>() {
@Override public JavaModelElement visitVariable(VariableElement e, Void ignored) {
if (e.getEnclosingElement() instanceof javax.lang.model.element.TypeElement) {
//this is a field
TypeElement type = environment.getTypeMap().get(e.getEnclosingElement());
List<FieldElement> fs = type.searchChildren(FieldElement.class, false,
FlatFilter.by(f -> f.getDeclaringElement().equals(e)));
return fs.get(0);
} else if (e.getEnclosingElement() instanceof javax.lang.model.element.ExecutableElement) {
//this is a method parameter
Element methodEl = e.getEnclosingElement();
TypeElement type = environment.getTypeMap().get(methodEl.getEnclosingElement());
List<MethodElement> ms = type.searchChildren(MethodElement.class, false,
FlatFilter.by(m -> m.getDeclaringElement().equals(methodEl)));
MethodElement method = ms.get(0);
//now look for the parameter
List<MethodParameterElement> params =
method.searchChildren(MethodParameterElement.class, false, FlatFilter.by(p -> true));
return params.get(indexInParent);
} else {
return null;
}
}
@Override public JavaModelElement visitType(javax.lang.model.element.TypeElement e, Void ignored) {
return environment.getTypeMap().get(e);
}
@Override public JavaModelElement visitExecutable(ExecutableElement e, Void ignored) {
TypeElement type = environment.getTypeMap().get(e.getEnclosingElement());
List<MethodElement> ms = type.searchChildren(MethodElement.class, false,
FlatFilter.by(m -> m.getDeclaringElement().equals(e)));
return ms.get(0);
}
}, null);
}
}