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.ThriftProtocolType; 019import com.google.common.base.Preconditions; 020import com.google.common.reflect.TypeParameter; 021import com.google.common.reflect.TypeToken; 022 023import javax.annotation.concurrent.Immutable; 024 025import java.lang.reflect.Type; 026import java.nio.ByteBuffer; 027import java.util.List; 028import java.util.Map; 029import java.util.Set; 030 031import static com.facebook.swift.codec.ThriftProtocolType.ENUM; 032import static com.facebook.swift.codec.ThriftProtocolType.STRUCT; 033import static com.google.common.base.Preconditions.checkNotNull; 034import static com.google.common.base.Preconditions.checkState; 035 036/** 037 * ThriftType contains all metadata necessary for converting the java type to and from Thrift. 038 */ 039@Immutable 040public class ThriftType 041{ 042 public static final ThriftType BOOL = new ThriftType(ThriftProtocolType.BOOL, boolean.class); 043 public static final ThriftType BYTE = new ThriftType(ThriftProtocolType.BYTE, byte.class); 044 public static final ThriftType DOUBLE = new ThriftType(ThriftProtocolType.DOUBLE, double.class); 045 public static final ThriftType I16 = new ThriftType(ThriftProtocolType.I16, short.class); 046 public static final ThriftType I32 = new ThriftType(ThriftProtocolType.I32, int.class); 047 public static final ThriftType I64 = new ThriftType(ThriftProtocolType.I64, long.class); 048 public static final ThriftType STRING = new ThriftType(ThriftProtocolType.STRING, String.class); 049 public static final ThriftType BINARY = new ThriftType(ThriftProtocolType.BINARY, ByteBuffer.class); 050 public static final ThriftType VOID = new ThriftType(ThriftProtocolType.STRUCT, void.class); 051 052 public static ThriftType struct(ThriftStructMetadata structMetadata) 053 { 054 return new ThriftType(structMetadata); 055 } 056 057 public static <K, V> ThriftType map(ThriftType keyType, ThriftType valueType) 058 { 059 checkNotNull(keyType, "keyType is null"); 060 checkNotNull(valueType, "valueType is null"); 061 062 @SuppressWarnings("serial") 063 Type javaType = new TypeToken<Map<K, V>>(){} 064 .where(new TypeParameter<K>(){}, (TypeToken<K>) TypeToken.of(keyType.getJavaType())) 065 .where(new TypeParameter<V>(){}, (TypeToken<V>) TypeToken.of(valueType.getJavaType())) 066 .getType(); 067 return new ThriftType(ThriftProtocolType.MAP, javaType, keyType, valueType); 068 } 069 070 public static <E> ThriftType set(ThriftType valueType) 071 { 072 Preconditions.checkNotNull(valueType, "valueType is null"); 073 074 @SuppressWarnings("serial") 075 Type javaType = new TypeToken<Set<E>>(){} 076 .where(new TypeParameter<E>(){}, (TypeToken<E>) TypeToken.of(valueType.getJavaType())) 077 .getType(); 078 return new ThriftType(ThriftProtocolType.SET, javaType, null, valueType); 079 } 080 081 public static <E> ThriftType list(ThriftType valueType) 082 { 083 checkNotNull(valueType, "valueType is null"); 084 085 @SuppressWarnings("serial") 086 Type javaType = new TypeToken<List<E>>(){} 087 .where(new TypeParameter<E>(){}, (TypeToken<E>) TypeToken.of(valueType.getJavaType())) 088 .getType(); 089 return new ThriftType(ThriftProtocolType.LIST, javaType, null, valueType); 090 } 091 092 public static ThriftType array(ThriftType valueType) 093 { 094 checkNotNull(valueType, "valueType is null"); 095 Class<?> javaType = ReflectionHelper.getArrayOfType(valueType.getJavaType()); 096 return new ThriftType(ThriftProtocolType.LIST, javaType, null, valueType); 097 } 098 099 public static ThriftType enumType(ThriftEnumMetadata<?> enumMetadata) 100 { 101 checkNotNull(enumMetadata, "enumMetadata is null"); 102 return new ThriftType(enumMetadata); 103 } 104 105 private final ThriftProtocolType protocolType; 106 private final Type javaType; 107 private final ThriftType keyType; 108 private final ThriftType valueType; 109 private final ThriftStructMetadata structMetadata; 110 private final ThriftEnumMetadata<?> enumMetadata; 111 private final ThriftType uncoercedType; 112 113 private ThriftType(ThriftProtocolType protocolType, Type javaType) 114 { 115 Preconditions.checkNotNull(protocolType, "protocolType is null"); 116 Preconditions.checkNotNull(javaType, "javaType is null"); 117 118 this.protocolType = protocolType; 119 this.javaType = javaType; 120 keyType = null; 121 valueType = null; 122 structMetadata = null; 123 enumMetadata = null; 124 uncoercedType = null; 125 } 126 127 private ThriftType(ThriftProtocolType protocolType, Type javaType, ThriftType keyType, ThriftType valueType) 128 { 129 Preconditions.checkNotNull(protocolType, "protocolType is null"); 130 Preconditions.checkNotNull(javaType, "javaType is null"); 131 Preconditions.checkNotNull(valueType, "valueType is null"); 132 133 this.protocolType = protocolType; 134 this.javaType = javaType; 135 this.keyType = keyType; 136 this.valueType = valueType; 137 this.structMetadata = null; 138 this.enumMetadata = null; 139 this.uncoercedType = null; 140 } 141 142 private ThriftType(ThriftStructMetadata structMetadata) 143 { 144 Preconditions.checkNotNull(structMetadata, "structMetadata is null"); 145 146 this.protocolType = STRUCT; 147 this.javaType = structMetadata.getStructClass(); 148 keyType = null; 149 valueType = null; 150 this.structMetadata = structMetadata; 151 this.enumMetadata = null; 152 this.uncoercedType = null; 153 } 154 155 private ThriftType(ThriftEnumMetadata<?> enumMetadata) 156 { 157 Preconditions.checkNotNull(enumMetadata, "enumMetadata is null"); 158 159 this.protocolType = ENUM; 160 this.javaType = enumMetadata.getEnumClass(); 161 keyType = null; 162 valueType = null; 163 this.structMetadata = null; 164 this.enumMetadata = enumMetadata; 165 this.uncoercedType = null; 166 } 167 168 public ThriftType(ThriftType uncoercedType, Type javaType) 169 { 170 this.javaType = javaType; 171 this.uncoercedType = uncoercedType; 172 173 this.protocolType = uncoercedType.getProtocolType(); 174 keyType = null; 175 valueType = null; 176 structMetadata = null; 177 enumMetadata = null; 178 } 179 180 public Type getJavaType() 181 { 182 return javaType; 183 } 184 185 public ThriftProtocolType getProtocolType() 186 { 187 return protocolType; 188 } 189 190 public ThriftType getKeyType() 191 { 192 checkState(keyType != null, "%s does not have a key", protocolType); 193 return keyType; 194 } 195 196 public ThriftType getValueType() 197 { 198 checkState(valueType != null, "%s does not have a value", protocolType); 199 return valueType; 200 } 201 202 public ThriftStructMetadata getStructMetadata() 203 { 204 checkState(structMetadata != null, "%s does not have struct metadata", protocolType); 205 return structMetadata; 206 } 207 208 public ThriftEnumMetadata<?> getEnumMetadata() 209 { 210 checkState(enumMetadata != null, "%s does not have enum metadata", protocolType); 211 return enumMetadata; 212 } 213 214 public boolean isCoerced() 215 { 216 return uncoercedType != null; 217 } 218 219 public ThriftType coerceTo(Type javaType) 220 { 221 if (javaType == this.javaType) { 222 return this; 223 } 224 225 Preconditions.checkState( 226 protocolType != ThriftProtocolType.STRUCT && 227 protocolType != ThriftProtocolType.SET && 228 protocolType != ThriftProtocolType.LIST && 229 protocolType != ThriftProtocolType.MAP, 230 "Coercion is not supported for %s", protocolType 231 ); 232 return new ThriftType(this, javaType); 233 } 234 235 public ThriftType getUncoercedType() 236 { 237 return uncoercedType; 238 } 239 240 @Override 241 public boolean equals(Object o) 242 { 243 if (this == o) { 244 return true; 245 } 246 if (o == null || getClass() != o.getClass()) { 247 return false; 248 } 249 250 final ThriftType that = (ThriftType) o; 251 252 if (javaType != null ? !javaType.equals(that.javaType) : that.javaType != null) { 253 return false; 254 } 255 if (protocolType != that.protocolType) { 256 return false; 257 } 258 259 return true; 260 } 261 262 @Override 263 public int hashCode() 264 { 265 int result = protocolType != null ? protocolType.hashCode() : 0; 266 result = 31 * result + (javaType != null ? javaType.hashCode() : 0); 267 return result; 268 } 269 270 @Override 271 public String toString() 272 { 273 final StringBuilder sb = new StringBuilder(); 274 sb.append("ThriftType"); 275 sb.append("{"); 276 sb.append(protocolType).append(" ").append(javaType); 277 if (structMetadata != null) { 278 sb.append(" ").append(structMetadata.getStructClass().getName()); 279 } 280 else if (keyType != null) { 281 sb.append(" keyType=").append(keyType); 282 sb.append(", valueType=").append(valueType); 283 } 284 else if (valueType != null) { 285 sb.append(" valueType=").append(valueType); 286 } 287 sb.append('}'); 288 return sb.toString(); 289 } 290}