/** * 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 javax.lang.model.element.AnnotationMirror; import javax.lang.model.element.ElementVisitor; import javax.lang.model.element.Name; import javax.lang.model.element.VariableElement; import javax.lang.model.type.TypeMirror; import java.lang.annotation.Annotation; import java.util.ArrayList; import java.util.HashMap; import java.util.List; import java.util.Map; /** * A property, representing the getter/setter pair. In all cases, the description of the property matches the description of the * getter, but the annotations are the union of the getter and the setter, with the intersection preferring the getter. * * @author Ryan Heaton */ public class PropertyElement extends DecoratedExecutableElement { private final DecoratedExecutableElement setter; private final DecoratedExecutableElement getter; private final String propertyName; private final TypeMirror propertyType; /** * A property declaration. * * @param getter The getter. * @param setter The setter. * @throws IllegalStateException If the getter and setter don't pair up. */ public PropertyElement(DecoratedExecutableElement getter, DecoratedExecutableElement setter, DecoratedProcessingEnvironment env) { this(getter, setter, new ElementUtils.DefaultPropertySpec(env), env); } public PropertyElement(DecoratedExecutableElement getter, DecoratedExecutableElement setter, PropertySpec spec, DecoratedProcessingEnvironment env) { super(getter == null ? setter : getter); this.getter = getter; this.setter = setter; this.propertyName = spec.getPropertyName(getter != null ? getter : setter); TypeMirror propertyType = null; if (getter != null) { propertyType = getter.getReturnType(); } if (setter != null) { List<? extends VariableElement> parameters = setter.getParameters(); if ((parameters == null) || (parameters.size() != 1)) { throw new IllegalStateException(this.setter + ": invalid setter for " + propertyType); } else { TypeMirror setterType = parameters.iterator().next().asType(); if (propertyType == null) { propertyType = setterType; } else if (!env.getTypeUtils().isSameType(propertyType, setterType)) { throw new IllegalStateException(this.setter + ": invalid setter for getter of type " + propertyType); } } } if (propertyType == null) { throw new IllegalStateException("Unable to determine property type for property" + this.propertyName + "."); } this.propertyType = propertyType; } @Override public TypeMirror asType() { return getPropertyType(); } /** * The type of this property. * * @return The type of this property. */ public TypeMirror getPropertyType() { return this.propertyType; } /** * The simple name of the property is the property name. * * @return The simple name of the property is the property name. */ @Override public Name getSimpleName() { return this.env.getElementUtils().getName(this.propertyName); } /** * Make sure the property name is calculated correctly. * cd */ @Override public String getPropertyName() { return this.propertyName; } /** * The setter, or null if this property is a read-only property. * * @return The setter. */ public DecoratedExecutableElement getSetter() { return setter; } /** * The getter. * * @return The getter. */ public DecoratedExecutableElement getGetter() { return getter; } /** * Whether this property is read-only. * * @return Whether this property is read-only. */ public boolean isReadOnly() { return getSetter() == null; } /** * Whether this property is write-only. * * @return Whether this property is write-only. */ public boolean isWriteOnly() { return getGetter() == null; } /** * Gets the annotations on the setter and the getter. If the annotation is on both the setter and the getter, only the one on the getter will * be included. * * @return The union of the annotations on the getter and setter. */ @Override public Map<String, AnnotationMirror> getAnnotations() { Map<String, AnnotationMirror> annotations = new HashMap<String, AnnotationMirror>(); if (getGetter() != null) { annotations.putAll(getGetter().getAnnotations()); } if (getSetter() != null) { annotations.putAll(getSetter().getAnnotations()); } return annotations; } /** * Gets the collection of annotations on the setter and the getter. If the annotation is on both the setter and the getter, only the one on the getter will * be included. * * @return The union of the annotations on the getter and setter. */ @Override public List<AnnotationMirror> getAnnotationMirrors() { return new ArrayList<AnnotationMirror>(getAnnotations().values()); } /** * Gets the annotation on the getter. If it doesn't exist, returns the one on the setter. * * @param annotationType The annotation type. * @return The annotation. */ @Override public <A extends Annotation> A getAnnotation(Class<A> annotationType) { A annotation = null; if (this.getter != null) { annotation = this.getter.getAnnotation(annotationType); } if ((annotation == null) && (this.setter != null)) { annotation = this.setter.getAnnotation(annotationType); } return annotation; } @Override public TypeMirror getReturnType() { return getPropertyType(); } @Override public boolean isGetter() { return false; } @Override public boolean isSetter() { return false; } @Override public boolean isVarArgs() { return false; } @Override public <R, P> R accept(ElementVisitor<R, P> v, P p) { return v.visitExecutable(this, p); } }