/*
* Copyright 2017-present Facebook, Inc.
*
* 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.facebook.buck.jvm.java.abi.source;
import com.facebook.buck.util.liteinfersupport.Nullable;
import com.sun.source.tree.AssignmentTree;
import com.sun.source.tree.Tree;
import com.sun.source.util.TreePath;
import java.util.List;
import java.util.stream.Collectors;
import javax.lang.model.element.AnnotationValue;
import javax.lang.model.element.AnnotationValueVisitor;
import javax.lang.model.element.TypeElement;
import javax.lang.model.element.VariableElement;
import javax.lang.model.type.TypeMirror;
import javax.lang.model.util.SimpleAnnotationValueVisitor8;
class TreeBackedAnnotationValue implements AnnotationValue {
private final AnnotationValue underlyingAnnotationValue;
private final TreePath valuePath;
private final TreeBackedElementResolver resolver;
@Nullable private Object value;
TreeBackedAnnotationValue(
AnnotationValue underlyingAnnotationValue,
TreePath path,
TreeBackedElementResolver resolver) {
this.underlyingAnnotationValue = underlyingAnnotationValue;
Tree leaf = path.getLeaf();
if (leaf instanceof AssignmentTree) {
AssignmentTree assignmentTree = (AssignmentTree) leaf;
valuePath = new TreePath(path, assignmentTree.getExpression());
} else {
valuePath = path;
}
this.resolver = resolver;
}
@Override
public Object getValue() {
if (value == null) {
value = resolver.getCanonicalValue(underlyingAnnotationValue, valuePath);
}
return value;
}
@Override
public <R, P> R accept(AnnotationValueVisitor<R, P> v, @Nullable P p) {
Object underlyingValue = underlyingAnnotationValue.getValue();
Object value = getValue();
if (underlyingValue.equals(value)) {
return underlyingAnnotationValue.accept(v, p);
}
if (value instanceof TreeBackedVariableElement) {
return v.visitEnumConstant((TreeBackedVariableElement) value, p);
} else if (value instanceof TypeMirror) {
return v.visitType((TypeMirror) value, p);
} else if (value instanceof TreeBackedAnnotationMirror) {
return v.visitAnnotation((TreeBackedAnnotationMirror) value, p);
} else if (value instanceof List) {
@SuppressWarnings("unchecked")
List<? extends AnnotationValue> valuesList = (List<? extends AnnotationValue>) value;
return v.visitArray(valuesList, p);
} else {
throw new IllegalStateException(String.format("Unexpected annotation value: %s", value));
}
}
@Override
public String toString() {
return accept(
new SimpleAnnotationValueVisitor8<String, Void>() {
@Override
protected String defaultAction(Object o, Void aVoid) {
return o.toString();
}
@Override
public String visitByte(byte b, Void aVoid) {
return String.format("(byte)0x%x", b);
}
@Override
public String visitChar(char c, Void aVoid) {
return String.format("'%c'", c);
}
@Override
public String visitString(String s, Void aVoid) {
return String.format("\"%s\"", s);
}
@Override
public String visitLong(long l, Void aVoid) {
return String.format("%dL", l);
}
@Override
public String visitFloat(float f, Void aVoid) {
return String.format("%.1ff", f);
}
@Override
public String visitEnumConstant(VariableElement c, Void aVoid) {
TypeElement enumType = (TypeElement) c.getEnclosingElement();
return String.format("%s.%s", enumType.getQualifiedName(), c.getSimpleName());
}
@Override
public String visitType(TypeMirror t, Void aVoid) {
return String.format("%s.class", t.toString());
}
@Override
public String visitArray(List<? extends AnnotationValue> vals, Void aVoid) {
return String.format(
"{%s}",
vals.stream().map(val -> val.accept(this, null)).collect(Collectors.joining(", ")));
}
},
null);
}
}