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.ThriftField; 019import com.google.common.base.Function; 020import com.google.common.base.Optional; 021import com.google.common.base.Predicate; 022import com.google.common.collect.ImmutableList; 023 024import javax.annotation.concurrent.Immutable; 025 026import java.util.List; 027import java.util.Objects; 028 029import static com.facebook.swift.codec.ThriftField.Requiredness; 030import static com.google.common.base.Preconditions.checkArgument; 031import static com.google.common.base.Preconditions.checkNotNull; 032 033/** 034 * ThriftFieldMetadata defines a single thrift field including the value extraction and injection 035 * points. 036 */ 037@Immutable 038public class ThriftFieldMetadata 039{ 040 private final short id; 041 private final ThriftType thriftType; 042 private final String name; 043 private final FieldKind fieldKind; 044 private final List<ThriftInjection> injections; 045 private final Optional<ThriftConstructorInjection> constructorInjection; 046 private final Optional<ThriftMethodInjection> methodInjection; 047 private final Optional<ThriftExtraction> extraction; 048 private final Optional<TypeCoercion> coercion; 049 private final ImmutableList<String> documentation; 050 private final Requiredness requiredness; 051 052 public ThriftFieldMetadata( 053 short id, 054 Requiredness requiredness, 055 ThriftType thriftType, 056 String name, 057 FieldKind fieldKind, 058 List<ThriftInjection> injections, 059 Optional<ThriftConstructorInjection> constructorInjection, 060 Optional<ThriftMethodInjection> methodInjection, 061 Optional<ThriftExtraction> extraction, 062 Optional<TypeCoercion> coercion 063 ) 064 { 065 this.requiredness = requiredness; 066 this.thriftType= checkNotNull(thriftType, "thriftType is null"); 067 this.fieldKind = checkNotNull(fieldKind, "type is null"); 068 this.name = checkNotNull(name, "name is null"); 069 this.injections = ImmutableList.copyOf(checkNotNull(injections, "injections is null")); 070 this.constructorInjection = checkNotNull(constructorInjection, "constructorInjection is null"); 071 this.methodInjection = checkNotNull(methodInjection, "methodInjection is null"); 072 073 this.extraction = checkNotNull(extraction, "extraction is null"); 074 this.coercion = checkNotNull(coercion, "coercion is null"); 075 076 switch (fieldKind) { 077 case THRIFT_FIELD: 078 checkArgument(id >= 0, "id is negative"); 079 break; 080 case THRIFT_UNION_ID: 081 checkArgument(id == Short.MIN_VALUE, "thrift union id must be Short.MIN_VALUE"); 082 break; 083 } 084 085 checkArgument(!injections.isEmpty() 086 || extraction.isPresent() 087 || constructorInjection.isPresent() 088 || methodInjection.isPresent(), "A thrift field must have an injection or extraction point"); 089 090 this.id = id; 091 092 if (extraction.isPresent()) { 093 if (extraction.get() instanceof ThriftFieldExtractor) { 094 ThriftFieldExtractor e = (ThriftFieldExtractor)extraction.get(); 095 this.documentation = ThriftCatalog.getThriftDocumentation(e.getField()); 096 } else if (extraction.get() instanceof ThriftMethodExtractor) { 097 ThriftMethodExtractor e = (ThriftMethodExtractor)extraction.get(); 098 this.documentation = ThriftCatalog.getThriftDocumentation(e.getMethod()); 099 } 100 else { 101 this.documentation = ImmutableList.of(); 102 } 103 } else { 104 // no extraction = no documentation 105 this.documentation = ImmutableList.of(); 106 } 107 } 108 109 public short getId() 110 { 111 return id; 112 } 113 114 public ThriftType getThriftType() 115 { 116 return thriftType; 117 } 118 119 public Requiredness getRequiredness() { return requiredness; } 120 121 public String getName() 122 { 123 return name; 124 } 125 126 public FieldKind getType() 127 { 128 return fieldKind; 129 } 130 131 public boolean isInternal() 132 { 133 switch (getType()) { 134 // These are normal thrift fields (i.e. they should be emitted by the swift2thrift generator) 135 case THRIFT_FIELD: 136 return false; 137 138 // Other fields types are used internally in swift, but do not make up part of the external 139 // thrift interface 140 default: 141 return true; 142 } 143 } 144 145 public boolean isReadOnly() 146 { 147 return injections.isEmpty() && !constructorInjection.isPresent() && !methodInjection.isPresent(); 148 } 149 150 public boolean isWriteOnly() 151 { 152 return !extraction.isPresent(); 153 } 154 155 public List<ThriftInjection> getInjections() 156 { 157 return injections; 158 } 159 160 public Optional<ThriftConstructorInjection> getConstructorInjection() 161 { 162 return constructorInjection; 163 } 164 165 public Optional<ThriftMethodInjection> getMethodInjection() 166 { 167 return methodInjection; 168 } 169 170 public Optional<ThriftExtraction> getExtraction() 171 { 172 return extraction; 173 } 174 175 public Optional<TypeCoercion> getCoercion() 176 { 177 return coercion; 178 } 179 180 public ImmutableList<String> getDocumentation() 181 { 182 return documentation; 183 } 184 185 @Override 186 public String toString() 187 { 188 final StringBuilder sb = new StringBuilder(); 189 sb.append("ThriftFieldMetadata"); 190 sb.append("{id=").append(id); 191 sb.append(", thriftType=").append(thriftType); 192 sb.append(", name='").append(name).append('\''); 193 sb.append(", fieldKind=").append(fieldKind); 194 sb.append(", injections=").append(injections); 195 sb.append(", constructorInjection=").append(constructorInjection); 196 sb.append(", methodInjection=").append(methodInjection); 197 sb.append(", extraction=").append(extraction); 198 sb.append(", coercion=").append(coercion); 199 sb.append('}'); 200 return sb.toString(); 201 } 202 203 @Override 204 public int hashCode() 205 { 206 return Objects.hash(id, thriftType, name); 207 } 208 209 @Override 210 public boolean equals(Object obj) 211 { 212 if (this == obj) { 213 return true; 214 } 215 if (obj == null || getClass() != obj.getClass()) { 216 return false; 217 } 218 final ThriftFieldMetadata other = (ThriftFieldMetadata) obj; 219 return Objects.equals(this.id, other.id) && Objects.equals(this.thriftType, other.thriftType) && Objects.equals(this.name, other.name); 220 } 221 222 public static Function<ThriftFieldMetadata, Short> getIdGetter() 223 { 224 return new Function<ThriftFieldMetadata, Short>() { 225 @Override 226 public Short apply(ThriftFieldMetadata metadata) 227 { 228 return metadata.getId(); 229 } 230 }; 231 } 232 233 public static Predicate<ThriftFieldMetadata> isTypePredicate(final FieldKind type) 234 { 235 return new Predicate<ThriftFieldMetadata>() { 236 @Override 237 public boolean apply(ThriftFieldMetadata fieldMetadata) 238 { 239 return fieldMetadata.getType() == type; 240 } 241 }; 242 } 243}