001/* 002 * Copyright (C) 2012 Facebook, Inc. 003 * 004 * Licensed under the Apache License, Version 2.0 (the "License"); you may 005 * not use this file except in compliance with the License. You may obtain 006 * a copy of the License at 007 * 008 * http://www.apache.org/licenses/LICENSE-2.0 009 * 010 * Unless required by applicable law or agreed to in writing, software 011 * distributed under the License is distributed on an "AS IS" BASIS, WITHOUT 012 * WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. See the 013 * License for the specific language governing permissions and limitations 014 * under the License. 015 */ 016package com.facebook.swift.codec.metadata; 017 018import com.facebook.swift.codec.ThriftStruct; 019import com.facebook.swift.codec.metadata.ThriftStructMetadata.MetadataType; 020import com.google.common.base.Function; 021import com.google.common.base.Optional; 022import com.google.common.collect.ImmutableList; 023import com.google.common.collect.Iterables; 024import com.google.common.collect.Lists; 025 026import javax.annotation.concurrent.NotThreadSafe; 027 028import java.lang.reflect.Method; 029import java.lang.reflect.Type; 030import java.util.Collection; 031import java.util.List; 032 033import static com.facebook.swift.codec.ThriftField.Requiredness; 034import static com.facebook.swift.codec.metadata.FieldKind.THRIFT_FIELD; 035 036@NotThreadSafe 037public class ThriftStructMetadataBuilder 038 extends AbstractThriftMetadataBuilder 039{ 040 public ThriftStructMetadataBuilder(ThriftCatalog catalog, Type structType) 041 { 042 super(catalog, structType); 043 044 // verify the class is public and has the correct annotations 045 verifyClass(ThriftStruct.class); 046 047 // finally normalize the field metadata using things like 048 normalizeThriftFields(catalog); 049 } 050 051 @Override 052 protected String extractName() 053 { 054 ThriftStruct annotation = getStructClass().getAnnotation(ThriftStruct.class); 055 if (annotation == null) { 056 return getStructClass().getSimpleName(); 057 } 058 else if (!annotation.value().isEmpty()) { 059 return annotation.value(); 060 } 061 else { 062 return getStructClass().getSimpleName(); 063 } 064 } 065 066 @Override 067 protected Class<?> extractBuilderClass() 068 { 069 ThriftStruct annotation = getStructClass().getAnnotation(ThriftStruct.class); 070 if (annotation != null && !annotation.builder().equals(void.class)) { 071 return annotation.builder(); 072 } 073 else { 074 return null; 075 } 076 } 077 078 @Override 079 protected void validateConstructors() 080 { 081 if (constructorInjections.size() > 1) { 082 metadataErrors.addError("Multiple constructors are annotated with @ThriftConstructor ", constructorInjections); 083 } 084 } 085 086 @Override 087 protected boolean isValidateSetter(Method method) 088 { 089 return method.getParameterTypes().length >= 1; 090 } 091 092 // 093 // Build final metadata 094 // 095 @Override 096 public ThriftStructMetadata build() 097 { 098 // this code assumes that metadata is clean 099 metadataErrors.throwIfHasErrors(); 100 101 // builder constructor injection 102 ThriftMethodInjection builderMethodInjection = buildBuilderConstructorInjections(); 103 104 // constructor injection (or factory method for builder) 105 ThriftConstructorInjection constructorInjections = buildConstructorInjection(); 106 107 // fields injections 108 Iterable<ThriftFieldMetadata> fieldsMetadata = buildFieldInjections(); 109 110 // methods injections 111 List<ThriftMethodInjection> methodInjections = buildMethodInjections(); 112 113 return new ThriftStructMetadata( 114 structName, 115 structType, 116 builderType, 117 MetadataType.STRUCT, 118 Optional.fromNullable(builderMethodInjection), 119 ImmutableList.copyOf(documentation), 120 ImmutableList.copyOf(fieldsMetadata), 121 Optional.of(constructorInjections), 122 methodInjections 123 ); 124 } 125 126 private ThriftConstructorInjection buildConstructorInjection() 127 { 128 return Iterables.getOnlyElement(Lists.transform(constructorInjections, new Function<ConstructorInjection, ThriftConstructorInjection>() 129 { 130 @Override 131 public ThriftConstructorInjection apply(ConstructorInjection injection) 132 { 133 return new ThriftConstructorInjection(injection.getConstructor(), buildParameterInjections(injection.getParameters())); 134 } 135 })); 136 } 137 138 @Override 139 protected ThriftFieldMetadata buildField(Collection<FieldMetadata> input) 140 { 141 short id = -1; 142 String name = null; 143 Requiredness requiredness = Requiredness.UNSPECIFIED; 144 ThriftType type = null; 145 146 // process field injections and extractions 147 ImmutableList.Builder<ThriftInjection> injections = ImmutableList.builder(); 148 ThriftExtraction extraction = null; 149 for (FieldMetadata fieldMetadata : input) { 150 id = fieldMetadata.getId(); 151 name = fieldMetadata.getName(); 152 requiredness = fieldMetadata.getRequiredness(); 153 type = catalog.getThriftType(fieldMetadata.getJavaType()); 154 155 if (fieldMetadata instanceof FieldInjection) { 156 FieldInjection fieldInjection = (FieldInjection) fieldMetadata; 157 injections.add(new ThriftFieldInjection(fieldInjection.getId(), fieldInjection.getName(), fieldInjection.getField(), fieldInjection.getType())); 158 } 159 else if (fieldMetadata instanceof ParameterInjection) { 160 ParameterInjection parameterInjection = (ParameterInjection) fieldMetadata; 161 injections.add(new ThriftParameterInjection( 162 parameterInjection.getId(), 163 parameterInjection.getName(), 164 parameterInjection.getParameterIndex(), 165 fieldMetadata.getJavaType() 166 )); 167 } 168 else if (fieldMetadata instanceof FieldExtractor) { 169 FieldExtractor fieldExtractor = (FieldExtractor) fieldMetadata; 170 extraction = new ThriftFieldExtractor(fieldExtractor.getId(), fieldExtractor.getName(), fieldExtractor.getType(), fieldExtractor.getField(), fieldExtractor.getJavaType()); 171 } 172 else if (fieldMetadata instanceof MethodExtractor) { 173 MethodExtractor methodExtractor = (MethodExtractor) fieldMetadata; 174 extraction = new ThriftMethodExtractor(methodExtractor.getId(), methodExtractor.getName(), methodExtractor.getType(), methodExtractor.getMethod(), methodExtractor.getJavaType()); 175 } 176 } 177 178 // add type coercion 179 TypeCoercion coercion = null; 180 if (type.isCoerced()) { 181 coercion = catalog.getDefaultCoercion(type.getJavaType()); 182 } 183 184 ThriftFieldMetadata thriftFieldMetadata = new ThriftFieldMetadata( 185 id, 186 requiredness, 187 type, 188 name, 189 THRIFT_FIELD, 190 injections.build(), 191 Optional.<ThriftConstructorInjection>absent(), 192 Optional.<ThriftMethodInjection>absent(), 193 Optional.fromNullable(extraction), 194 Optional.fromNullable(coercion) 195 ); 196 return thriftFieldMetadata; 197 } 198}