// Copyright (c) 2011 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.reflect.ParameterizedType;
import java.lang.reflect.Type;
import java.lang.reflect.WildcardType;
import java.util.ArrayList;
import java.util.Arrays;
import java.util.Collection;
import java.util.Collections;
import java.util.Comparator;
import java.util.HashMap;
import java.util.List;
import java.util.Map;
/**
* A set of interfaces and classes used to generate Java code of parser implementation.
*/
public interface JavaCodeGenerator {
GlobalScope newGlobalScope(Collection<TypeHandler<?>> typeHandlers,
Collection<GeneratedCodeMap> basePackages);
interface GlobalScope {
String getTypeImplReference(TypeHandler<?> typeHandler);
String getTypeImplShortName(TypeHandler<?> typeHandler);
/**
* @return new {@link FileScope} that extends {@link GlobalScope} and shares the state
* with this {@link GlobalScope}
*/
FileScope newFileScope(StringBuilder output);
}
interface FileScope extends GlobalScope {
StringBuilder getStringBuilder();
void startLine(String line);
void append(String line);
void indentRight();
void indentLeft();
/**
* @return new {@link ClassScope} that extends {@link FileScope} and shares the state
* with this {@link FileScope}
*/
ClassScope newClassScope();
}
interface ClassScope extends FileScope {
ClassScope getRootClassScope();
/**
* @return new {@link ClassScope} that has different state as {@link ClassScope},
* but shares the state with this as {@link FileScope}
*/
@Override
ClassScope newClassScope();
/**
* Adds a member to the class. The member is identified by the key. Member Java code
* is generated later. If the member with a particular key
* has already been added, method return data instance if returned the previous time.
*
* @return user-defined field element data
*/
<T extends ElementData> T addMember(Object key, ElementFactory<T> factory);
/**
* @return new {@link MethodScope} that extends {@link ClassScope} and shares the state
* with this {@link ClassScope}.
*/
MethodScope newMethodScope();
/**
* Writes Java code of all added members.
*/
void writeClassMembers();
}
interface MethodScope extends ClassScope {
/**
* @return a name unique to this scope with the provided prefix
*/
String newMethodScopedName(String prefix);
}
interface ElementData {
void generateCode(ClassScope classScope);
}
interface ElementFactory<T extends ElementData> {
T create(int code);
}
class Util {
/**
* Generate Java type name of the passed type. Type may be parameterized.
*/
public static void writeJavaTypeName(Type arg, StringBuilder output) {
if (arg instanceof Class) {
Class<?> clazz = (Class<?>) arg;
output.append(clazz.getCanonicalName());
} else if (arg instanceof ParameterizedType) {
ParameterizedType parameterizedType = (ParameterizedType) arg;
writeJavaTypeName(parameterizedType.getRawType(), output);
output.append("<");
Type[] params = parameterizedType.getActualTypeArguments();
for (int i = 0; i < params.length; i++) {
if (i != 0) {
output.append(", ");
}
writeJavaTypeName(params[i], output);
}
output.append(">");
} else if (arg instanceof WildcardType) {
WildcardType wildcardType = (WildcardType) arg;
Type[] upperBounds = wildcardType.getUpperBounds();
if (upperBounds == null) {
throw new RuntimeException();
}
if (upperBounds.length != 1) {
throw new RuntimeException();
}
output.append("? extends ");
writeJavaTypeName(upperBounds[0], output);
} else {
output.append(arg);
}
}
/**
* Generates a commonly-used code that gets property from JSON in form of
* 'value' and 'hasValue' pair of variables.
*/
public static void writeReadValueAndHasValue(MethodScope scope, String fieldName,
String jsonObjectRef, String valueRef, String hasValueRef) {
scope.startLine("Object " + valueRef + " = " + jsonObjectRef + ".get(\"" +
fieldName + "\");\n");
scope.startLine("boolean " + hasValueRef + ";\n");
scope.startLine("if (" + valueRef + " == null) {\n");
scope.startLine(" " + hasValueRef + " = " + jsonObjectRef + ".containsKey(\"" +
fieldName + "\");\n");
scope.startLine("} else {\n");
scope.startLine(" " + hasValueRef + " = true;\n");
scope.startLine("}\n");
}
public static final String BASE_PACKAGE = "org.chromium.sdk.internal.protocolparser";
public static final String THROWS_CLAUSE =
" throws org.chromium.sdk.internal.protocolparser.JsonProtocolParseException";
}
class Impl implements JavaCodeGenerator {
@Override
public GlobalScope newGlobalScope(Collection<TypeHandler<?>> typeHandlers,
Collection<GeneratedCodeMap> basePackages) {
return new GlobalScopeImpl(typeHandlers, basePackages);
}
private static class GlobalScopeImpl implements GlobalScope {
private final State state;
GlobalScopeImpl(Collection<TypeHandler<?>> typeHandlers,
Collection<GeneratedCodeMap> basePackages) {
state = new State(typeHandlers, basePackages);
}
GlobalScopeImpl(GlobalScopeImpl globalScopeImpl) {
state = globalScopeImpl.state;
}
@Override
public String getTypeImplReference(TypeHandler<?> typeHandler) {
return state.getTypeImplReference(typeHandler);
}
@Override
public String getTypeImplShortName(TypeHandler<?> typeHandler) {
return state.getTypeImplShortName(typeHandler);
}
@Override
public FileScope newFileScope(StringBuilder output) {
return new FileScopeImpl(this, output);
}
private static class State {
private final Map<TypeHandler<?>, String> type2Name;
private final Collection<GeneratedCodeMap> basePackages;
State(Collection<TypeHandler<?>> typeHandlers, Collection<GeneratedCodeMap> basePackages) {
this.basePackages = basePackages;
type2Name = buildLocalTypeNameMap(typeHandlers);
}
String getTypeImplReference(TypeHandler<?> typeHandler) {
String localName = type2Name.get(typeHandler);
if (localName == null) {
for (GeneratedCodeMap base : basePackages) {
String result = base.getTypeImplementationReference(typeHandler.getTypeClass());
if (result != null) {
return result;
}
}
} else {
return localName;
}
throw new RuntimeException();
}
String getTypeImplShortName(TypeHandler<?> typeHandler) {
String result = type2Name.get(typeHandler);
if (result == null) {
throw new RuntimeException();
}
return result;
}
private static Map<TypeHandler<?>, String> buildLocalTypeNameMap(
Collection<TypeHandler<?>> typeHandlers) {
List<TypeHandler<?>> list = new ArrayList<TypeHandler<?>>(typeHandlers);
// Sort to produce consistent GeneratedCodeMap later.
Collections.sort(list, new Comparator<TypeHandler<?>>() {
@Override
public int compare(TypeHandler<?> o1, TypeHandler<?> o2) {
return getName(o1).compareTo(getName(o2));
}
private String getName(TypeHandler<?> handler) {
return handler.getTypeClass().getName();
}
});
int uniqueCode = 0;
Map<TypeHandler<?>, String> result = new HashMap<TypeHandler<?>, String>();
for (TypeHandler<?> handler : list) {
String name = "Value_" + uniqueCode++;
Object conflict = result.put(handler, name);
if (conflict != null) {
throw new RuntimeException();
}
}
return result;
}
}
}
private static class FileScopeImpl extends GlobalScopeImpl implements FileScope {
private final State state;
FileScopeImpl(GlobalScopeImpl globalScopeImpl, StringBuilder stringBuilder) {
super(globalScopeImpl);
this.state = new State(stringBuilder);
}
FileScopeImpl(FileScopeImpl fileScopeImpl) {
super(fileScopeImpl);
this.state = fileScopeImpl.state;
}
@Override
public StringBuilder getStringBuilder() {
return state.getStringBuilder();
}
@Override
public void startLine(String line) {
state.startLine(line);
}
@Override
public void append(String line) {
state.append(line);
}
@Override
public void indentRight() {
state.indentRight();
}
@Override
public void indentLeft() {
state.indentLeft();
}
@Override
public ClassScope newClassScope() {
return new ClassScopeImpl(this, asClassScopeImpl());
}
protected ClassScopeImpl asClassScopeImpl() {
return null;
}
private static class State {
private final StringBuilder stringBuilder;
private int indent = 0;
State(StringBuilder stringBuilder) {
this.stringBuilder = stringBuilder;
}
StringBuilder getStringBuilder() {
return stringBuilder;
}
void startLine(String line) {
for (int i = 0; i < indent; i++) {
stringBuilder.append(' ');
}
stringBuilder.append(line);
}
void append(String line) {
stringBuilder.append(line);
}
void indentRight() {
indent += 2;
}
void indentLeft() {
indent -= 2;
}
}
}
private static class ClassScopeImpl extends FileScopeImpl implements ClassScope {
private final State state;
private final ClassScope parentClass;
ClassScopeImpl(FileScopeImpl fileScopeImpl, ClassScope parentClass) {
super(fileScopeImpl);
this.state = new State();
this.parentClass = parentClass;
}
ClassScopeImpl(ClassScopeImpl classScopeImpl) {
super(classScopeImpl);
this.state = classScopeImpl.state;
this.parentClass = classScopeImpl.parentClass;
}
@Override
public ClassScope getRootClassScope() {
if (parentClass == null) {
return this;
} else {
return parentClass.getRootClassScope();
}
}
@Override
public <T extends ElementData> T addMember(Object key,
ElementFactory<T> factory) {
return state.addMember(key, factory);
}
@Override
public void writeClassMembers() {
state.writeClassElements(this);
}
@Override
public MethodScope newMethodScope() {
return new MethodScopeImpl(this);
}
@Override
protected ClassScopeImpl asClassScopeImpl() {
return this;
}
private static class State {
private final Map<Object, ElementData> key2ElementData =
new HashMap<Object, JavaCodeGenerator.ElementData>(2);
private int nextCode = 0;
<T extends ElementData> T addMember(Object key, ElementFactory<T> factory) {
List<Object> extendedKey = Arrays.asList(key, factory);
ElementData rawData = key2ElementData.get(extendedKey);
T data = (T) rawData;
if (data == null) {
data = factory.create(nextCode++);
key2ElementData.put(extendedKey, data);
}
return data;
}
void writeClassElements(ClassScope classScope) {
for (ElementData data : key2ElementData.values()) {
data.generateCode(classScope);
}
key2ElementData.clear();
}
}
}
private static class MethodScopeImpl extends ClassScopeImpl implements MethodScope {
private final State state;
public MethodScopeImpl(ClassScopeImpl classScopeImpl) {
super(classScopeImpl);
state = new State();
}
@Override
public String newMethodScopedName(String prefix) {
return state.newMethodScopedName(prefix);
}
private static class State {
private int nextId = 0;
String newMethodScopedName(String prefix) {
return prefix + nextId++;
}
}
}
}
}