/**
* Copyright 2011-2017 Asakusa Framework Team.
*
* 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.asakusafw.operator.builtin;
import java.text.MessageFormat;
import java.util.ArrayList;
import java.util.BitSet;
import java.util.Collections;
import java.util.List;
import java.util.Map;
import java.util.Objects;
import java.util.Set;
import java.util.concurrent.atomic.AtomicBoolean;
import java.util.function.IntConsumer;
import java.util.function.Predicate;
import java.util.stream.Collectors;
import javax.annotation.processing.Messager;
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.Modifier;
import javax.lang.model.element.TypeElement;
import javax.lang.model.element.VariableElement;
import javax.lang.model.type.DeclaredType;
import javax.lang.model.type.TypeKind;
import javax.lang.model.type.TypeMirror;
import javax.lang.model.util.ElementFilter;
import javax.lang.model.util.Types;
import javax.tools.Diagnostic;
import com.asakusafw.operator.CompileEnvironment;
import com.asakusafw.operator.Constants;
import com.asakusafw.operator.OperatorDriver;
import com.asakusafw.operator.description.ClassDescription;
import com.asakusafw.operator.description.Descriptions;
import com.asakusafw.operator.description.EnumConstantDescription;
import com.asakusafw.operator.description.ObjectDescription;
import com.asakusafw.operator.description.ValueDescription;
import com.asakusafw.operator.model.DataModelMirror;
import com.asakusafw.operator.model.KeyMirror;
import com.asakusafw.operator.model.OperatorDescription;
import com.asakusafw.operator.model.OperatorDescription.Document;
import com.asakusafw.operator.model.OperatorDescription.Node;
import com.asakusafw.operator.model.OperatorDescription.ParameterReference;
import com.asakusafw.operator.model.OperatorDescription.Reference;
import com.asakusafw.operator.model.OperatorDescription.Reference.Kind;
import com.asakusafw.operator.util.AnnotationHelper;
/**
* Helper for built-in operators.
* @since 0.9.0
* @version 0.9.1
*/
final class DslBuilder {
private static final ValueDescription[] EMPTY_ATTRS = new ValueDescription[0];
static final EnumConstantDescription CONSTANT_SHUFFLE = new EnumConstantDescription(
new ClassDescription("com.asakusafw.vocabulary.flow.graph.FlowBoundary"), //$NON-NLS-1$
"SHUFFLE"); //$NON-NLS-1$
static final ClassDescription TYPE_ITERABLE = Descriptions.classOf(Iterable.class);
static final ClassDescription TYPE_LIST = Descriptions.classOf(List.class);
static final ClassDescription TYPE_ENUM = Descriptions.classOf(Enum.class);
static final ClassDescription TYPE_STRING = Descriptions.classOf(String.class);
static final ClassDescription TYPE_VOLATILE =
new ClassDescription("com.asakusafw.vocabulary.operator.Volatile"); //$NON-NLS-1$
static final ClassDescription TYPE_STICKY =
new ClassDescription("com.asakusafw.vocabulary.operator.Sticky"); //$NON-NLS-1$
static final ClassDescription TYPE_OBSERVATION_COUNT =
new ClassDescription("com.asakusafw.vocabulary.flow.graph.ObservationCount"); //$NON-NLS-1$
static final ClassDescription TYPE_VIEW_INFO =
new ClassDescription("com.asakusafw.vocabulary.attribute.ViewInfo"); //$NON-NLS-1$
static final String NAME_FLAT_VIEW_INFO_FACTORY = "flat"; //$NON-NLS-1$
static final String NAME_GROUP_VIEW_INFO_FACTORY = "groupOf"; //$NON-NLS-1$
private final List<Node> parameters = new ArrayList<>();
private final List<Node> inputs = new ArrayList<>();
private final List<Node> arguments = new ArrayList<>();
private final List<Node> outputs = new ArrayList<>();
private final List<KeyRef> mainKeys = new ArrayList<>();
private ExecutableElement support;
private final List<ValueDescription> attributes = new ArrayList<>();
final CompileEnvironment environment;
final ExecutableElement method;
final AtomicBoolean errorSink = new AtomicBoolean();
private final AnnotationRef annotationRef;
private final ElementRef methodRef;
private final List<ElementRef> parameterRefs;
private final ElementRef resultRef;
private final ElementRef unknownRef;
DslBuilder(OperatorDriver.Context context) {
Objects.requireNonNull(context, "context must not be null"); //$NON-NLS-1$
this.environment = context.getEnvironment();
this.method = context.getMethod();
this.annotationRef = new AnnotationRef(context.getMethod(), context.getAnnotation());
this.unknownRef = new MissingElementRef(context.getMethod());
this.methodRef = new GeneralElementRef(context.getMethod(), Reference.method());
this.parameterRefs = new ArrayList<>();
int paramIndex = 0;
for (VariableElement p : context.getMethod().getParameters()) {
parameterRefs.add(new GeneralElementRef(p, Reference.parameter(paramIndex++)));
}
this.resultRef = new ResultElementRef(context.getMethod());
}
public void addInput(Document document, String name, TypeMirror type, Reference reference) {
addInput(document, name, type, null, reference, EMPTY_ATTRS);
}
public void addInput(Document document, String name, TypeMirror type, KeyRef key, Reference reference) {
addInput(document, name, type, key, reference, EMPTY_ATTRS);
}
public Node addOutput(Document document, String name, TypeMirror type, Reference reference) {
return addOutput(document, name, type, reference, EMPTY_ATTRS);
}
public void addInput(
Document document, String name, TypeMirror type,
Reference reference,
ValueDescription... attrs) {
addInput(document, name, type, null, reference, attrs);
}
public void addInput(
Document document, String name, TypeMirror type,
KeyRef key, Reference reference,
ValueDescription... attrs) {
Node node = new Node(Node.Kind.INPUT, name, document, type, reference);
inputs.add(node);
if (key != null) {
node.withKey(key.getModel());
mainKeys.add(key);
}
for (ValueDescription attr : attrs) {
node.withAttribute(attr);
}
parameters.add(node);
}
public Node addOutput(
Document document, String name, TypeMirror type,
Reference reference,
ValueDescription... attrs) {
Node node = new Node(Node.Kind.OUTPUT, name, document, type, reference);
for (ValueDescription attr : attrs) {
node.withAttribute(attr);
}
outputs.add(node);
return node;
}
public void addArgument(Document document, String name, TypeMirror type, Reference reference) {
Node node = new Node(Node.Kind.DATA, name, document, type, reference);
arguments.add(node);
parameters.add(node);
}
public void requireShuffle() {
addAttribute(CONSTANT_SHUFFLE);
}
public void setSupport(ExecutableElement newValue) {
this.support = newValue;
}
public void addAttribute(ValueDescription attribute) {
attributes.add(attribute);
}
public CompileEnvironment getEnvironment() {
return environment;
}
public ExecutableElement getMethod() {
return method;
}
public List<Node> getInputs() {
return inputs;
}
public List<Node> getOutputs() {
return outputs;
}
public List<Node> getParameters() {
return parameters;
}
public boolean sawError() {
return errorSink.get();
}
public OperatorDescription toDescription() {
if (sawError()) {
return null;
}
if (inputs.isEmpty()) {
methodRef.error(Messages.getString("DslBuilder.errorInputMissing")); //$NON-NLS-1$
}
if (outputs.isEmpty()) {
methodRef.error(Messages.getString("DslBuilder.errorOutputMissing")); //$NON-NLS-1$
}
validateMainKeys();
if (environment.isStrictOperatorParameterOrder()) {
validateParameterOrder();
}
if (sawError()) {
return null;
}
List<ValueDescription> attrs = new ArrayList<>();
attrs.addAll(attributes);
attrs.add(computeObservationCount());
return new OperatorDescription(Document.reference(Reference.method()), parameters, outputs, attrs)
.withSupport(support);
}
private void validateMainKeys() {
if (mainKeys.size() <= 1) {
return;
}
KeyRef first = mainKeys.get(0);
for (int i = 1, n = mainKeys.size(); i < n; i++) {
KeyRef target = mainKeys.get(i);
validateMainKey(first, target);
}
}
private void validateMainKey(KeyRef first, KeyRef target) {
List<KeyMirror.Group> as = first.getModel().getGroup();
List<KeyMirror.Group> bs = target.getModel().getGroup();
Types types = environment.getProcessingEnvironment().getTypeUtils();
for (int i = 0, n = Math.min(as.size(), bs.size()); i < n; i++) {
KeyMirror.Group a = as.get(i);
KeyMirror.Group b = bs.get(i);
if (types.isSameType(a.getProperty().getType(), b.getProperty().getType()) == false) {
target.error(b.getSource(), MessageFormat.format(
Messages.getString("DslBuilder.errorKeyAnnotationInconsistentGroupPropertyType"), //$NON-NLS-1$
first.getOwner().getSimpleName(),
a.getProperty().getName(),
target.getOwner().getSimpleName(),
b.getProperty().getName()));
}
}
if (as.size() < bs.size()) {
for (int i = as.size(), n = bs.size(); i < n; i++) {
KeyMirror.Group b = bs.get(i);
first.error(MessageFormat.format(
Messages.getString("DslBuilder.errorKeyAnnotationInconsistentGroupProperty"), //$NON-NLS-1$
target.getOwner().getSimpleName(),
b.getProperty().getName(),
first.getOwner().getSimpleName()));
}
} else if (as.size() > bs.size()) {
for (int i = bs.size(), n = as.size(); i < n; i++) {
KeyMirror.Group a = as.get(i);
target.error(MessageFormat.format(
Messages.getString("DslBuilder.errorKeyAnnotationInconsistentGroupProperty"), //$NON-NLS-1$
first.getOwner().getSimpleName(),
a.getProperty().getName(),
target.getOwner().getSimpleName()));
}
}
}
private void validateParameterOrder() {
Predicate<Node> side = n -> n.getAttributes().stream()
.filter(v -> v instanceof ObjectDescription)
.map(v -> (ObjectDescription) v)
.anyMatch(o -> o.getValueType().equals(TYPE_VIEW_INFO));
BitSet inMask = toMask(inputs);
BitSet sideMask = toMask(inputs, side);
BitSet outMask = toMask(outputs);
BitSet argMask = toMask(arguments);
outMask.andNot(inMask); // don't consider input&output parameters
if (sideMask.isEmpty() == false) {
BitSet mainMask = (BitSet) inMask.clone();
mainMask.andNot(sideMask);
forEach(mainMask, sideMask.nextSetBit(0), i -> {
parameterRefs.get(i).error(Messages.getString("DslBuilder.errorInputAfterSide")); //$NON-NLS-1$
});
}
if (outMask.isEmpty() == false) {
forEach(inMask, outMask.nextSetBit(0), i -> {
parameterRefs.get(i).error(Messages.getString("DslBuilder.errorInputAfterOutput")); //$NON-NLS-1$
});
}
if (argMask.isEmpty() == false) {
forEach(inMask, argMask.nextSetBit(0), i -> {
parameterRefs.get(i).error(Messages.getString("DslBuilder.errorInputAfterArgument")); //$NON-NLS-1$
});
forEach(outMask, argMask.nextSetBit(0), i -> {
parameterRefs.get(i).error(Messages.getString("DslBuilder.errorOutputAfterArgument")); //$NON-NLS-1$
});
}
}
private static BitSet toMask(List<Node> nodes, Predicate<Node> predicate) {
BitSet results = new BitSet();
nodes.stream()
.filter(predicate)
.map(n -> n.getReference())
.filter(r -> r.getKind() == Kind.PARAMETER)
.map(ParameterReference.class::cast)
.forEach(r -> results.set(r.getLocation()));
return results;
}
private static BitSet toMask(List<Node> nodes) {
return toMask(nodes, n -> true);
}
private static void forEach(BitSet bits, int start, IntConsumer body) {
for (int i = bits.nextSetBit(start); i >= 0; i = bits.nextSetBit(i + 1)) {
body.accept(i);
}
}
private EnumConstantDescription computeObservationCount() {
boolean isVolatile = false;
boolean isSticky = false;
DeclaredType volatileType = environment.findDeclaredType(TYPE_VOLATILE);
DeclaredType stickyType = environment.findDeclaredType(TYPE_STICKY);
Types types = environment.getProcessingEnvironment().getTypeUtils();
for (AnnotationMirror mirror : method.getAnnotationMirrors()) {
DeclaredType annotationType = mirror.getAnnotationType();
if (isVolatile == false && types.isSameType(annotationType, volatileType)) {
isVolatile = true;
}
if (isSticky == false && types.isSameType(annotationType, stickyType)) {
isSticky = true;
}
}
String name;
if (isVolatile && isSticky) {
name = "EXACTLY_ONCE"; //$NON-NLS-1$
} else if (isVolatile) {
name = "AT_MOST_ONCE"; //$NON-NLS-1$
} else if (isSticky) {
name = "AT_LEAST_ONCE"; //$NON-NLS-1$
} else {
name = "DONT_CARE"; //$NON-NLS-1$
}
return new EnumConstantDescription(TYPE_OBSERVATION_COUNT, name);
}
public ElementRef method() {
return methodRef;
}
public ElementRef parameter(int index) {
if (index < 0) {
throw new IndexOutOfBoundsException();
}
if (index >= parameterRefs.size()) {
return unknownRef;
}
return parameterRefs.get(index);
}
public ElementRef result() {
return resultRef;
}
public List<ElementRef> parameters() {
return parameters(0, parameterRefs.size());
}
public List<ElementRef> parametersFrom(int from) {
return parameters(from, parameterRefs.size());
}
public List<ElementRef> parameters(int from, int to) {
if (0 <= from && from < to && to <= parameterRefs.size()) {
return parameterRefs.subList(from, to);
} else {
return Collections.emptyList();
}
}
public AnnotationRef annotation() {
return annotationRef;
}
public void consumeExtraParameter(ElementRef parameter) {
TypeRef type = parameter.type();
if (type.isBasic()) {
addArgument(parameter.document(), parameter.name(), type.mirror(), parameter.reference());
} else if (type.isGroupView()) {
TypeRef arg = type.arg(0);
if (arg.isDataModel()) {
KeyRef key = parameter.resolveKey(arg);
ValueDescription info;
if (key == null) {
// error
info = ObjectDescription.of(TYPE_VIEW_INFO, NAME_GROUP_VIEW_INFO_FACTORY);
} else {
info = key.toTableInfo();
}
addInput(parameter.document(), parameter.name(), arg.mirror(), null, parameter.reference(), info);
} else {
parameter.error(Messages.getString("DslBuilder.errorGroupViewNotDataModelType")); //$NON-NLS-1$
}
} else if (type.isFlatView()) {
TypeRef arg = type.arg(0);
if (arg.isDataModel()) {
AnnotationRef annotation = parameter.annotation(Constants.TYPE_KEY);
if (annotation != null) {
annotation.warn(Messages.getString("DslBuilder.warnFlatViewWithKey")); //$NON-NLS-1$
}
ValueDescription info = ObjectDescription.of(TYPE_VIEW_INFO, NAME_FLAT_VIEW_INFO_FACTORY);
addInput(parameter.document(), parameter.name(), arg.mirror(), null, parameter.reference(), info);
} else {
parameter.error(Messages.getString("DslBuilder.errorFlatViewNotDataModelType")); //$NON-NLS-1$
}
} else {
throw new IllegalArgumentException();
}
}
public boolean isGeneric() {
return method.getTypeParameters().isEmpty() == false;
}
interface Ref {
void warn(String string);
void error(String string);
}
interface ElementRef extends Ref {
boolean exists();
Element get();
TypeRef type();
Document document();
Reference reference();
String name();
default Set<Modifier> modifiers() {
return Collections.emptySet();
}
default AnnotationRef annotation(ClassDescription type) {
return null;
}
default KeyRef resolveKey(TypeRef modelType) {
throw new IllegalStateException();
}
default KeyRef resolveKey(TypeRef modelType, AnnotationMirror annotation) {
throw new IllegalStateException();
}
}
private class MissingElementRef implements ElementRef {
private final Element owner;
MissingElementRef(Element owner) {
assert owner != null;
this.owner = owner;
}
@Override
public Element get() {
return owner;
}
@Override
public boolean exists() {
return false;
}
@Override
public TypeRef type() {
return new TypeRef();
}
@Override
public Document document() {
return Document.text("MISSING"); //$NON-NLS-1$
}
@Override
public Reference reference() {
return Reference.special("UNKWON"); //$NON-NLS-1$
}
@Override
public String name() {
return "MISSING"; //$NON-NLS-1$
}
@Override
public void warn(String message) {
Messager messager = environment.getProcessingEnvironment().getMessager();
messager.printMessage(Diagnostic.Kind.WARNING, message, owner);
}
@Override
public void error(String message) {
errorSink.set(true);
Messager messager = environment.getProcessingEnvironment().getMessager();
messager.printMessage(Diagnostic.Kind.ERROR, message, owner);
}
}
private class GeneralElementRef implements ElementRef {
final Element element;
private final Document document;
private final Reference reference;
GeneralElementRef(Element element, Reference reference) {
this(element, Document.reference(reference), reference);
}
GeneralElementRef(Element element, Document document, Reference reference) {
assert element != null;
assert document != null;
assert reference != null;
this.element = element;
this.document = document;
this.reference = reference;
}
@Override
public Element get() {
return element;
}
@Override
public boolean exists() {
return true;
}
@Override
public TypeRef type() {
return new TypeRef(element.asType());
}
@Override
public Document document() {
return document;
}
@Override
public Reference reference() {
return reference;
}
@Override
public String name() {
return element.getSimpleName().toString();
}
@Override
public Set<Modifier> modifiers() {
return element.getModifiers();
}
@Override
public AnnotationRef annotation(ClassDescription type) {
TypeElement annotationType = environment.findTypeElement(type);
if (annotationType == null) {
return null;
}
AnnotationMirror annotation = AnnotationHelper.findAnnotation(environment, annotationType, element);
if (annotation == null) {
return null;
}
return new AnnotationRef(element, annotation);
}
@Override
public KeyRef resolveKey(TypeRef dataModelType) {
TypeElement annotationType = environment.findTypeElement(Constants.TYPE_KEY);
if (annotationType == null) {
errorSink.set(true);
return null;
}
AnnotationMirror annotation = AnnotationHelper.findAnnotation(environment, annotationType, element);
if (annotation == null) {
error(Messages.getString("DslBuilder.errorElementMissingKeyAnnotation")); //$NON-NLS-1$
return null;
}
return resolveKey(dataModelType, annotation);
}
@Override
public KeyRef resolveKey(TypeRef modelType, AnnotationMirror annotation) {
DataModelMirror dataModel = environment.findDataModel(modelType.mirror());
if (dataModel == null) {
errorSink.set(true);
return null;
}
KeyMirror model = KeyMirror.parse(environment, annotation, element, dataModel);
if (model == null) {
errorSink.set(true);
return null;
}
return new KeyRef(element, model);
}
@Override
public void warn(String message) {
Messager messager = environment.getProcessingEnvironment().getMessager();
messager.printMessage(Diagnostic.Kind.WARNING, message, element);
}
@Override
public void error(String message) {
errorSink.set(true);
Messager messager = environment.getProcessingEnvironment().getMessager();
messager.printMessage(Diagnostic.Kind.ERROR, message, element);
}
}
class ResultElementRef implements ElementRef {
private final ExecutableElement element;
ResultElementRef(ExecutableElement element) {
assert element != null;
this.element = element;
}
@Override
public Element get() {
return element;
}
@Override
public boolean exists() {
return true;
}
@Override
public TypeRef type() {
return new TypeRef(element.getReturnType());
}
@Override
public Document document() {
return Document.reference(reference());
}
@Override
public Reference reference() {
return Reference.returns();
}
@Override
public String name() {
throw new UnsupportedOperationException();
}
@Override
public void warn(String message) {
Messager messager = environment.getProcessingEnvironment().getMessager();
messager.printMessage(Diagnostic.Kind.WARNING, message, element);
}
@Override
public void error(String message) {
errorSink.set(true);
Messager messager = environment.getProcessingEnvironment().getMessager();
messager.printMessage(Diagnostic.Kind.ERROR, message, element);
}
}
class TypeRef {
private final TypeMirror mirror;
TypeRef() {
this(environment.getProcessingEnvironment().getTypeUtils().getNoType(TypeKind.NONE));
}
TypeRef(TypeMirror mirror) {
assert mirror != null;
this.mirror = mirror;
}
private Types types() {
return environment.getProcessingEnvironment().getTypeUtils();
}
public boolean exists() {
return mirror.getKind() != TypeKind.NONE;
}
public boolean isVoid() {
return mirror.getKind() == TypeKind.VOID;
}
public boolean isExtra() {
return isPrimitive() || isString() || isViewLike();
}
public boolean isViewLike() {
return isFlatView() || isGroupView();
}
public boolean isFlatView() {
return isErasureEqualTo(environment.findDeclaredType(Constants.TYPE_VIEW));
}
public boolean isGroupView() {
return isErasureEqualTo(environment.findDeclaredType(Constants.TYPE_GROUP_VIEW));
}
public boolean isBasic() {
return isPrimitive() || isString();
}
public boolean isBoolean() {
return mirror.getKind() == TypeKind.BOOLEAN;
}
public boolean isPrimitive() {
return mirror.getKind().isPrimitive();
}
public boolean isString() {
return types().isSameType(mirror, environment.findDeclaredType(TYPE_STRING));
}
public boolean isEnum() {
return types().isSubtype(mirror, environment.findDeclaredType(TYPE_ENUM));
}
public boolean isIterable() {
return isErasureEqualTo(environment.findDeclaredType(TYPE_ITERABLE));
}
public boolean isList() {
return isErasureEqualTo(environment.findDeclaredType(TYPE_LIST));
}
public boolean isResult() {
return isErasureEqualTo(environment.findDeclaredType(Constants.TYPE_RESULT));
}
public TypeRef arg(int index) {
if (mirror.getKind() == TypeKind.DECLARED) {
List<? extends TypeMirror> typeArguments = ((DeclaredType) mirror).getTypeArguments();
if (0 <= index && index < typeArguments.size()) {
return new TypeRef(typeArguments.get(index));
}
}
return new TypeRef();
}
public boolean isDataModel() {
DataModelMirror dataModel = environment.findDataModel(mirror);
return dataModel != null;
}
public boolean isEqualTo(TypeRef other) {
return types().isSameType(mirror, other.mirror);
}
private boolean isErasureEqualTo(TypeMirror other) {
return types().isSameType(environment.getErasure(mirror), environment.getErasure(other));
}
public TypeMirror mirror() {
return mirror;
}
public DataModelMirror dataModel() {
DataModelMirror dataModel = environment.findDataModel(mirror);
if (isDataModel() == false) {
throw new IllegalStateException();
}
return dataModel;
}
public List<ElementRef> enumConstants() {
if (isEnum() == false) {
throw new IllegalStateException();
}
TypeElement type = (TypeElement) ((DeclaredType) mirror).asElement();
List<ElementRef> results = new ArrayList<>();
for (VariableElement var : ElementFilter.fieldsIn(type.getEnclosedElements())) {
if (var.getKind() == ElementKind.ENUM_CONSTANT) {
Document document = Document.external(var);
Reference reference = Reference.special(var.getSimpleName().toString());
results.add(new GeneralElementRef(var, document, reference));
}
}
return results;
}
public AnnotationRef annotation(ClassDescription annotationType) {
if (mirror.getKind() != TypeKind.DECLARED) {
return null;
}
TypeElement declaredType = (TypeElement) ((DeclaredType) mirror).asElement();
if (declaredType == null) {
return null;
}
TypeElement type = environment.findTypeElement(annotationType);
if (type == null) {
return null;
}
AnnotationMirror annotation = AnnotationHelper.findAnnotation(environment, type, declaredType);
if (annotation == null) {
return null;
}
return new AnnotationRef(declaredType, annotation);
}
@Override
public String toString() {
return mirror.toString();
}
}
class AnnotationRef implements Ref {
private final AnnotationMirror annotation;
private Map<String, AnnotationValue> values;
private final Element holder;
AnnotationRef(Element holder, AnnotationMirror annotation) {
assert holder != null;
assert annotation != null;
this.holder = holder;
this.annotation = annotation;
}
public AnnotationMirror get() {
return annotation;
}
public AnnotationValue value(String name) {
return getHolder(name);
}
public EnumConstantDescription constant(String name) {
AnnotationValue valueHolder = getHolder(name);
Object value = valueHolder.getValue();
if (value instanceof VariableElement) {
VariableElement var = (VariableElement) value;
TypeElement type = (TypeElement) var.getEnclosingElement();
return new EnumConstantDescription(
new ClassDescription(type.getQualifiedName().toString()),
var.getSimpleName().toString());
}
return null;
}
public String string(String name) {
AnnotationValue valueHolder = getHolder(name);
Object value = valueHolder.getValue();
if (value instanceof String) {
return (String) value;
}
return null;
}
public TypeRef type(String name) {
AnnotationValue valueHolder = getHolder(name);
Object value = valueHolder.getValue();
if (value instanceof TypeMirror) {
return new TypeRef((TypeMirror) value);
}
return null;
}
public AnnotationRef annotation(String name) {
AnnotationValue valueHolder = getHolder(name);
Object value = valueHolder.getValue();
if (value instanceof AnnotationMirror) {
return new AnnotationRef(holder, (AnnotationMirror) value);
}
return null;
}
public List<AnnotationRef> annotations(String name) {
AnnotationValue valueHolder = getHolder(name);
List<AnnotationValue> valueHolderList = AnnotationHelper.toValueList(environment, valueHolder);
List<AnnotationMirror> annotations =
AnnotationHelper.extractList(environment, AnnotationMirror.class, valueHolderList);
List<AnnotationRef> results = new ArrayList<>();
for (AnnotationMirror component : annotations) {
results.add(new AnnotationRef(method, component));
}
return results;
}
private AnnotationValue getHolder(String name) {
AnnotationValue valueHolder = values().get(name);
if (valueHolder == null) {
throw new IllegalArgumentException(name);
}
return valueHolder;
}
private synchronized Map<String, AnnotationValue> values() {
if (values == null) {
values = AnnotationHelper.getValues(environment, annotation);
}
return values;
}
@Override
public void warn(String message) {
Messager messager = environment.getProcessingEnvironment().getMessager();
messager.printMessage(Diagnostic.Kind.WARNING, message, holder, annotation);
}
@Override
public void error(String message) {
errorSink.set(true);
Messager messager = environment.getProcessingEnvironment().getMessager();
messager.printMessage(Diagnostic.Kind.ERROR, message, holder, annotation);
}
public void warn(String elementName, String message) {
AnnotationValue value = AnnotationHelper.getValue(environment, annotation, elementName);
if (value == null) {
warn(message);
} else {
errorSink.set(true);
Messager messager = environment.getProcessingEnvironment().getMessager();
messager.printMessage(Diagnostic.Kind.WARNING, message, holder, annotation, value);
}
}
public void error(String elementName, String message) {
AnnotationValue value = AnnotationHelper.getValue(environment, annotation, elementName);
if (value == null) {
error(message);
} else {
errorSink.set(true);
Messager messager = environment.getProcessingEnvironment().getMessager();
messager.printMessage(Diagnostic.Kind.ERROR, message, holder, annotation, value);
}
}
}
class KeyRef {
private final Element owner;
private final KeyMirror model;
KeyRef(Element owner, KeyMirror model) {
this.owner = owner;
this.model = model;
}
public Element getOwner() {
return owner;
}
public KeyMirror getModel() {
return model;
}
public void error(String message) {
errorSink.set(true);
Messager messager = environment.getProcessingEnvironment().getMessager();
messager.printMessage(Diagnostic.Kind.ERROR, message, owner, model.getSource());
}
public void error(AnnotationValue value, String message) {
errorSink.set(true);
Messager messager = environment.getProcessingEnvironment().getMessager();
messager.printMessage(Diagnostic.Kind.ERROR, message, owner, model.getSource(), value);
}
public ValueDescription toTableInfo() {
ObjectDescription info = ObjectDescription.of(TYPE_VIEW_INFO, NAME_GROUP_VIEW_INFO_FACTORY, terms());
return info;
}
public List<ValueDescription> terms() {
return model.toTerms().stream()
.sequential()
.map(Descriptions::valueOf)
.collect(Collectors.toList());
}
}
}