All Downloads are FREE. Search and download functionalities are using the official Maven repository.

org.apache.thrift.partial.ThriftMetadata Maven / Gradle / Ivy

The newest version!
/*
 * Licensed to the Apache Software Foundation (ASF) under one
 * or more contributor license agreements. See the NOTICE file
 * distributed with this work for additional information
 * regarding copyright ownership. The ASF licenses this file
 * to you 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 org.apache.thrift.partial;

import java.io.Serializable;
import java.lang.reflect.Constructor;
import java.util.ArrayList;
import java.util.Collection;
import java.util.Collections;
import java.util.HashMap;
import java.util.List;
import java.util.Map;
import org.apache.commons.lang3.StringUtils;
import org.apache.thrift.TBase;
import org.apache.thrift.TFieldIdEnum;
import org.apache.thrift.TFieldRequirementType;
import org.apache.thrift.TUnion;
import org.apache.thrift.meta_data.FieldMetaData;
import org.apache.thrift.meta_data.ListMetaData;
import org.apache.thrift.meta_data.MapMetaData;
import org.apache.thrift.meta_data.SetMetaData;
import org.apache.thrift.meta_data.StructMetaData;
import org.apache.thrift.protocol.TType;

/**
 * Container for Thrift metadata classes such as {@link ThriftPrimitive}, {@link ThriftList}, etc.
 *
 * 

This class is mainly used by {@code TDeserializer}. */ public class ThriftMetadata { enum FieldTypeEnum implements TFieldIdEnum { ROOT((short) 0, "root"), ENUM((short) 1, "enum"), LIST_ELEMENT((short) 2, "listElement"), MAP_KEY((short) 3, "mapKey"), MAP_VALUE((short) 4, "mapValue"), SET_ELEMENT((short) 5, "setElement"); private final short id; private final String name; FieldTypeEnum(short id, String name) { this.id = id; this.name = name; } @Override public short getThriftFieldId() { return id; } @Override public String getFieldName() { return name; } } private enum ComparisonResult { UNKNOWN, EQUAL, NOT_EQUAL } /** * Base class of field types that can be partially deserialized. * *

Holds metadata necessary for partial deserialization. The metadata is internally computed * and used; therefore it is not visible to the users of {@code TDeserializer}. */ public abstract static class ThriftObject implements Serializable { public final ThriftObject parent; public final TFieldIdEnum fieldId; public final FieldMetaData data; // Placeholder to attach additional data. This class or its descendents // do not try to access or interpret this field. public Object additionalData; ThriftObject(ThriftObject parent, TFieldIdEnum fieldId, FieldMetaData data) { this.parent = parent; this.fieldId = fieldId; this.data = data; } /** * Converts this instance to formatted and indented string representation. * * @param sb the {@code StringBuilder} to add formatted strings to. * @param level the current indent level. */ protected abstract void toPrettyString(StringBuilder sb, int level); /** Gets a space string whose length is proportional to the given indent level. */ protected String getIndent(int level) { return StringUtils.repeat(" ", level * 4); } /** Helper method to append a formatted string to the given {@code StringBuilder}. */ protected void append(StringBuilder sb, String format, Object... args) { sb.append(String.format(format, args)); } /** Gets the name of this field. */ protected String getName() { return this.fieldId.getFieldName(); } protected List noFields = Collections.emptyList(); protected String getSubElementName(TFieldIdEnum fieldId) { return getSubElementName(fieldId, "element"); } protected String getSubElementName(TFieldIdEnum fieldId, String suffix) { return String.format("%s_%s", fieldId.getFieldName(), suffix); } private static class Factory { static ThriftObject createNew( ThriftObject parent, TFieldIdEnum fieldId, FieldMetaData data, List fields) { byte fieldType = data.valueMetaData.type; switch (fieldType) { case TType.STRUCT: return ThriftStructBase.create(parent, fieldId, data, fields); case TType.LIST: return new ThriftList(parent, fieldId, data, fields); case TType.MAP: return new ThriftMap(parent, fieldId, data, fields); case TType.SET: return new ThriftSet(parent, fieldId, data, fields); case TType.ENUM: return new ThriftEnum(parent, fieldId, data); case TType.BOOL: case TType.BYTE: case TType.I16: case TType.I32: case TType.I64: case TType.DOUBLE: case TType.STRING: return new ThriftPrimitive(parent, fieldId, data); default: throw unsupportedFieldTypeException(fieldType); } } } } /** Metadata about primitive types. */ public static class ThriftPrimitive extends ThriftObject { ThriftPrimitive(ThriftObject parent, TFieldIdEnum fieldId, FieldMetaData data) { super(parent, fieldId, data); } public boolean isBinary() { return this.data.valueMetaData.isBinary(); } @Override protected void toPrettyString(StringBuilder sb, int level) { String fieldType = this.getTypeName(); this.append(sb, "%s%s %s;\n", this.getIndent(level), fieldType, this.getName()); } private String getTypeName() { byte fieldType = this.data.valueMetaData.type; switch (fieldType) { case TType.BOOL: return "bool"; case TType.BYTE: return "byte"; case TType.I16: return "i16"; case TType.I32: return "i32"; case TType.I64: return "i64"; case TType.DOUBLE: return "double"; case TType.STRING: if (this.isBinary()) { return "binary"; } else { return "string"; } default: throw unsupportedFieldTypeException(fieldType); } } } public static class ThriftEnum extends ThriftObject { private static EnumCache enums = new EnumCache(); ThriftEnum(ThriftObject parent, TFieldIdEnum fieldId, FieldMetaData data) { super(parent, fieldId, data); } @Override protected void toPrettyString(StringBuilder sb, int level) { this.append(sb, "%senum %s;\n", this.getIndent(level), this.getName()); } } /** Metadata of container like objects: list, set, map */ public abstract static class ThriftContainer extends ThriftObject { public ThriftContainer(ThriftObject parent, TFieldIdEnum fieldId, FieldMetaData data) { super(parent, fieldId, data); } public abstract boolean hasUnion(); } public static class ThriftList extends ThriftContainer { public final ThriftObject elementData; ThriftList( ThriftObject parent, TFieldIdEnum fieldId, FieldMetaData data, List fields) { super(parent, fieldId, data); this.elementData = ThriftObject.Factory.createNew( this, FieldTypeEnum.LIST_ELEMENT, new FieldMetaData( getSubElementName(fieldId), TFieldRequirementType.REQUIRED, ((ListMetaData) data.valueMetaData).elemMetaData), fields); } @Override public boolean hasUnion() { return this.elementData instanceof ThriftUnion; } @Override protected void toPrettyString(StringBuilder sb, int level) { this.append(sb, "%slist<\n", this.getIndent(level)); this.elementData.toPrettyString(sb, level + 1); this.append(sb, "%s> %s;\n", this.getIndent(level), this.getName()); } } public static class ThriftSet extends ThriftContainer { public final ThriftObject elementData; ThriftSet( ThriftObject parent, TFieldIdEnum fieldId, FieldMetaData data, List fields) { super(parent, fieldId, data); this.elementData = ThriftObject.Factory.createNew( this, FieldTypeEnum.SET_ELEMENT, new FieldMetaData( getSubElementName(fieldId), TFieldRequirementType.REQUIRED, ((SetMetaData) data.valueMetaData).elemMetaData), fields); } @Override public boolean hasUnion() { return this.elementData instanceof ThriftUnion; } @Override protected void toPrettyString(StringBuilder sb, int level) { this.append(sb, "%sset<\n", this.getIndent(level)); this.elementData.toPrettyString(sb, level + 1); this.append(sb, "%s> %s;\n", this.getIndent(level), this.getName()); } } public static class ThriftMap extends ThriftContainer { public final ThriftObject keyData; public final ThriftObject valueData; ThriftMap( ThriftObject parent, TFieldIdEnum fieldId, FieldMetaData data, List fields) { super(parent, fieldId, data); this.keyData = ThriftObject.Factory.createNew( this, FieldTypeEnum.MAP_KEY, new FieldMetaData( getSubElementName(fieldId, "key"), TFieldRequirementType.REQUIRED, ((MapMetaData) data.valueMetaData).keyMetaData), Collections.emptyList()); this.valueData = ThriftObject.Factory.createNew( this, FieldTypeEnum.MAP_VALUE, new FieldMetaData( getSubElementName(fieldId, "value"), TFieldRequirementType.REQUIRED, ((MapMetaData) data.valueMetaData).valueMetaData), fields); } @Override public boolean hasUnion() { return (this.keyData instanceof ThriftUnion) || (this.valueData instanceof ThriftUnion); } @Override protected void toPrettyString(StringBuilder sb, int level) { this.append(sb, "%smap<\n", this.getIndent(level)); this.append(sb, "%skey = {\n", this.getIndent(level + 1)); this.keyData.toPrettyString(sb, level + 2); this.append(sb, "%s},\n", this.getIndent(level + 1)); this.append(sb, "%svalue = {\n", this.getIndent(level + 1)); this.valueData.toPrettyString(sb, level + 2); this.append(sb, "%s}\n", this.getIndent(level + 1)); this.append(sb, "%s> %s;\n", this.getIndent(level), this.getName()); } } /** * Base class for metadata of ThriftStruct and ThriftUnion. Holds functionality that is common to * both. */ public abstract static class ThriftStructBase extends ThriftObject { public ThriftStructBase(ThriftObject parent, TFieldIdEnum fieldId, FieldMetaData data) { super(parent, fieldId, data); } public Class getStructClass() { return getStructClass(this.data); } public static Class getStructClass(FieldMetaData data) { return (Class) ((StructMetaData) data.valueMetaData).structClass; } public boolean isUnion() { return isUnion(this.data); } public static boolean isUnion(FieldMetaData data) { return TUnion.class.isAssignableFrom(getStructClass(data)); } public static ThriftStructBase create( ThriftObject parent, TFieldIdEnum fieldId, FieldMetaData data, Iterable fieldsData) { if (isUnion(data)) { return new ThriftUnion<>(parent, fieldId, data, fieldsData); } else { return new ThriftStruct<>(parent, fieldId, data, fieldsData); } } } /** Metadata of a Thrift union. Currently not adequately supported. */ public static class ThriftUnion extends ThriftStructBase { public ThriftUnion( ThriftObject parent, TFieldIdEnum fieldId, FieldMetaData data, Iterable fieldsData) { super(parent, fieldId, data); } @Override protected void toPrettyString(StringBuilder sb, int level) { String indent = this.getIndent(level); String indent2 = this.getIndent(level + 1); this.append(sb, "%sunion %s {\n", indent, this.getName()); this.append(sb, "%s// unions not adequately supported at present.\n", indent2); this.append(sb, "%s}\n", indent); } } /** Metadata of a Thrift struct. */ public static class ThriftStruct extends ThriftStructBase { public final Map fields; ThriftStruct( ThriftObject parent, TFieldIdEnum fieldId, FieldMetaData data, Iterable fieldsData) { super(parent, fieldId, data); Class clasz = getStructClass(data); this.fields = getFields(this, clasz, fieldsData); } public T createNewStruct() { try { Class structClass = getStructClass(this.data); Constructor declaredConstructor = structClass.getDeclaredConstructor(); return declaredConstructor.newInstance(); } catch (ReflectiveOperationException e) { throw new RuntimeException(e); } } public static ThriftStruct of(Class clasz) { return ThriftStruct.fromFields(clasz, Collections.emptyList()); } public static ThriftStruct fromFieldNames( Class clasz, Collection fieldNames) { return fromFields(clasz, ThriftField.fromNames(fieldNames)); } public static ThriftStruct fromFields( Class clasz, Iterable fields) { Validate.checkNotNull(clasz, "clasz"); Validate.checkNotNull(fields, "fields"); return new ThriftStruct<>( null, FieldTypeEnum.ROOT, new FieldMetaData( FieldTypeEnum.ROOT.getFieldName(), TFieldRequirementType.REQUIRED, new StructMetaData(TType.STRUCT, clasz)), fields); } @Override public String toString() { StringBuilder sb = new StringBuilder(); this.toPrettyString(sb, 0); return sb.toString(); } @Override protected void toPrettyString(StringBuilder sb, int level) { String indent = this.getIndent(level); String indent2 = this.getIndent(level + 1); this.append(sb, "%sstruct %s {\n", indent, this.getName()); if (this.fields.size() == 0) { this.append(sb, "%s*;", indent2); } else { List ids = new ArrayList<>(this.fields.keySet()); Collections.sort(ids); for (Integer id : ids) { this.fields.get(id).toPrettyString(sb, level + 1); } } this.append(sb, "%s}\n", indent); } private static Map getFields( ThriftStruct parent, Class clasz, Iterable fieldsData) { Map fieldsMetaData = FieldMetaData.getStructMetaDataMap(clasz); Map fields = new HashMap<>(); boolean getAllFields = !fieldsData.iterator().hasNext(); if (getAllFields) { for (Map.Entry entry : fieldsMetaData.entrySet()) { TFieldIdEnum fieldId = entry.getKey(); FieldMetaData fieldMetaData = entry.getValue(); ThriftObject field = ThriftObject.Factory.createNew( parent, fieldId, fieldMetaData, Collections.emptyList()); fields.put((int) fieldId.getThriftFieldId(), field); } } else { for (ThriftField fieldData : fieldsData) { String fieldName = fieldData.name; FieldMetaData fieldMetaData = findFieldMetaData(fieldsMetaData, fieldName); TFieldIdEnum fieldId = findFieldId(fieldsMetaData, fieldName); ThriftObject field = ThriftObject.Factory.createNew(parent, fieldId, fieldMetaData, fieldData.fields); fields.put((int) fieldId.getThriftFieldId(), field); } } return fields; } private static FieldMetaData findFieldMetaData( Map fieldsMetaData, String fieldName) { for (FieldMetaData fieldData : fieldsMetaData.values()) { if (fieldData.fieldName.equals(fieldName)) { return fieldData; } } throw fieldNotFoundException(fieldName); } private static TFieldIdEnum findFieldId( Map fieldsMetaData, String fieldName) { for (TFieldIdEnum fieldId : fieldsMetaData.keySet()) { if (fieldId.getFieldName().equals(fieldName)) { return fieldId; } } throw fieldNotFoundException(fieldName); } } static IllegalArgumentException fieldNotFoundException(String fieldName) { return new IllegalArgumentException("field not found: '" + fieldName + "'"); } static UnsupportedOperationException unsupportedFieldTypeException(byte fieldType) { return new UnsupportedOperationException("field type not supported: '" + fieldType + "'"); } }