/* * Copyright (C) 2019 The Dagger Authors. * * 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 dagger.internal.codegen; import static com.google.auto.common.AnnotationMirrors.getAnnotationValuesWithDefaults; import static com.google.common.base.Preconditions.checkState; import static com.google.common.collect.Maps.transformValues; import static dagger.internal.codegen.DaggerStreams.toImmutableList; import static javax.lang.model.util.ElementFilter.fieldsIn; import com.google.auto.common.MoreTypes; import com.google.common.collect.ImmutableList; import dagger.internal.codegen.serialization.AnnotationProto; import dagger.internal.codegen.serialization.AnnotationValueProto; import java.util.Collections; import java.util.List; import javax.inject.Inject; import javax.lang.model.element.AnnotationMirror; import javax.lang.model.element.AnnotationValue; import javax.lang.model.element.AnnotationValueVisitor; import javax.lang.model.element.VariableElement; import javax.lang.model.type.TypeMirror; import javax.lang.model.util.SimpleAnnotationValueVisitor8; /** Converts {@link AnnotationMirror}s to {@link AnnotationProto}s and vice-versa. */ final class AnnotationProtoConverter { private final TypeProtoConverter typeProtoConverter; @Inject AnnotationProtoConverter(TypeProtoConverter typeProtoConverter) { this.typeProtoConverter = typeProtoConverter; } /** Translates an {@link AnnotationMirror} to a proto representation. */ static AnnotationProto toProto(AnnotationMirror annotationMirror) { AnnotationProto.Builder builder = AnnotationProto.newBuilder(); builder.setAnnotationType(TypeProtoConverter.toProto(annotationMirror.getAnnotationType())); getAnnotationValuesWithDefaults(annotationMirror) .forEach( (attribute, value) -> builder.putAllValues( Collections.singletonMap( attribute.getSimpleName().toString(), annotationValueProto(value)))); return builder.build(); } /** Creates an {@link AnnotationMirror} from its proto representation. */ AnnotationMirror fromProto(AnnotationProto annotation) { return SimpleAnnotationMirror.of( MoreTypes.asTypeElement(typeProtoConverter.fromProto(annotation.getAnnotationType())), transformValues(annotation.getValues(), AnnotationValueFromProto::new)); } private static final AnnotationValueVisitor< AnnotationValueProto.Builder, AnnotationValueProto.Builder> ANNOTATION_VALUE_TO_PROTO = new SimpleAnnotationValueVisitor8< AnnotationValueProto.Builder, AnnotationValueProto.Builder>() { @Override public AnnotationValueProto.Builder visitAnnotation( AnnotationMirror nestedAnnotation, AnnotationValueProto.Builder builder) { return builder .setNestedAnnotation(toProto(nestedAnnotation)) .setKind(AnnotationValueProto.Kind.ANNOTATION); } @Override public AnnotationValueProto.Builder visitBoolean( boolean b, AnnotationValueProto.Builder builder) { return builder.setBooleanValue(b).setKind(AnnotationValueProto.Kind.BOOLEAN); } @Override public AnnotationValueProto.Builder visitChar( char c, AnnotationValueProto.Builder builder) { return builder .setStringValue(String.valueOf(c)) .setKind(AnnotationValueProto.Kind.CHAR); } @Override public AnnotationValueProto.Builder visitByte( byte b, AnnotationValueProto.Builder builder) { return builder.setIntValue(b).setKind(AnnotationValueProto.Kind.BYTE); } @Override public AnnotationValueProto.Builder visitShort( short s, AnnotationValueProto.Builder builder) { return builder.setIntValue(s).setKind(AnnotationValueProto.Kind.SHORT); } @Override public AnnotationValueProto.Builder visitInt( int i, AnnotationValueProto.Builder builder) { return builder.setIntValue(i).setKind(AnnotationValueProto.Kind.INT); } @Override public AnnotationValueProto.Builder visitFloat( float f, AnnotationValueProto.Builder builder) { return builder.setFloatValue(f).setKind(AnnotationValueProto.Kind.FLOAT); } @Override public AnnotationValueProto.Builder visitLong( long l, AnnotationValueProto.Builder builder) { return builder.setLongValue(l).setKind(AnnotationValueProto.Kind.LONG); } @Override public AnnotationValueProto.Builder visitDouble( double d, AnnotationValueProto.Builder builder) { return builder.setDoubleValue(d).setKind(AnnotationValueProto.Kind.DOUBLE); } @Override public AnnotationValueProto.Builder visitString( String s, AnnotationValueProto.Builder builder) { return builder.setStringValue(s).setKind(AnnotationValueProto.Kind.STRING); } @Override public AnnotationValueProto.Builder visitType( TypeMirror t, AnnotationValueProto.Builder builder) { return builder .setClassLiteral(TypeProtoConverter.toProto(t)) .setKind(AnnotationValueProto.Kind.CLASS_LITERAL); } @Override public AnnotationValueProto.Builder visitEnumConstant( VariableElement c, AnnotationValueProto.Builder builder) { return builder .setEnumType(TypeProtoConverter.toProto(c.asType())) .setEnumName(c.getSimpleName().toString()) .setKind(AnnotationValueProto.Kind.ENUM); } @Override public AnnotationValueProto.Builder visitArray( List values, AnnotationValueProto.Builder builder) { values.forEach(value -> builder.addArrayValues(annotationValueProto(value))); return builder.setKind(AnnotationValueProto.Kind.ARRAY); } @Override public AnnotationValueProto.Builder visitUnknown( AnnotationValue av, AnnotationValueProto.Builder builder) { throw new UnsupportedOperationException(av.toString()); } }; /** Translates an {@link AnnotationValue} to a proto representation. */ private static AnnotationValueProto annotationValueProto(AnnotationValue annotationValue) { return annotationValue .accept(ANNOTATION_VALUE_TO_PROTO, AnnotationValueProto.newBuilder()) .build(); } private class AnnotationValueFromProto implements AnnotationValue { private final AnnotationValueProto proto; AnnotationValueFromProto(AnnotationValueProto proto) { this.proto = proto; } @Override public Object getValue() { switch (proto.getKind()) { case BOOLEAN: return proto.getBooleanValue(); case BYTE: return (byte) proto.getIntValue(); case SHORT: return (short) proto.getIntValue(); case CHAR: return getCharValue(); case INT: return proto.getIntValue(); case FLOAT: return proto.getFloatValue(); case LONG: return proto.getLongValue(); case DOUBLE: return proto.getDoubleValue(); case STRING: return proto.getStringValue(); case CLASS_LITERAL: return typeProtoConverter.fromProto(proto.getClassLiteral()); case ENUM: return getEnumConstant(); case ANNOTATION: return fromProto(proto.getNestedAnnotation()); case ARRAY: return getArrayValues(); case UNKNOWN: case UNRECOGNIZED: // fall through } throw new AssertionError(proto); } @Override public R accept(AnnotationValueVisitor visitor, P passedValue) { switch (proto.getKind()) { case BOOLEAN: return visitor.visitBoolean(proto.getBooleanValue(), passedValue); case BYTE: return visitor.visitByte((byte) proto.getIntValue(), passedValue); case SHORT: return visitor.visitShort((short) proto.getIntValue(), passedValue); case CHAR: return visitor.visitChar(getCharValue(), passedValue); case INT: return visitor.visitInt(proto.getIntValue(), passedValue); case FLOAT: return visitor.visitFloat(proto.getFloatValue(), passedValue); case LONG: return visitor.visitLong(proto.getLongValue(), passedValue); case DOUBLE: return visitor.visitDouble(proto.getDoubleValue(), passedValue); case STRING: return visitor.visitString((String) getValue(), passedValue); case CLASS_LITERAL: return visitor.visitType((TypeMirror) getValue(), passedValue); case ENUM: return visitor.visitEnumConstant((VariableElement) getValue(), passedValue); case ANNOTATION: return visitor.visitAnnotation((AnnotationMirror) getValue(), passedValue); case ARRAY: return visitor.visitArray(getArrayValues(), passedValue); case UNKNOWN: case UNRECOGNIZED: // fall through } throw new AssertionError(proto); } private char getCharValue() { checkState(proto.getKind().equals(AnnotationValueProto.Kind.CHAR)); return proto.getStringValue().charAt(0); } private VariableElement getEnumConstant() { checkState(proto.getKind().equals(AnnotationValueProto.Kind.ENUM)); TypeMirror enumType = typeProtoConverter.fromProto(proto.getEnumType()); return fieldsIn(MoreTypes.asTypeElement(enumType).getEnclosedElements()).stream() .filter(value -> value.getSimpleName().contentEquals(proto.getEnumName())) .findFirst() .get(); } private ImmutableList getArrayValues() { checkState(proto.getKind().equals(AnnotationValueProto.Kind.ARRAY)); return proto.getArrayValuesList().stream() .map(AnnotationValueFromProto::new) .collect(toImmutableList()); } } }