/**
* Copyright © 2006-2016 Web Cohesion (info@webcohesion.com)
*
* 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 com.webcohesion.enunciate.javac.decorations.element;
import com.webcohesion.enunciate.javac.decorations.DecoratedProcessingEnvironment;
import com.webcohesion.enunciate.javac.decorations.ElementDecorator;
import com.webcohesion.enunciate.javac.decorations.TypeMirrorDecorator;
import javax.lang.model.element.*;
import javax.lang.model.type.DeclaredType;
import javax.lang.model.type.TypeMirror;
import javax.lang.model.util.ElementFilter;
import java.lang.annotation.Annotation;
import java.lang.annotation.Inherited;
import java.util.ArrayList;
import java.util.HashMap;
import java.util.List;
/**
* A decorated type declaration provides:
* <p/>
* <ul>
* <li>boolean properties for the "type" of type declaration.
* </ul>
*
* @author Ryan Heaton
*/
public class DecoratedTypeElement extends DecoratedElement<TypeElement> implements TypeElement {
private PackageElement pckg;
private List<PropertyElement> properties;
private TypeMirror superclass;
private List<? extends TypeMirror> interfaces;
private List<ExecutableElement> methods;
private List<ExecutableElement> constructors;
private List<VariableElement> enumConstants;
public DecoratedTypeElement(TypeElement delegate, DecoratedProcessingEnvironment env) {
super(delegate, env);
}
public PackageElement getPackage() {
if (this.pckg == null) {
this.pckg = ElementDecorator.decorate(this.env.getElementUtils().getPackageOf(this.delegate), this.env);
}
return this.pckg;
}
public List<? extends TypeParameterElement> getTypeParameters() {
return ElementDecorator.decorate(this.delegate.getTypeParameters(), this.env);
}
@Override
public NestingKind getNestingKind() {
return this.delegate.getNestingKind();
}
@Override
public Name getQualifiedName() {
return this.delegate.getQualifiedName();
}
@Override
public TypeMirror getSuperclass() {
if (this.superclass == null) {
this.superclass = TypeMirrorDecorator.decorate(this.delegate.getSuperclass(), env);
}
return this.superclass;
}
@Override
public List<? extends TypeMirror> getInterfaces() {
if (this.interfaces == null) {
this.interfaces = TypeMirrorDecorator.decorate(this.delegate.getInterfaces(), env);
}
return this.interfaces;
}
public List<ExecutableElement> getMethods() {
if (this.methods == null) {
this.methods = ElementDecorator.decorate(ElementFilter.methodsIn(this.delegate.getEnclosedElements()), this.env);
}
return this.methods;
}
public List<? extends VariableElement> getFields() {
List<VariableElement> fields = new ArrayList<VariableElement>();
List<VariableElement> allFields = ElementFilter.fieldsIn(this.delegate.getEnclosedElements());
for (VariableElement field : allFields) {
if (field.getKind() == ElementKind.FIELD && !(field.getModifiers().contains(Modifier.STATIC))) {
fields.add(field);
}
}
return fields;
}
public List<ExecutableElement> getConstructors() {
if (this.constructors == null) {
this.constructors = ElementDecorator.decorate(ElementFilter.constructorsIn(this.delegate.getEnclosedElements()), this.env);
}
return constructors;
}
public List<PropertyElement> getProperties() {
return getProperties(new ElementUtils.DefaultPropertySpec(this.env));
}
public List<PropertyElement> getProperties(PropertySpec spec) {
if (this.properties == null) {
this.properties = loadProperties(spec);
}
return this.properties;
}
public List<VariableElement> enumValues() {
if (this.enumConstants == null) {
this.enumConstants = loadEnumConstants();
}
return enumConstants;
}
@Override
public <A extends Annotation> A getAnnotation(Class<A> annotationType) {
A annotation = super.getAnnotation(annotationType);
if (isClass() && (annotation == null) && (annotationType.getAnnotation(Inherited.class) != null) && (getSuperclass() instanceof DeclaredType)) {
TypeElement superDecl = (TypeElement) ((DeclaredType) getSuperclass()).asElement();
if ((superDecl != null) && (!Object.class.getName().equals(superDecl.getQualifiedName().toString()))) {
return superDecl.getAnnotation(annotationType);
}
}
return annotation;
}
protected List<PropertyElement> loadProperties(PropertySpec spec) {
HashMap<String, DecoratedExecutableElement> getters = new HashMap<String, DecoratedExecutableElement>();
HashMap<String, DecoratedExecutableElement> setters = new HashMap<String, DecoratedExecutableElement>();
for (ExecutableElement method : getMethods()) {
DecoratedExecutableElement decoratedMethod = (DecoratedExecutableElement) method;
boolean getter = spec.isGetter(decoratedMethod);
boolean setter = spec.isSetter(decoratedMethod);
if (getter || setter) {
HashMap<String, DecoratedExecutableElement> methodMap = getter ? getters : setters;
methodMap.put(spec.getPropertyName(decoratedMethod), decoratedMethod);
}
}
ArrayList<PropertyElement> properties = new ArrayList<PropertyElement>(getters.size());
//now iterate through the getters and setters and pair them up....
for (String propertyName : getters.keySet()) {
DecoratedExecutableElement getter = getters.get(propertyName);
DecoratedExecutableElement setter = setters.remove(propertyName);
if (spec.isPaired(getter, setter)) {
properties.add(new PropertyElement(getter, setter, spec, this.env));
}
else {
properties.add(new PropertyElement(getter, null, spec, this.env));
}
}
for (DecoratedExecutableElement setter : setters.values()) {
properties.add(new PropertyElement(null, setter, spec, this.env));
}
return properties;
}
protected List<VariableElement> loadEnumConstants() {
ArrayList<VariableElement> constants = new ArrayList<VariableElement>();
if (isEnum()) {
List<VariableElement> fields = ElementFilter.fieldsIn(this.delegate.getEnclosedElements());
for (VariableElement field : fields) {
if (field.getKind() == ElementKind.ENUM_CONSTANT) {
constants.add(field);
}
}
}
return constants;
}
public boolean isClass() {
return getKind() == ElementKind.CLASS;
}
public boolean isInterface() {
return getKind() == ElementKind.INTERFACE;
}
public boolean isEnum() {
return getKind() == ElementKind.ENUM;
}
public boolean isAnnotatedType() {
return getKind() == ElementKind.ANNOTATION_TYPE;
}
@Override
public <R, P> R accept(ElementVisitor<R, P> v, P p) {
return v.visitType(this, p);
}
}