com.facebook.swift.codec.metadata.ThriftUnionMetadataBuilder Maven / Gradle / Ivy
/*
* Copyright (C) 2012 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.swift.codec.metadata;
import com.facebook.swift.codec.ThriftIdlAnnotation;
import com.facebook.swift.codec.ThriftUnion;
import com.facebook.swift.codec.ThriftUnionId;
import com.facebook.swift.codec.metadata.ThriftStructMetadata.MetadataType;
import com.google.common.base.Optional;
import com.google.common.collect.ImmutableList;
import com.google.common.collect.ImmutableMap;
import javax.annotation.concurrent.NotThreadSafe;
import java.lang.reflect.Field;
import java.lang.reflect.Method;
import java.lang.reflect.Modifier;
import java.lang.reflect.Type;
import java.util.Collection;
import java.util.List;
import java.util.Map;
import static com.facebook.swift.codec.ThriftField.Requiredness;
import static com.facebook.swift.codec.metadata.FieldKind.THRIFT_UNION_ID;
import static com.facebook.swift.codec.metadata.ReflectionHelper.findAnnotatedMethods;
@NotThreadSafe
public class ThriftUnionMetadataBuilder
extends AbstractThriftMetadataBuilder
{
public ThriftUnionMetadataBuilder(ThriftCatalog catalog, Type structType)
{
super(catalog, structType);
// verify the class is public and has the correct annotations
verifyClass(ThriftUnion.class);
// extract the @ThriftUnionId fields
extractThriftUnionId();
// finally normalize the field metadata using things like
normalizeThriftFields(catalog);
}
@Override
protected String extractName()
{
ThriftUnion annotation = getStructClass().getAnnotation(ThriftUnion.class);
if (annotation == null) {
return getStructClass().getSimpleName();
}
else if (!annotation.value().isEmpty()) {
return annotation.value();
}
else {
return getStructClass().getSimpleName();
}
}
@Override
protected Map extractStructIdlAnnotations()
{
ThriftUnion annotation = getStructClass().getAnnotation(ThriftUnion.class);
if (annotation == null) {
return ImmutableMap.of();
}
else {
ImmutableMap.Builder builder = ImmutableMap.builder();
for (ThriftIdlAnnotation idlAnnotation : annotation.idlAnnotations()) {
builder.put(idlAnnotation.key(), idlAnnotation.value());
}
return builder.build();
}
}
@Override
protected Class> extractBuilderClass()
{
ThriftUnion annotation = getStructClass().getAnnotation(ThriftUnion.class);
if (annotation != null && !annotation.builder().equals(void.class)) {
return annotation.builder();
}
else {
return null;
}
}
private void extractThriftUnionId()
{
Collection idFields = ReflectionHelper.findAnnotatedFields(getStructClass(), ThriftUnionId.class);
Collection idMethods = findAnnotatedMethods(getStructClass(), ThriftUnionId.class);
if (idFields.size() + idMethods.size() != 1) {
if (idFields.size() + idMethods.size() == 0) {
metadataErrors.addError("Neither a field nor a method is annotated with @ThriftUnionId");
}
else if (idFields.size() > 1) {
metadataErrors.addError("More than one @ThriftUnionId field present");
}
else if (idMethods.size() > 1) {
metadataErrors.addError("More than one @ThriftUnionId method present");
}
else {
metadataErrors.addError("Both fields and methods annotated with @ThriftUnionId");
}
return;
}
for (Field idField : idFields) {
FieldExtractor fieldExtractor = new FieldExtractor(structType, idField, null, THRIFT_UNION_ID);
fields.add(fieldExtractor);
extractors.add(fieldExtractor);
FieldInjection fieldInjection = new FieldInjection(structType, idField, null, THRIFT_UNION_ID);
fields.add(fieldInjection);
fieldInjections.add(fieldInjection);
}
for (Method idMethod: idMethods) {
if (!Modifier.isPublic(idMethod.getModifiers())) {
metadataErrors.addError("@ThriftUnionId method [%s] is not public", idMethod.toGenericString());
continue;
}
if (Modifier.isStatic(idMethod.getModifiers())) {
metadataErrors.addError("@ThriftUnionId method [%s] is static", idMethod.toGenericString());
continue;
}
if (isValidateGetter(idMethod)) {
MethodExtractor methodExtractor = new MethodExtractor(structType, idMethod, null, THRIFT_UNION_ID);
fields.add(methodExtractor);
extractors.add(methodExtractor);
}
}
}
@Override
protected void validateConstructors()
{
for (ConstructorInjection constructorInjection : constructorInjections) {
if (constructorInjection.getParameters().size() > 1) {
metadataErrors.addError("@ThriftConstructor [%s] takes %d arguments, this is illegal for an union",
constructorInjection.getConstructor().toGenericString(),
constructorInjection.getParameters().size());
}
}
}
@Override
protected boolean isValidateSetter(Method method)
{
// Unions only allow setters with exactly one parameters
return method.getParameterTypes().length == 1;
}
//
// Build final metadata
//
@Override
public ThriftStructMetadata build()
{
// this code assumes that metadata is clean
metadataErrors.throwIfHasErrors();
// builder constructor injection
ThriftMethodInjection builderMethodInjection = buildBuilderConstructorInjections();
// constructor injection (or factory method for builder)
ThriftConstructorInjection constructorInjection = buildConstructorInjection();
// fields injections
Iterable fieldsMetadata = buildFieldInjections();
// methods injections
List methodInjections = buildMethodInjections();
return new ThriftStructMetadata(
structName,
extractStructIdlAnnotations(),
structType,
builderType,
MetadataType.UNION,
Optional.fromNullable(builderMethodInjection),
ImmutableList.copyOf(documentation),
ImmutableList.copyOf(fieldsMetadata),
Optional.fromNullable(constructorInjection),
methodInjections
);
}
private ThriftConstructorInjection buildConstructorInjection()
{
for (ConstructorInjection constructorInjection : constructorInjections) {
if (constructorInjection.getParameters().size() == 0) {
return new ThriftConstructorInjection(constructorInjection.getConstructor(), buildParameterInjections(constructorInjection.getParameters()));
}
}
// This is actually legal for a ThriftUnion, all c'tors available take arguments and are associated with the FieldMetadata...
return null;
}
@Override
protected ThriftFieldMetadata buildField(Collection input)
{
short id = -1;
boolean isLegacyId = false;
String name = null;
boolean recursiveness = false;
Requiredness requiredness = Requiredness.UNSPECIFIED;
Map idlAnnotations = null;
FieldKind fieldType = FieldKind.THRIFT_FIELD;
ThriftTypeReference thriftTypeReference = null;
ThriftConstructorInjection thriftConstructorInjection = null;
ThriftMethodInjection thriftMethodInjection = null;
// process field injections and extractions
ImmutableList.Builder injections = ImmutableList.builder();
ThriftExtraction extraction = null;
for (FieldMetadata fieldMetadata : input) {
id = fieldMetadata.getId();
isLegacyId = fieldMetadata.isLegacyId();
name = fieldMetadata.getName();
recursiveness = fieldMetadata.isRecursiveReference();
requiredness = fieldMetadata.getRequiredness();
idlAnnotations = fieldMetadata.getIdlAnnotations();
fieldType = fieldMetadata.getType();
thriftTypeReference = catalog.getFieldThriftTypeReference(fieldMetadata);
switch (requiredness) {
case REQUIRED:
case OPTIONAL:
metadataErrors.addError(
"Thrift union '%s' field '%s(%s)' should not be marked required or optional",
structName,
name,
id);
break;
default:
break;
}
if (fieldMetadata instanceof FieldInjection) {
FieldInjection fieldInjection = (FieldInjection) fieldMetadata;
injections.add(new ThriftFieldInjection(fieldInjection.getId(), fieldInjection.getName(), fieldInjection.getField(), fieldInjection.getType()));
}
else if (fieldMetadata instanceof ParameterInjection) {
ParameterInjection parameterInjection = (ParameterInjection) fieldMetadata;
ThriftParameterInjection thriftParameterInjection = new ThriftParameterInjection(
parameterInjection.getId(),
parameterInjection.getName(),
parameterInjection.getParameterIndex(),
fieldMetadata.getJavaType()
);
injections.add(thriftParameterInjection);
for (ConstructorInjection constructorInjection : constructorInjections) {
if (constructorInjection.getParameters().size() == 1 && constructorInjection.getParameters().get(0).equals(parameterInjection)) {
thriftConstructorInjection = new ThriftConstructorInjection(constructorInjection.getConstructor(), thriftParameterInjection);
break;
}
}
for (MethodInjection methodInjection : methodInjections) {
if (methodInjection.getParameters().size() == 1 && methodInjection.getParameters().get(0).equals(parameterInjection)) {
thriftMethodInjection = new ThriftMethodInjection(methodInjection.getMethod(), thriftParameterInjection);
}
}
}
else if (fieldMetadata instanceof FieldExtractor) {
FieldExtractor fieldExtractor = (FieldExtractor) fieldMetadata;
extraction = new ThriftFieldExtractor(fieldExtractor.getId(), fieldExtractor.getName(), fieldExtractor.getType(), fieldExtractor.getField(), fieldExtractor.getJavaType());
}
else if (fieldMetadata instanceof MethodExtractor) {
MethodExtractor methodExtractor = (MethodExtractor) fieldMetadata;
extraction = new ThriftMethodExtractor(methodExtractor.getId(), methodExtractor.getName(), methodExtractor.getType(), methodExtractor.getMethod(), methodExtractor.getJavaType());
}
}
// add type coercion
TypeCoercion coercion = null;
if (!thriftTypeReference.isRecursive() && thriftTypeReference.get().isCoerced()) {
coercion = catalog.getDefaultCoercion(thriftTypeReference.get().getJavaType());
}
ThriftFieldMetadata thriftFieldMetadata = new ThriftFieldMetadata(
id,
isLegacyId,
recursiveness,
requiredness,
idlAnnotations,
thriftTypeReference,
name,
fieldType,
injections.build(),
Optional.fromNullable(thriftConstructorInjection),
Optional.fromNullable(thriftMethodInjection),
Optional.fromNullable(extraction),
Optional.fromNullable(coercion)
);
return thriftFieldMetadata;
}
}
© 2015 - 2025 Weber Informatics LLC | Privacy Policy