// Copyright (c) 2009 The Chromium Authors. All rights reserved.
// Use of this source code is governed by a BSD-style license that can be
// found in the LICENSE file.
package org.chromium.sdk.internal.protocolparser.dynamicimpl;
import java.lang.annotation.RetentionPolicy;
import java.lang.reflect.Method;
import java.lang.reflect.ParameterizedType;
import java.lang.reflect.Proxy;
import java.lang.reflect.Type;
import java.lang.reflect.WildcardType;
import java.util.AbstractList;
import java.util.ArrayList;
import java.util.Arrays;
import java.util.Collection;
import java.util.Collections;
import java.util.HashMap;
import java.util.LinkedHashMap;
import java.util.List;
import java.util.Map;
import java.util.Set;
import java.util.concurrent.atomic.AtomicReferenceArray;
import org.chromium.sdk.internal.protocolparser.FieldLoadStrategy;
import org.chromium.sdk.internal.protocolparser.JsonField;
import org.chromium.sdk.internal.protocolparser.JsonNullable;
import org.chromium.sdk.internal.protocolparser.JsonObjectBased;
import org.chromium.sdk.internal.protocolparser.JsonOptionalField;
import org.chromium.sdk.internal.protocolparser.JsonOverrideField;
import org.chromium.sdk.internal.protocolparser.JsonParserRoot;
import org.chromium.sdk.internal.protocolparser.JsonProtocolModelParseException;
import org.chromium.sdk.internal.protocolparser.JsonProtocolParseException;
import org.chromium.sdk.internal.protocolparser.JsonSubtype;
import org.chromium.sdk.internal.protocolparser.JsonSubtypeCasting;
import org.chromium.sdk.internal.protocolparser.JsonType;
import org.chromium.sdk.internal.protocolparser.dynamicimpl.JavaCodeGenerator.ClassScope;
import org.chromium.sdk.internal.protocolparser.dynamicimpl.JavaCodeGenerator.FileScope;
import org.chromium.sdk.internal.protocolparser.dynamicimpl.JavaCodeGenerator.GlobalScope;
import org.chromium.sdk.internal.protocolparser.dynamicimpl.JavaCodeGenerator.MethodScope;
import org.chromium.sdk.internal.protocolparser.dynamicimpl.JavaCodeGenerator.Util;
import org.chromium.sdk.internal.protocolparser.implutil.CommonImpl.ParseRuntimeException;
import org.json.simple.JSONArray;
import org.json.simple.JSONObject;
/**
* Java dynamic-proxy based implementation of {@link JsonProtocolParser}. It analyses
* interfaces with reflection and provides their implementation by {@link Proxy} factory.
* User-friendly 'root' interface is available by {@link #getParserRoot()} method.
* @param <ROOT> root user-provided type (see {@link JsonParserRoot})
*/
public class DynamicParserImpl<ROOT> {
private final Map<Class<?>, TypeHandler<?>> type2TypeHandler;
private final ParserRootImpl<ROOT> rootImpl;
/**
* Constructs parser from a set of type interfaces.
*/
public DynamicParserImpl(Class<ROOT> parserRootClass, List<Class<?>> protocolInterfaces)
throws JsonProtocolModelParseException {
this(parserRootClass, protocolInterfaces, Collections.<DynamicParserImpl<?>>emptyList());
}
/**
* Constructs parser from a set of type interfaces and a list of base packages. Type interfaces
* may reference to type interfaces from base packages.
* @param basePackages list of base packages in form of list of {@link DynamicParserImpl}'s
*/
public DynamicParserImpl(Class<ROOT> parserRootClass,
List<? extends Class<?>> protocolInterfaces,
List<? extends DynamicParserImpl<?>> basePackages) throws JsonProtocolModelParseException {
this(parserRootClass, protocolInterfaces, basePackages, false);
}
public DynamicParserImpl(Class<ROOT> parserRootClass,
List<? extends Class<?>> protocolInterfaces,
List<? extends DynamicParserImpl<?>> basePackages, boolean strictMode)
throws JsonProtocolModelParseException {
type2TypeHandler = readTypes(protocolInterfaces, basePackages, strictMode);
rootImpl = new ParserRootImpl<ROOT>(parserRootClass, type2TypeHandler);
}
public ROOT getParserRoot() {
return rootImpl.getInstance();
}
private static Map<Class<?>, TypeHandler<?>> readTypes(
List<? extends Class<?>> protocolInterfaces,
final List<? extends DynamicParserImpl<?>> basePackages, boolean strictMode)
throws JsonProtocolModelParseException {
ReadInterfacesSession session =
new ReadInterfacesSession(protocolInterfaces, basePackages, strictMode);
session.go();
return session.getResult();
}
private static class ReadInterfacesSession {
private final Map<Class<?>, TypeHandler<?>> type2typeHandler;
private final List<? extends DynamicParserImpl<?>> basePackages;
private final boolean strictMode;
final List<RefImpl<?>> refs = new ArrayList<RefImpl<?>>();
final List<SubtypeCaster> subtypeCasters =
new ArrayList<SubtypeCaster>();
ReadInterfacesSession(List<? extends Class<?>> protocolInterfaces,
List<? extends DynamicParserImpl<?>> basePackages, boolean strictMode) {
// Keep interfaces ordered to keep generated parser less random.
this.type2typeHandler = new LinkedHashMap<Class<?>, TypeHandler<?>>();
this.basePackages = basePackages;
this.strictMode = strictMode;
for (Class<?> typeClass : protocolInterfaces) {
if (type2typeHandler.containsKey(typeClass)) {
throw new IllegalArgumentException(
"Protocol interface duplicated " + typeClass.getName());
}
type2typeHandler.put(typeClass, null);
}
}
void go() throws JsonProtocolModelParseException {
// Create TypeHandler's.
for (Class<?> typeClass : type2typeHandler.keySet()) {
TypeHandler<?> typeHandler = createTypeHandler(typeClass);
type2typeHandler.put(typeClass, typeHandler);
}
// Resolve cross-references.
for (RefImpl<?> ref : refs) {
TypeHandler<?> type = type2typeHandler.get(ref.typeClass);
if (type == null) {
throw new RuntimeException();
}
ref.set(type);
}
// Set subtype casters.
for (SubtypeCaster subtypeCaster : subtypeCasters) {
TypeHandler<?> subtypeHandler = subtypeCaster.getSubtypeHandler();
subtypeHandler.getSubtypeSupport().setSubtypeCaster(subtypeCaster);
}
// Check subtype casters consistency.
for (TypeHandler<?> type : type2typeHandler.values()) {
type.getSubtypeSupport().checkHasSubtypeCaster();
}
if (strictMode) {
for (TypeHandler<?> type : type2typeHandler.values()) {
type.buildClosedNameSet();
}
}
}
Map<Class<?>, TypeHandler<?>> getResult() {
return type2typeHandler;
}
private <T> TypeHandler<T> createTypeHandler(Class<T> typeClass)
throws JsonProtocolModelParseException {
if (!typeClass.isInterface()) {
throw new JsonProtocolModelParseException("Json model type should be interface: " +
typeClass.getName());
}
FieldProcessor<T> fields = new FieldProcessor<T>(typeClass);
fields.go();
Map<Method, MethodHandler> methodHandlerMap = fields.getMethodHandlerMap();
methodHandlerMap.putAll(BaseHandlersLibrary.INSTANCE.getAllHandlers());
TypeHandler.EagerFieldParser eagerFieldParser =
new EagerFieldParserImpl(fields.getOnDemandHanlers());
RefToType<?> superclassRef = getSuperclassRef(typeClass);
boolean requiresJsonObject = fields.requiresJsonObject() ||
JsonObjectBased.class.isAssignableFrom(typeClass);
return new TypeHandler<T>(typeClass, superclassRef,
fields.getFieldArraySize(), fields.getVolatileFields(), methodHandlerMap,
fields.getFieldLoaders(),
fields.getFieldConditions(), eagerFieldParser, fields.getAlgCasesData(),
requiresJsonObject, strictMode);
}
private SlowParser<?> getFieldTypeParser(Type type, boolean declaredNullable,
boolean isSubtyping, FieldLoadStrategy loadStrategy)
throws JsonProtocolModelParseException {
if (type instanceof Class) {
Class<?> typeClass = (Class<?>) type;
if (type == Long.class) {
nullableIsNotSupported(declaredNullable);
return LONG_PARSER.getNullable();
} else if (type == Long.TYPE) {
nullableIsNotSupported(declaredNullable);
return LONG_PARSER.getNotNullable();
} else if (type == Boolean.class) {
nullableIsNotSupported(declaredNullable);
return BOOLEAN_PARSER.getNullable();
} else if (type == Boolean.TYPE) {
nullableIsNotSupported(declaredNullable);
return BOOLEAN_PARSER.getNotNullable();
} else if (type == Float.class) {
nullableIsNotSupported(declaredNullable);
return FLOAT_PARSER.getNullable();
} else if (type == Float.TYPE) {
nullableIsNotSupported(declaredNullable);
return FLOAT_PARSER.getNotNullable();
} else if (type == Number.class) {
return NUMBER_PARSER.get(declaredNullable);
} else if (type == Void.class) {
nullableIsNotSupported(declaredNullable);
return VOID_PARSER;
} else if (type == String.class) {
return STRING_PARSER.get(declaredNullable);
} else if (type == Object.class) {
return OBJECT_PARSER.get(declaredNullable);
} else if (type == JSONObject.class) {
return JSON_PARSER.get(declaredNullable);
} else if (typeClass.isEnum()) {
Class<RetentionPolicy> enumTypeClass = (Class<RetentionPolicy>) typeClass;
return EnumParser.create(enumTypeClass, declaredNullable);
} else if (type2typeHandler.containsKey(typeClass)) {
}
RefToType<?> ref = getTypeRef(typeClass);
if (ref != null) {
return createJsonParser(ref, declaredNullable, isSubtyping);
}
throw new JsonProtocolModelParseException("Method return type " + type +
" (simple class) not supported");
} else if (type instanceof ParameterizedType) {
ParameterizedType parameterizedType = (ParameterizedType) type;
if (parameterizedType.getRawType() == List.class) {
Type argumentType = parameterizedType.getActualTypeArguments()[0];
if (argumentType instanceof WildcardType) {
WildcardType wildcard = (WildcardType) argumentType;
if (wildcard.getLowerBounds().length == 0 && wildcard.getUpperBounds().length == 1) {
argumentType = wildcard.getUpperBounds()[0];
}
}
SlowParser<?> componentParser =
getFieldTypeParser(argumentType, false, false, loadStrategy);
return createArrayParser(componentParser, declaredNullable, loadStrategy);
} else {
throw new JsonProtocolModelParseException("Method return type " + type +
" (generic) not supported");
}
} else {
throw new JsonProtocolModelParseException("Method return type " + type + " not supported");
}
}
private void nullableIsNotSupported(boolean declaredNullable)
throws JsonProtocolModelParseException {
if (declaredNullable) {
throw new JsonProtocolModelParseException("The type cannot be declared nullable");
}
}
private <T> JsonTypeParser<T> createJsonParser(RefToType<T> type, boolean isNullable,
boolean isSubtyping) {
return new JsonTypeParser<T>(type, isNullable, isSubtyping);
}
private <T> ArrayParser<T> createArrayParser(SlowParser<T> componentParser,
boolean isNullable, FieldLoadStrategy loadStrategy) {
if (loadStrategy == FieldLoadStrategy.LAZY) {
return new ArrayParser<T>(componentParser, isNullable, ArrayParser.LAZY);
} else {
return new ArrayParser<T>(componentParser, isNullable, ArrayParser.EAGER);
}
}
private <T> RefToType<T> getTypeRef(final Class<T> typeClass) {
if (type2typeHandler.containsKey(typeClass)) {
RefImpl<T> result = new RefImpl<T>(typeClass);
refs.add(result);
return result;
}
for (DynamicParserImpl<?> baseParser : basePackages) {
TypeHandler<?> typeHandler = baseParser.type2TypeHandler.get(typeClass);
if (typeHandler != null) {
final TypeHandler<T> typeHandlerT = (TypeHandler<T>) typeHandler;
return new RefToType<T>() {
@Override
TypeHandler<T> get() {
return typeHandlerT;
}
@Override
Class<?> getTypeClass() {
return typeClass;
}
};
}
}
return null;
}
private RefToType<?> getSuperclassRef(Class<?> typeClass)
throws JsonProtocolModelParseException {
RefToType<?> result = null;
for (Type interfc : typeClass.getGenericInterfaces()) {
if (interfc instanceof ParameterizedType == false) {
continue;
}
ParameterizedType parameterizedType = (ParameterizedType) interfc;
if (parameterizedType.getRawType() != JsonSubtype.class) {
continue;
}
Type param = parameterizedType.getActualTypeArguments()[0];
if (param instanceof Class == false) {
throw new JsonProtocolModelParseException("Unexpected type of superclass " + param);
}
Class<?> paramClass = (Class<?>) param;
if (result != null) {
throw new JsonProtocolModelParseException("Already has superclass " +
result.getTypeClass().getName());
}
result = getTypeRef(paramClass);
if (result == null) {
throw new JsonProtocolModelParseException("Unknown base class " + paramClass.getName());
}
}
return result;
}
class FieldProcessor<T> {
private final Class<T> typeClass;
private final JsonType jsonTypeAnn;
private final List<FieldLoader> fieldLoaders = new ArrayList<FieldLoader>(2);
private final List<LazyHandler> onDemandHanlers = new ArrayList<LazyHandler>();
private final Map<Method, MethodHandler> methodHandlerMap =
new HashMap<Method, MethodHandler>();
private final FieldMap fieldMap = new FieldMap();
private final List<FieldCondition> fieldConditions = new ArrayList<FieldCondition>(2);
private ManualAlgebraicCasesDataImpl manualAlgCasesData = null;
private AutoAlgebraicCasesDataImpl autoAlgCasesData = null;
private int fieldArraySize = 0;
private List<VolatileFieldBinding> volatileFields = new ArrayList<VolatileFieldBinding>(2);
private boolean requiresJsonObject = false;
FieldProcessor(Class<T> typeClass) throws JsonProtocolModelParseException {
this.typeClass = typeClass;
jsonTypeAnn = typeClass.getAnnotation(JsonType.class);
if (jsonTypeAnn == null) {
throw new JsonProtocolModelParseException("Not a json model type: " + typeClass);
}
}
void go() throws JsonProtocolModelParseException {
for (Method m : typeClass.getDeclaredMethods()) {
try {
processMethod(m);
} catch (JsonProtocolModelParseException e) {
throw new JsonProtocolModelParseException("Problem with method " + m, e);
}
}
}
private void processMethod(Method m) throws JsonProtocolModelParseException {
if (m.getParameterTypes().length != 0) {
throw new JsonProtocolModelParseException("No parameters expected in " + m);
}
JsonOverrideField overrideFieldAnn = m.getAnnotation(JsonOverrideField.class);
FieldConditionLogic fieldConditionLogic = FieldConditionLogic.readLogic(m);
String fieldName = checkAndGetJsonFieldName(m);
MethodHandler methodHandler;
JsonSubtypeCasting jsonSubtypeCaseAnn = m.getAnnotation(JsonSubtypeCasting.class);
if (jsonSubtypeCaseAnn != null) {
if (fieldConditionLogic != null) {
throw new JsonProtocolModelParseException(
"Subtype condition annotation only works with field getter methods");
}
if (overrideFieldAnn != null) {
throw new JsonProtocolModelParseException(
"Override annotation only works with field getter methods");
}
if (jsonTypeAnn.subtypesChosenManually()) {
if (manualAlgCasesData == null) {
manualAlgCasesData = new ManualAlgebraicCasesDataImpl();
}
methodHandler = processManualSubtypeMethod(m, jsonSubtypeCaseAnn);
} else {
if (autoAlgCasesData == null) {
autoAlgCasesData = new AutoAlgebraicCasesDataImpl();
}
if (jsonSubtypeCaseAnn.reinterpret()) {
throw new JsonProtocolModelParseException(
"Option 'reinterpret' is only available with 'subtypes chosen manually'");
}
requiresJsonObject = true;
methodHandler = processAutomaticSubtypeMethod(m);
}
} else {
requiresJsonObject = true;
methodHandler = processFieldGetterMethod(m, fieldConditionLogic, overrideFieldAnn,
fieldName);
}
methodHandlerMap.put(m, methodHandler);
}
private MethodHandler processFieldGetterMethod(Method m,
FieldConditionLogic fieldConditionLogic, JsonOverrideField overrideFieldAnn,
String fieldName) throws JsonProtocolModelParseException {
MethodHandler methodHandler;
FieldLoadStrategy loadStrategy;
if (m.getAnnotation(JsonField.class) == null) {
loadStrategy = FieldLoadStrategy.AUTO;
} else {
loadStrategy = m.getAnnotation(JsonField.class).loadStrategy();
}
JsonNullable nullableAnn = m.getAnnotation(JsonNullable.class);
SlowParser<?> fieldTypeParser = getFieldTypeParser(m.getGenericReturnType(),
nullableAnn != null, false, loadStrategy);
if (fieldConditionLogic != null) {
fieldConditions.add(new FieldCondition(fieldName, fieldTypeParser.asQuickParser(),
fieldConditionLogic));
}
if (overrideFieldAnn == null) {
fieldMap.localNames.add(fieldName);
} else {
fieldMap.overridenNames.add(fieldName);
}
boolean isOptional = isOptionalField(m);
if (fieldTypeParser.asQuickParser() != null) {
QuickParser<?> quickParser = fieldTypeParser.asQuickParser();
if (loadStrategy == FieldLoadStrategy.EAGER) {
methodHandler = createEagerLoadGetterHandler(fieldName, fieldTypeParser, isOptional);
} else {
methodHandler = createLazyQuickGetterHandler(quickParser, isOptional, fieldName);
}
} else {
if (loadStrategy == FieldLoadStrategy.LAZY) {
methodHandler = createLazyCachedGetterHandler(fieldName, fieldTypeParser, isOptional);
} else {
methodHandler = createEagerLoadGetterHandler(fieldName, fieldTypeParser, isOptional);
}
}
return methodHandler;
}
private MethodHandler createLazyQuickGetterHandler(QuickParser<?> quickParser,
boolean isOptional, String fieldName) {
LazyParseFieldMethodHandler onDemandHandler = new LazyParseFieldMethodHandler(quickParser,
isOptional, fieldName, typeClass);
onDemandHanlers.add(onDemandHandler);
return onDemandHandler;
}
private MethodHandler createEagerLoadGetterHandler(String fieldName,
SlowParser<?> fieldTypeParser, boolean isOptional) {
int fieldCode = allocateFieldInArray();
FieldLoader fieldLoader = new FieldLoader(fieldCode, fieldName, fieldTypeParser,
isOptional);
fieldLoaders.add(fieldLoader);
return new PreparsedFieldMethodHandler(fieldCode,
fieldTypeParser.getValueFinisher(), fieldName);
}
private MethodHandler createLazyCachedGetterHandler(String fieldName,
SlowParser<?> fieldTypeParser, boolean isOptional) {
VolatileFieldBinding fieldBinding = allocateVolatileField(fieldTypeParser, false);
LazyCachedFieldMethodHandler lazyCachedHandler =
new LazyCachedFieldMethodHandler(fieldBinding, fieldTypeParser, isOptional,
fieldName, typeClass);
onDemandHanlers.add(lazyCachedHandler);
return lazyCachedHandler;
}
private MethodHandler processAutomaticSubtypeMethod(Method m)
throws JsonProtocolModelParseException {
MethodHandler methodHandler;
if (m.getReturnType() == Void.TYPE) {
if (autoAlgCasesData.hasDefaultCase) {
throw new JsonProtocolModelParseException("Duplicate default case method: " + m);
}
autoAlgCasesData.hasDefaultCase = true;
methodHandler = RETURN_NULL_METHOD_HANDLER;
} else {
Class<?> methodType = m.getReturnType();
RefToType<?> ref = getTypeRef(methodType);
if (ref == null) {
throw new JsonProtocolModelParseException("Unknown return type in " + m);
}
if (autoAlgCasesData.variantCodeFieldPos == -1) {
autoAlgCasesData.variantCodeFieldPos = allocateFieldInArray();
autoAlgCasesData.variantValueFieldPos = allocateFieldInArray();
}
final int algCode = autoAlgCasesData.subtypes.size();
autoAlgCasesData.subtypes.add(ref);
final AutoSubtypeMethodHandler algMethodHandler = new AutoSubtypeMethodHandler(
autoAlgCasesData.variantCodeFieldPos, autoAlgCasesData.variantValueFieldPos,
algCode);
methodHandler = algMethodHandler;
SubtypeCaster subtypeCaster = new SubtypeCaster(typeClass, ref) {
@Override
ObjectData getSubtypeObjectData(ObjectData objectData) {
return algMethodHandler.getFieldObjectData(objectData);
}
@Override
void writeJava(ClassScope scope, String expectedTypeName, String superTypeValueRef,
String resultRef) {
scope.startLine(expectedTypeName + " " + resultRef + " = " + superTypeValueRef +
"." + AutoAlgebraicCasesDataImpl.getAutoAlgFieldNameJava(algCode) + ";\n");
}
};
subtypeCasters.add(subtypeCaster);
}
return methodHandler;
}
private MethodHandler processManualSubtypeMethod(final Method m,
JsonSubtypeCasting jsonSubtypeCaseAnn) throws JsonProtocolModelParseException {
SlowParser<?> fieldTypeParser = getFieldTypeParser(m.getGenericReturnType(), false,
!jsonSubtypeCaseAnn.reinterpret(), FieldLoadStrategy.AUTO);
VolatileFieldBinding fieldInfo = allocateVolatileField(fieldTypeParser, true);
if (!Arrays.asList(m.getExceptionTypes()).contains(JsonProtocolParseException.class)) {
throw new JsonProtocolModelParseException(
"Method should declare JsonProtocolParseException exception: " + m);
}
final ManualSubtypeMethodHandler handler = new ManualSubtypeMethodHandler(fieldInfo,
fieldTypeParser);
JsonTypeParser<?> parserAsJsonTypeParser = fieldTypeParser.asJsonTypeParser();
if (parserAsJsonTypeParser != null && parserAsJsonTypeParser.isSubtyping()) {
SubtypeCaster subtypeCaster = new SubtypeCaster(typeClass,
parserAsJsonTypeParser.getType()) {
@Override
ObjectData getSubtypeObjectData(ObjectData baseObjectData)
throws JsonProtocolParseException {
ObjectData objectData = baseObjectData;
return handler.getSubtypeData(objectData);
}
@Override
void writeJava(ClassScope scope, String expectedTypeName, String superTypeValueRef,
String resultRef) {
scope.startLine(expectedTypeName + " " + resultRef + " = " + superTypeValueRef +
"." + m.getName() + "();\n");
}
};
manualAlgCasesData.subtypes.add(parserAsJsonTypeParser.getType());
subtypeCasters.add(subtypeCaster);
}
return handler;
}
int getFieldArraySize() {
return fieldArraySize;
}
List<VolatileFieldBinding> getVolatileFields() {
return volatileFields;
}
TypeHandler.AlgebraicCasesData getAlgCasesData() {
if (jsonTypeAnn.subtypesChosenManually()) {
return manualAlgCasesData;
} else {
return autoAlgCasesData;
}
}
List<FieldLoader> getFieldLoaders() {
return fieldLoaders;
}
List<LazyHandler> getOnDemandHanlers() {
return onDemandHanlers;
}
Map<Method, MethodHandler> getMethodHandlerMap() {
return methodHandlerMap;
}
List<FieldCondition> getFieldConditions() {
return fieldConditions;
}
boolean requiresJsonObject() {
return requiresJsonObject;
}
private int allocateFieldInArray() {
return fieldArraySize++;
}
private VolatileFieldBinding allocateVolatileField(final SlowParser<?> fieldTypeParser,
boolean internalType) {
int position = volatileFields.size();
FieldTypeInfo fieldTypeInfo;
if (internalType) {
fieldTypeInfo = new FieldTypeInfo() {
@Override public void appendValueTypeNameJava(FileScope scope) {
fieldTypeParser.appendInternalValueTypeNameJava(scope);
}
};
} else {
fieldTypeInfo = new FieldTypeInfo() {
@Override public void appendValueTypeNameJava(FileScope scope) {
fieldTypeParser.appendFinishedValueTypeNameJava(scope);
}
};
}
VolatileFieldBinding binding = new VolatileFieldBinding(position, fieldTypeInfo);
volatileFields.add(binding);
return binding;
}
private boolean isOptionalField(Method m) {
JsonOptionalField jsonOptionalFieldAnn = m.getAnnotation(JsonOptionalField.class);
return jsonOptionalFieldAnn != null;
}
private String checkAndGetJsonFieldName(Method m) throws JsonProtocolModelParseException {
if (m.getParameterTypes().length != 0) {
throw new JsonProtocolModelParseException("Must have 0 parameters");
}
JsonField fieldAnn = m.getAnnotation(JsonField.class);
if (fieldAnn != null) {
String jsonLiteralName = fieldAnn.jsonLiteralName();
if (!jsonLiteralName.isEmpty()) {
return jsonLiteralName;
}
}
return m.getName();
}
}
}
private static class EagerFieldParserImpl extends TypeHandler.EagerFieldParser {
private final List<LazyHandler> onDemandHandlers;
private EagerFieldParserImpl(List<LazyHandler> onDemandHandlers) {
this.onDemandHandlers = onDemandHandlers;
}
@Override
void parseAllFields(ObjectData objectData) throws JsonProtocolParseException {
for (LazyHandler handler : onDemandHandlers) {
handler.parseEager(objectData);
}
}
@Override
void addAllFieldNames(Set<? super String> output) {
for (LazyHandler handler : onDemandHandlers) {
output.add(handler.getFieldName());
}
}
}
private interface LazyHandler {
void parseEager(ObjectData objectData) throws JsonProtocolParseException;
String getFieldName();
}
private static class LazyParseFieldMethodHandler extends MethodHandler implements LazyHandler {
private final QuickParser<?> quickParser;
private final boolean isOptional;
private final String fieldName;
private final Class<?> typeClass;
LazyParseFieldMethodHandler(QuickParser<?> quickParser, boolean isOptional, String fieldName,
Class<?> typeClass) {
this.quickParser = quickParser;
this.isOptional = isOptional;
this.fieldName = fieldName;
this.typeClass = typeClass;
}
@Override
Object handle(ObjectData objectData, Object[] args) {
try {
return parse(objectData);
} catch (JsonProtocolParseException e) {
throw new ParseRuntimeException(
"On demand parsing failed for " + objectData.getUnderlyingObject(), e);
}
}
@Override
public void parseEager(ObjectData objectData) throws JsonProtocolParseException {
parse(objectData);
}
public Object parse(ObjectData objectData) throws JsonProtocolParseException {
Map<?,?> properties = (JSONObject)objectData.getUnderlyingObject();
Object value = properties.get(fieldName);
boolean hasValue;
if (value == null) {
hasValue = properties.containsKey(fieldName);
} else {
hasValue = true;
}
return parse(hasValue, value, objectData);
}
public Object parse(boolean hasValue, Object value, ObjectData objectData)
throws JsonProtocolParseException {
if (hasValue) {
try {
return quickParser.parseValueQuick(value);
} catch (JsonProtocolParseException e) {
throw new JsonProtocolParseException("Failed to parse field '" + fieldName +
"' in type " + typeClass.getName(), e);
}
} else {
if (!isOptional) {
throw new JsonProtocolParseException("Field is not optional: " + fieldName +
" (in type " + typeClass.getName() + ")");
}
return null;
}
}
@Override
public String getFieldName() {
return fieldName;
}
@Override
void writeMethodImplementationJava(ClassScope classScope, Method m) {
writeMethodDeclarationJava(classScope, m, Collections.<String>emptyList());
classScope.startLine("{\n");
MethodScope scope = classScope.newMethodScope();
scope.indentRight();
scope.startLine("");
quickParser.appendFinishedValueTypeNameJava(scope);
scope.append(" result;\n");
boolean wrap = quickParser.javaCodeThrowsException() || !isOptional;
if (wrap) {
scope.startLine("try {\n");
scope.indentRight();
}
String valueRef = scope.newMethodScopedName("value");
String hasValueRef = scope.newMethodScopedName("hasValue");
Util.writeReadValueAndHasValue(scope, fieldName, "underlying", valueRef, hasValueRef);
scope.startLine("if (" + hasValueRef + ") {\n");
scope.indentRight();
if (quickParser.javaCodeThrowsException()) {
scope.startLine("try {\n");
scope.indentRight();
quickParser.writeParseQuickCode(scope, valueRef, "r1");
scope.startLine("result = r1;\n");
scope.indentLeft();
scope.startLine("} catch (" + Util.BASE_PACKAGE + ".JsonProtocolParseException e) {\n");
scope.startLine(" throw new " + Util.BASE_PACKAGE + ".JsonProtocolParseException(" +
"\"Failed to parse field " + fieldName + " in type ");
scope.append(typeClass.getName() + "\", e);\n");
scope.startLine("}\n");
} else {
quickParser.writeParseQuickCode(scope, valueRef, "r1");
scope.startLine("result = r1;\n");
}
scope.indentLeft();
scope.startLine("} else {\n");
scope.indentRight();
if (isOptional) {
scope.startLine("result = null;\n");
} else {
scope.startLine("throw new " + Util.BASE_PACKAGE + ".JsonProtocolParseException(" +
"\"Field is not optional: " + fieldName + "\");\n");
}
scope.indentLeft();
scope.startLine("}\n");
if (wrap) {
scope.indentLeft();
scope.startLine("} catch (" + Util.BASE_PACKAGE + ".JsonProtocolParseException e) {\n");
scope.startLine(" throw new " + Util.BASE_PACKAGE +
".implutil.CommonImpl.ParseRuntimeException(" +
"\"On demand parsing failed for \" + underlying, e);\n");
scope.startLine("}\n");
}
scope.startLine("return result;\n");
scope.indentLeft();
scope.startLine("}\n");
}
}
/**
* Basic implementation of the method that parses value on demand and store it for
* a future use.
*/
private static abstract class LazyCachedMethodHandlerBase extends MethodHandler {
private final VolatileFieldBinding fieldBinding;
LazyCachedMethodHandlerBase(VolatileFieldBinding fieldBinding) {
this.fieldBinding = fieldBinding;
}
@Override
Object handle(ObjectData objectData, Object[] args) {
try {
return handle(objectData);
} catch (JsonProtocolParseException e) {
throw new ParseRuntimeException(
"On demand parsing failed for " + objectData.getUnderlyingObject(), e);
}
}
Object handle(ObjectData objectData) throws JsonProtocolParseException {
Object raw = handleRaw(objectData);
return finishRawValue(raw);
}
protected abstract Object finishRawValue(Object raw);
Object handleRaw(ObjectData objectData) throws JsonProtocolParseException {
AtomicReferenceArray<Object> atomicReferenceArray = objectData.getAtomicReferenceArray();
Object cachedValue = fieldBinding.get(atomicReferenceArray);
if (cachedValue != null) {
return cachedValue;
}
Object parsedValue = parse(objectData);
if (parsedValue != null) {
parsedValue = fieldBinding.setAndGet(atomicReferenceArray, parsedValue);
}
return parsedValue;
}
protected abstract Object parse(ObjectData objectData) throws JsonProtocolParseException;
protected VolatileFieldBinding getFieldBinding() {
return fieldBinding;
}
protected abstract void writeReturnTypeJava(ClassScope scope, Method m);
@Override
void writeMethodImplementationJava(ClassScope classScope, Method m) {
classScope.startLine("@Override public ");
writeReturnTypeJava(classScope, m);
classScope.append(" ");
appendMethodSignatureJava(classScope, m, Collections.<String>emptyList());
{
Type[] exceptions = m.getGenericExceptionTypes();
if (exceptions.length > 0) {
classScope.append(" throws ");
for (int i = 0; i < exceptions.length; i++) {
if (i != 0) {
classScope.append(", ");
}
Util.writeJavaTypeName(exceptions[i], classScope.getStringBuilder());
}
}
}
MethodScope scope = classScope.newMethodScope();
scope.append(" {\n");
scope.indentRight();
classScope.startLine("");
writeReturnTypeJava(classScope, m);
scope.append(" result = ");
getFieldBinding().writeGetExpressionJava(scope.getStringBuilder());
scope.append(";\n");
scope.startLine("if (result != null) {\n");
scope.startLine(" return result;\n");
scope.startLine("}\n");
String parseResultRef = scope.newMethodScopedName("parseResult");
writeParseJava(scope, parseResultRef);
scope.startLine("if (" + parseResultRef + " != null) {\n");
scope.indentRight();
getFieldBinding().writeSetAndGetJava(scope, parseResultRef, "cachedResult");
scope.startLine(parseResultRef + " = cachedResult;\n");
scope.indentLeft();
scope.startLine("}\n");
scope.startLine("return " + parseResultRef + ";\n");
scope.indentLeft();
scope.startLine("}\n");
}
protected abstract void writeParseJava(MethodScope scope, String parseResultRef);
}
private static class LazyCachedFieldMethodHandler extends LazyCachedMethodHandlerBase
implements LazyHandler {
private final SlowParser<?> slowParser;
private final boolean isOptional;
private final String fieldName;
private final Class<?> typeClass;
LazyCachedFieldMethodHandler(VolatileFieldBinding fieldBinding, SlowParser<?> slowParser,
boolean isOptional, String fieldName, Class<?> typeClass) {
super(fieldBinding);
this.slowParser = slowParser;
this.isOptional = isOptional;
this.fieldName = fieldName;
this.typeClass = typeClass;
}
@Override
public void parseEager(ObjectData objectData) throws JsonProtocolParseException {
parse(objectData);
}
@Override
protected Object parse(ObjectData objectData) throws JsonProtocolParseException {
Map<?,?> properties = (JSONObject)objectData.getUnderlyingObject();
Object value = properties.get(fieldName);
boolean hasValue;
if (value == null) {
hasValue = properties.containsKey(fieldName);
} else {
hasValue = true;
}
Object parsedValue = parse(hasValue, value, objectData);
// Cache already finished value, because we don't use unfinished value anywhere.
FieldLoadedFinisher valueFinisher = slowParser.getValueFinisher();
if (valueFinisher != null) {
parsedValue = valueFinisher.getValueForUser(parsedValue);
}
return parsedValue;
}
@Override
protected Object finishRawValue(Object raw) {
return raw;
}
private Object parse(boolean hasValue, Object value, ObjectData objectData)
throws JsonProtocolParseException {
if (hasValue) {
try {
return slowParser.parseValue(value, objectData);
} catch (JsonProtocolParseException e) {
throw new JsonProtocolParseException("Failed to parse field " + fieldName + " in type " +
typeClass.getName(), e);
}
} else {
if (!isOptional) {
throw new JsonProtocolParseException("Field is not optional: " + fieldName +
" (in type " + typeClass.getName() + ")");
}
return null;
}
}
@Override
protected void writeReturnTypeJava(ClassScope scope, Method m) {
getFieldBinding().getTypeInfo().appendValueTypeNameJava(scope);
}
@Override
protected void writeParseJava(MethodScope scope, String parseResultRef) {
scope.startLine("");
getFieldBinding().getTypeInfo().appendValueTypeNameJava(scope);
scope.append(" " + parseResultRef + ";\n");
boolean wrap = slowParser.javaCodeThrowsException() || !isOptional;
if (wrap) {
scope.startLine("try {\n");
scope.indentRight();
}
String valueRef = scope.newMethodScopedName("value");
String hasValueRef = scope.newMethodScopedName("hasValue");
Util.writeReadValueAndHasValue(scope, fieldName, "underlying", valueRef, hasValueRef);
scope.startLine("if (" + hasValueRef + ") {\n");
scope.indentRight();
if (slowParser.javaCodeThrowsException()) {
scope.startLine("try {\n");
scope.indentRight();
slowParser.writeParseCode(scope, valueRef, "null", "r1");
scope.startLine(parseResultRef + " = r1;\n");
scope.indentLeft();
scope.startLine("} catch (" + Util.BASE_PACKAGE + ".JsonProtocolParseException e) {\n");
scope.startLine(" throw new " + Util.BASE_PACKAGE + ".JsonProtocolParseException(" +
"\"Failed to parse field " + fieldName + " in type ");
scope.append(typeClass.getName() + "\", e);\n");
scope.startLine("}\n");
} else {
slowParser.writeParseCode(scope, valueRef, "null", "r1");
scope.startLine(parseResultRef + " = r1;\n");
}
scope.indentLeft();
scope.startLine("} else {\n");
scope.indentRight();
if (isOptional) {
scope.startLine(parseResultRef + " = null;\n");
} else {
scope.startLine("throw new " + Util.BASE_PACKAGE + ".JsonProtocolParseException(" +
"\"Field is not optional: " + fieldName + "\");\n");
}
scope.indentLeft();
scope.startLine("}\n");
if (wrap) {
scope.indentLeft();
scope.startLine("} catch (" + Util.BASE_PACKAGE + ".JsonProtocolParseException e) {\n");
scope.startLine(" throw new " + Util.BASE_PACKAGE +
".implutil.CommonImpl.ParseRuntimeException(" +
"\"On demand parsing failed for \" + underlying, e);\n");
scope.startLine("}\n");
}
}
@Override
public String getFieldName() {
return fieldName;
}
}
private static class PreparsedFieldMethodHandler extends MethodHandler {
private final int pos;
private final FieldLoadedFinisher valueFinisher;
private final String fieldName;
PreparsedFieldMethodHandler(int pos, FieldLoadedFinisher valueFinisher, String fieldName) {
this.pos = pos;
this.valueFinisher = valueFinisher;
this.fieldName = fieldName;
}
@Override
Object handle(ObjectData objectData, Object[] args) throws Throwable {
Object val = objectData.getFieldArray()[pos];
if (valueFinisher != null) {
val = valueFinisher.getValueForUser(val);
}
return val;
}
@Override
void writeMethodImplementationJava(ClassScope scope, Method m) {
writeMethodDeclarationJava(scope, m, Collections.<String>emptyList());
scope.append(" {\n");
scope.startLine(" return field_" + fieldName + ";\n");
scope.startLine("}\n");
}
}
static SlowParser<Void> VOID_PARSER = new QuickParser<Void>() {
@Override
public Void parseValueQuick(Object value) {
return null;
}
@Override
public void appendFinishedValueTypeNameJava(FileScope scope) {
scope.append("Void");
}
@Override
void writeParseQuickCode(MethodScope scope, String valueRef, String resultRef) {
scope.startLine("Void " + resultRef + " = null;\n");
}
@Override
boolean javaCodeThrowsException() {
return false;
}
};
static class SimpleCastParser<T> extends QuickParser<T> {
private final boolean nullable;
private final Class<T> fieldType;
SimpleCastParser(Class<T> fieldType, boolean nullable) {
this.fieldType = fieldType;
this.nullable = nullable;
}
@Override
public T parseValueQuick(Object value) throws JsonProtocolParseException {
if (value == null) {
if (nullable) {
return null;
} else {
throw new JsonProtocolParseException("Field must have type " + fieldType.getName());
}
}
try {
return fieldType.cast(value);
} catch (ClassCastException e) {
throw new JsonProtocolParseException("Field must have type " + fieldType.getName(), e);
}
}
@Override
public FieldLoadedFinisher getValueFinisher() {
return null;
}
@Override
public void appendFinishedValueTypeNameJava(FileScope scope) {
scope.append(fieldType.getCanonicalName());
}
@Override
public void writeParseQuickCode(MethodScope scope, String valueRef,
String resultRef) {
scope.startLine(fieldType.getCanonicalName() + " " + resultRef + " = (" +
fieldType.getCanonicalName() + ") " + valueRef + ";\n");
}
@Override
boolean javaCodeThrowsException() {
return false;
}
}
static class SimpleParserPair<T> {
static <T> SimpleParserPair<T> create(Class<T> fieldType) {
return new SimpleParserPair<T>(fieldType);
}
private final SimpleCastParser<T> nullable;
private final SimpleCastParser<T> notNullable;
private SimpleParserPair(Class<T> fieldType) {
nullable = new SimpleCastParser<T>(fieldType, true);
notNullable = new SimpleCastParser<T>(fieldType, false);
}
SimpleCastParser<T> getNullable() {
return nullable;
}
SimpleCastParser<T> getNotNullable() {
return notNullable;
}
SlowParser<?> get(boolean declaredNullable) {
return declaredNullable ? nullable : notNullable;
}
}
private static final SimpleParserPair<Long> LONG_PARSER =
SimpleParserPair.create(Long.class);
private static final SimpleParserPair<Boolean> BOOLEAN_PARSER =
SimpleParserPair.create(Boolean.class);
private static final SimpleParserPair<Float> FLOAT_PARSER =
SimpleParserPair.create(Float.class);
private static final SimpleParserPair<Number> NUMBER_PARSER =
SimpleParserPair.create(Number.class);
private static final SimpleParserPair<String> STRING_PARSER =
SimpleParserPair.create(String.class);
private static final SimpleParserPair<Object> OBJECT_PARSER =
SimpleParserPair.create(Object.class);
private static final SimpleParserPair<JSONObject> JSON_PARSER =
SimpleParserPair.create(JSONObject.class);
static class ArrayParser<T> extends SlowParser<List<? extends T>> {
static abstract class ListFactory {
abstract <T> List<T> create(JSONArray array, SlowParser<T> componentParser)
throws JsonProtocolParseException;
abstract void writeCreateListCode(SlowParser<?> componentParser, MethodScope scope,
String inputRef, String resultRef);
}
static final ListFactory EAGER = new ListFactory() {
@Override
<T> List<T> create(JSONArray array, SlowParser<T> componentParser)
throws JsonProtocolParseException {
int size = array.size();
List list = new ArrayList<Object>(size);
FieldLoadedFinisher valueFinisher = componentParser.getValueFinisher();
for (int i = 0; i < size; i++) {
// We do not support super object for array component.
Object val = componentParser.parseValue(array.get(i), null);
if (valueFinisher != null) {
val = valueFinisher.getValueForUser(val);
}
list.add(val);
}
return Collections.unmodifiableList(list);
}
@Override
void writeCreateListCode(SlowParser<?> componentParser, MethodScope scope, String inputRef,
String resultRef) {
String sizeRef = scope.newMethodScopedName("size");
String listRef = scope.newMethodScopedName("list");
String indexRef = scope.newMethodScopedName("index");
String componentRef = scope.newMethodScopedName("arrayComponent");
scope.startLine("int " + sizeRef + " = " + inputRef + ".size();\n");
scope.startLine("java.util.List<");
componentParser.appendFinishedValueTypeNameJava(scope);
scope.append("> " + listRef + " = new java.util.ArrayList<");
componentParser.appendFinishedValueTypeNameJava(scope);
scope.append(">(" + sizeRef + ");\n");
scope.startLine("for (int " + indexRef + " = 0; " + indexRef + " < " + sizeRef + "; " +
indexRef + "++) {\n");
scope.indentRight();
componentParser.writeParseCode(scope, inputRef + ".get(" + indexRef + ")", "null",
componentRef);
scope.startLine(listRef + ".add(" + componentRef + ");\n");
scope.indentLeft();
scope.startLine("}\n");
scope.startLine("java.util.List<");
componentParser.appendFinishedValueTypeNameJava(scope);
scope.append("> " + resultRef + " = java.util.Collections.unmodifiableList(" +
listRef + ");\n");
}
};
static final ListFactory LAZY = new ListFactory() {
@Override
<T> List<T> create(final JSONArray array, final SlowParser<T> componentParser) {
final int size = array.size();
List<T> list = new AbstractList<T>() {
private final AtomicReferenceArray<T> values = new AtomicReferenceArray<T>(size);
@Override
public synchronized T get(int index) {
T parsedValue = values.get(index);
if (parsedValue == null) {
Object rawObject = array.get(index);
if (rawObject != null) {
Object parsedObject;
try {
parsedObject = componentParser.parseValue(array.get(index), null);
} catch (JsonProtocolParseException e) {
throw new ParseRuntimeException(e);
}
FieldLoadedFinisher valueFinisher = componentParser.getValueFinisher();
if (valueFinisher != null) {
parsedObject = valueFinisher.getValueForUser(parsedObject);
}
parsedValue = (T) parsedObject;
values.compareAndSet(index, null, parsedValue);
parsedValue = values.get(index);
}
}
return parsedValue;
}
@Override
public int size() {
return size;
}
};
return list;
}
@Override
void writeCreateListCode(SlowParser<?> componentParser, MethodScope scope, String inputRef,
String resultRef) {
String sizeRef = scope.newMethodScopedName("size");
scope.startLine("final int " + sizeRef + " = " + inputRef + ".size();\n");
scope.startLine("java.util.List<");
componentParser.appendFinishedValueTypeNameJava(scope);
scope.append("> " + resultRef + " = new java.util.AbstractList<");
componentParser.appendFinishedValueTypeNameJava(scope);
scope.append(">() {\n");
scope.indentRight();
scope.startLine("private final java.util.concurrent.atomic.AtomicReferenceArray<");
componentParser.appendFinishedValueTypeNameJava(scope);
scope.append("> cachedValues = new java.util.concurrent.atomic.AtomicReferenceArray<");
componentParser.appendFinishedValueTypeNameJava(scope);
scope.append(">(" + sizeRef + ");\n");
scope.append("\n");
scope.startLine("@Override public int size() { return " + sizeRef + "; }\n");
scope.append("\n");
writeGetMethodCode(componentParser, scope, inputRef);
scope.indentLeft();
scope.startLine("};\n");
}
private void writeGetMethodCode(SlowParser<?> componentParser, MethodScope outerMethodScope,
String arrayRef) {
outerMethodScope.startLine("@Override public ");
componentParser.appendFinishedValueTypeNameJava(outerMethodScope);
outerMethodScope.append(" get(int index) {\n");
{
MethodScope scope = outerMethodScope.newMethodScope();
scope.indentRight();
String resultRef = scope.newMethodScopedName("result");
scope.startLine("");
componentParser.appendFinishedValueTypeNameJava(scope);
scope.append(" " + resultRef + " = cachedValues.get(index);\n");
scope.startLine("if (" + resultRef + " == null) {\n");
scope.indentRight();
boolean wrap = componentParser.javaCodeThrowsException();
if (wrap) {
scope.startLine("try {\n");
scope.indentRight();
}
scope.startLine("Object unparsed = " + arrayRef + ".get(index);\n");
componentParser.writeParseCode(scope, "unparsed", "null", "parsed");
scope.startLine(resultRef + " = parsed;\n");
if (wrap) {
scope.indentLeft();
scope.startLine("} catch (" + Util.BASE_PACKAGE +
".JsonProtocolParseException e) {\n");
scope.startLine(" throw new " + Util.BASE_PACKAGE +
".implutil.CommonImpl.ParseRuntimeException(e);\n");
scope.startLine("}\n");
}
scope.startLine("cachedValues.compareAndSet(index, null, " + resultRef + ");\n");
scope.startLine(resultRef + " = cachedValues.get(index);\n");
scope.indentLeft();
scope.startLine("}\n");
scope.startLine("return " + resultRef + ";\n");
scope.indentLeft();
}
outerMethodScope.startLine("}\n");
}
};
private final SlowParser<T> componentParser;
private final boolean isNullable;
private final ListFactory listFactory;
ArrayParser(SlowParser<T> componentParser, boolean isNullable, ListFactory listFactory) {
this.componentParser = componentParser;
this.isNullable = isNullable;
this.listFactory = listFactory;
}
@Override
public List<? extends T> parseValue(Object value, ObjectData thisData)
throws JsonProtocolParseException {
if (isNullable && value == null) {
return null;
}
if (value instanceof JSONArray == false) {
throw new JsonProtocolParseException("Array value expected");
}
JSONArray arrayValue = (JSONArray) value;
return listFactory.create(arrayValue, componentParser);
}
@Override
public FieldLoadedFinisher getValueFinisher() {
return null;
}
@Override
public JsonTypeParser<?> asJsonTypeParser() {
return null;
}
@Override
public void appendFinishedValueTypeNameJava(FileScope scope) {
scope.append("java.util.List<");
componentParser.appendFinishedValueTypeNameJava(scope);
scope.append(">");
}
@Override
public void appendInternalValueTypeNameJava(FileScope scope) {
appendFinishedValueTypeNameJava(scope);
}
@Override
void writeParseCode(MethodScope scope, String valueRef,
String superValueRef, String resultRef) {
if (isNullable) {
scope.startLine("if (" + valueRef + " == null) {\n");
scope.startLine(" return null;\n");
scope.startLine("}\n");
}
scope.startLine("if (" + valueRef + " instanceof org.json.simple.JSONArray == false) {\n");
scope.startLine(" throw new " + Util.BASE_PACKAGE +
".JsonProtocolParseException(\"Array value expected\");\n");
scope.startLine("}\n");
String arrayValueRef = scope.newMethodScopedName("arrayValue");
scope.startLine("final org.json.simple.JSONArray " + arrayValueRef +
" = (org.json.simple.JSONArray) " + valueRef + ";\n");
listFactory.writeCreateListCode(componentParser, scope, arrayValueRef, resultRef);
}
@Override
boolean javaCodeThrowsException() {
return true;
}
}
static MethodHandler RETURN_NULL_METHOD_HANDLER = new MethodHandler() {
@Override
Object handle(ObjectData objectData, Object[] args) throws Throwable {
return null;
}
@Override
void writeMethodImplementationJava(ClassScope scope, Method m) {
writeMethodDeclarationJava(scope, m, Collections.<String>emptyList());
scope.append(" {\n");
scope.startLine("}\n");
}
};
static class AutoSubtypeMethodHandler extends MethodHandler {
private final int variantCodeField;
private final int variantValueField;
private final int code;
AutoSubtypeMethodHandler(int variantCodeField, int variantValueField, int code) {
this.variantCodeField = variantCodeField;
this.variantValueField = variantValueField;
this.code = code;
}
ObjectData getFieldObjectData(ObjectData objectData) {
Object[] array = objectData.getFieldArray();
Integer actualCode = (Integer) array[variantCodeField];
if (this.code == actualCode) {
ObjectData data = (ObjectData) array[variantValueField];
return data;
} else {
return null;
}
}
@Override
Object handle(ObjectData objectData, Object[] args) {
ObjectData resData = getFieldObjectData(objectData);
if (resData == null) {
return null;
} else {
return resData.getProxy();
}
}
@Override
void writeMethodImplementationJava(ClassScope scope, Method m) {
writeMethodDeclarationJava(scope, m, Collections.<String>emptyList());
scope.append(" {\n");
scope.startLine(" return " + AutoAlgebraicCasesDataImpl.getAutoAlgFieldNameJava(code) +
";\n");
scope.startLine("}\n");
}
}
static class ManualSubtypeMethodHandler extends LazyCachedMethodHandlerBase {
private final SlowParser<?> parser;
ManualSubtypeMethodHandler(VolatileFieldBinding fieldInf, SlowParser<?> parser) {
super(fieldInf);
this.parser = parser;
}
@Override
protected Object parse(ObjectData objectData) throws JsonProtocolParseException {
return parser.parseValue(objectData.getUnderlyingObject(), objectData);
}
@Override
protected Object finishRawValue(Object raw) {
FieldLoadedFinisher valueFinisher = parser.getValueFinisher();
Object res = raw;
if (valueFinisher != null) {
res = valueFinisher.getValueForUser(res);
}
return res;
}
ObjectData getSubtypeData(ObjectData objectData) throws JsonProtocolParseException {
return (ObjectData) handleRaw(objectData);
}
@Override
protected void writeReturnTypeJava(ClassScope scope, Method m) {
JsonTypeParser<?> jsonTypeParser = parser.asJsonTypeParser();
if (jsonTypeParser == null) {
JavaCodeGenerator.Util.writeJavaTypeName(m.getGenericReturnType(),
scope.getStringBuilder());
} else {
String valueTypeName = scope.getTypeImplReference(jsonTypeParser.getType().get());
scope.append(valueTypeName);
}
}
@Override
protected void writeParseJava(MethodScope scope, String parseResultRef) {
parser.writeParseCode(scope, "underlying", "this", parseResultRef);
}
}
static class AutoAlgebraicCasesDataImpl extends TypeHandler.AlgebraicCasesData {
private int variantCodeFieldPos = -1;
private int variantValueFieldPos = -1;
private boolean hasDefaultCase = false;
private final List<RefToType<?>> subtypes = new ArrayList<RefToType<?>>();
@Override
List<RefToType<?>> getSubtypes() {
return subtypes;
}
@Override
void parseObjectSubtype(ObjectData objectData, Map<?, ?> jsonProperties,
Object input) throws JsonProtocolParseException {
if (jsonProperties == null) {
throw new JsonProtocolParseException(
"JSON object input expected for non-manual subtyping");
}
int code = -1;
for (int i = 0; i < this.getSubtypes().size(); i++) {
TypeHandler<?> nextSubtype = this.getSubtypes().get(i).get();
boolean ok = nextSubtype.getSubtypeSupport().checkConditions(jsonProperties);
if (ok) {
if (code == -1) {
code = i;
} else {
throw new JsonProtocolParseException("More than one case match");
}
}
}
if (code == -1) {
if (!this.hasDefaultCase) {
throw new JsonProtocolParseException("Not a singe case matches");
}
} else {
ObjectData fieldData =
this.getSubtypes().get(code).get().parse(input, objectData);
objectData.getFieldArray()[this.variantValueFieldPos] = fieldData;
}
objectData.getFieldArray()[this.variantCodeFieldPos] =
Integer.valueOf(code);
}
@Override
void writeConstructorCodeJava(MethodScope methodScope) {
methodScope.startLine("int code = -1;\n");
for (int i = 0; i < getSubtypes().size(); i++) {
TypeHandler<?> nextSubtype = getSubtypes().get(i).get();
methodScope.startLine("if (" + methodScope.getTypeImplReference(nextSubtype) +
".checkSubtypeConditions(underlying)) {\n");
methodScope.startLine(" if (code == -1) {\n");
methodScope.startLine(" code = " + i + ";\n");
methodScope.startLine(" } else {\n");
methodScope.startLine(" throw new " + Util.BASE_PACKAGE +
".JsonProtocolParseException(\"More than one case match\");\n");
methodScope.startLine(" }\n");
methodScope.startLine("}\n");
}
if (!hasDefaultCase) {
methodScope.startLine("if (code == -1) {\n");
methodScope.startLine(" throw new " + Util.BASE_PACKAGE +
".JsonProtocolParseException(\"Not a singe case matches\");\n");
methodScope.startLine("}\n");
}
for (int i = 0; i < getSubtypes().size(); i++) {
TypeHandler<?> nextSubtype = getSubtypes().get(i).get();
methodScope.startLine(getAutoAlgFieldNameJava(i) + " = (code == " + i + ") ? new " +
methodScope.getTypeImplReference(nextSubtype) + "(underlying, this) : null;\n");
}
}
@Override
void writeFiledsJava(ClassScope classScope) {
for (int i = 0; i < getSubtypes().size(); i++) {
TypeHandler<?> nextSubtype = getSubtypes().get(i).get();
classScope.startLine("private final " + classScope.getTypeImplReference(nextSubtype) +
" " + getAutoAlgFieldNameJava(i) + ";\n");
}
}
private static String getAutoAlgFieldNameJava(int code) {
return "auto_alg_field_" + code;
}
}
static class ManualAlgebraicCasesDataImpl extends TypeHandler.AlgebraicCasesData {
private final List<RefToType<?>> subtypes = new ArrayList<RefToType<?>>();
@Override
List<RefToType<?>> getSubtypes() {
return subtypes;
}
@Override
void parseObjectSubtype(ObjectData objectData, Map<?, ?> jsonProperties, Object input) {
}
@Override
void writeConstructorCodeJava(MethodScope methodScope) {
}
@Override
void writeFiledsJava(ClassScope classScope) {
}
}
static class VolatileFieldBinding {
private final int position;
private final FieldTypeInfo fieldTypeInfo;
public VolatileFieldBinding(int position, FieldTypeInfo fieldTypeInfo) {
this.position = position;
this.fieldTypeInfo = fieldTypeInfo;
}
public Object setAndGet(AtomicReferenceArray<Object> atomicReferenceArray,
Object value) {
atomicReferenceArray.compareAndSet(position, null, value);
return atomicReferenceArray.get(position);
}
public Object get(AtomicReferenceArray<Object> atomicReferenceArray) {
return atomicReferenceArray.get(position);
}
void writeGetExpressionJava(StringBuilder output) {
output.append(getCodeFieldName() + ".get()");
}
void writeSetAndGetJava(MethodScope scope,
String valueRef, String resultRef) {
scope.startLine(getCodeFieldName() + ".compareAndSet(null, " + valueRef + ");\n");
scope.startLine("");
fieldTypeInfo.appendValueTypeNameJava(scope);
scope.append(" " + resultRef + " = ");
writeGetExpressionJava(scope.getStringBuilder());
scope.append(";\n");
}
void writeFieldDeclarationJava(ClassScope scope) {
scope.startLine("private final java.util.concurrent.atomic.AtomicReference<");
fieldTypeInfo.appendValueTypeNameJava(scope);
scope.append(" > " + getCodeFieldName() +
" = new java.util.concurrent.atomic.AtomicReference<");
fieldTypeInfo.appendValueTypeNameJava(scope);
scope.append(">(null);\n");
}
FieldTypeInfo getTypeInfo() {
return fieldTypeInfo;
}
private String getCodeFieldName() {
return FIELD_NAME_PREFIX + position;
}
private static final String FIELD_NAME_PREFIX = "lazyCachedField_";
}
private static class RefImpl<T> extends RefToType<T> {
private final Class<T> typeClass;
private TypeHandler<T> type = null;
RefImpl(Class<T> typeClass) {
this.typeClass = typeClass;
}
@Override
Class<?> getTypeClass() {
return typeClass;
}
@Override
TypeHandler<T> get() {
return type;
}
void set(TypeHandler<?> type) {
this.type = (TypeHandler<T>)type;
}
}
// We should use it for static analysis later.
private static class FieldMap {
final List<String> localNames = new ArrayList<String>(5);
final List<String> overridenNames = new ArrayList<String>(1);
}
public GeneratedCodeMap generateStaticParser(StringBuilder stringBuilder,
String packageName, String className) {
return generateStaticParser(stringBuilder, packageName, className,
Collections.<GeneratedCodeMap>emptyList());
}
public GeneratedCodeMap generateStaticParser(StringBuilder stringBuilder, String packageName,
String className, Collection<GeneratedCodeMap> basePackages) {
JavaCodeGenerator generator = new JavaCodeGenerator.Impl();
GlobalScope globalScope = generator.newGlobalScope(type2TypeHandler.values(), basePackages);
FileScope fileScope = globalScope.newFileScope(stringBuilder);
fileScope.startLine("// This is a generated source.\n");
fileScope.startLine("// See " + this.getClass().getName() + " for details\n");
fileScope.append("\n");
fileScope.startLine("package " + packageName + ";\n");
fileScope.append("\n");
fileScope.startLine("public class " + className + " implements " +
rootImpl.getType().getCanonicalName() + " {\n");
ClassScope rootClassScope = fileScope.newClassScope();
rootClassScope.indentRight();
rootImpl.writeStaticMethodJava(rootClassScope);
for (TypeHandler<?> typeHandler : type2TypeHandler.values()) {
typeHandler.writeStaticClassJava(rootClassScope);
}
rootClassScope.writeClassMembers();
rootClassScope.indentLeft();
rootClassScope.startLine("}\n");
Map<Class<?>, String> type2ImplClassName = new HashMap<Class<?>, String>();
for (TypeHandler<?> typeHandler : type2TypeHandler.values()) {
String shortName = fileScope.getTypeImplShortName(typeHandler);
String fullReference = packageName + "." + className + "." + shortName;
type2ImplClassName.put(typeHandler.getTypeClass(), fullReference);
}
return new GeneratedCodeMap(type2ImplClassName);
}
}