Prepare ClassAnalyzer to be replaced by other implementations.

ClassAnalyzer uses normal reflection. We intend to move
to an Annotation Processor and possibly an Android Studio
plugin version of type interaction as well. This abstracts
the type interaction to prepare.

Change-Id: I2b95ea9074bca7e3053aeadcd3692dffe93b41d6
This commit is contained in:
George Mount
2015-02-10 11:02:48 -08:00
parent 30bc4d25dd
commit fbdb3c08f0
35 changed files with 938 additions and 374 deletions

View File

@@ -15,6 +15,7 @@ import java.util.Collections;
import java.util.Enumeration;
import java.util.HashMap;
import java.util.HashSet;
import java.util.List;
import java.util.Set;
import javax.annotation.processing.AbstractProcessor;
@@ -23,11 +24,14 @@ import javax.annotation.processing.SupportedAnnotationTypes;
import javax.annotation.processing.SupportedSourceVersion;
import javax.lang.model.SourceVersion;
import javax.lang.model.element.Element;
import javax.lang.model.element.ElementKind;
import javax.lang.model.element.ExecutableElement;
import javax.lang.model.element.Name;
import javax.lang.model.element.TypeElement;
import javax.lang.model.element.VariableElement;
import javax.lang.model.type.TypeKind;
import javax.lang.model.util.Elements;
import javax.lang.model.util.Types;
import javax.tools.Diagnostic;
import javax.tools.FileObject;
import javax.tools.JavaFileObject;

View File

@@ -0,0 +1,43 @@
package com.android.databinding.annotationprocessor;
import android.binding.Bindable;
import java.io.File;
import java.io.IOException;
import java.io.InputStream;
import java.io.ObjectInputStream;
import java.io.ObjectOutputStream;
import java.io.Serializable;
import java.io.Writer;
import java.net.URL;
import java.util.ArrayList;
import java.util.Collections;
import java.util.Enumeration;
import java.util.HashMap;
import java.util.HashSet;
import java.util.Set;
import javax.annotation.processing.AbstractProcessor;
import javax.annotation.processing.ProcessingEnvironment;
import javax.annotation.processing.RoundEnvironment;
import javax.annotation.processing.SupportedAnnotationTypes;
import javax.annotation.processing.SupportedSourceVersion;
import javax.lang.model.SourceVersion;
import javax.lang.model.element.Element;
import javax.lang.model.element.ExecutableElement;
import javax.lang.model.element.Name;
import javax.lang.model.element.TypeElement;
import javax.lang.model.element.VariableElement;
import javax.lang.model.type.TypeKind;
import javax.tools.Diagnostic;
import javax.tools.FileObject;
import javax.tools.JavaFileObject;
import javax.tools.StandardLocation;
public class ProcessExpressions {
public static boolean process(ProcessingEnvironment processingEnv,
Set<? extends TypeElement> annotations, RoundEnvironment roundEnv) {
return true;
}
}

View File

@@ -16,6 +16,8 @@
package com.android.databinding;
import com.android.databinding.reflection.ReflectionAnalyzer;
import com.android.databinding.reflection.ReflectionClass;
import com.android.databinding.store.SetterStore;
import com.android.databinding.expr.Expr;
@@ -37,15 +39,15 @@ public class Binding {
}
public String toJavaCode(String targetViewName, String expressionCode) {
Class viewType = mTarget.getResolvedType();
return SetterStore.get(ClassAnalyzer.getInstance()).getSetterCall(mName, viewType,
ReflectionClass viewType = mTarget.getResolvedType();
return SetterStore.get(ReflectionAnalyzer.getInstance()).getSetterCall(mName, viewType,
mExpr.getResolvedType(), targetViewName, expressionCode);
}
// private String resolveJavaCode(ClassAnalyzer classAnalyzer) {
// private String resolveJavaCode(ReflectionAnalyzer reflectionAnalyzer) {
//
// }
//// return classAnalyzer.findMethod(mTarget.getResolvedType(), mName,
//// return reflectionAnalyzer.findMethod(mTarget.getResolvedType(), mName,
//// Arrays.asList(mExpr.getResolvedType()));
// //}
//

View File

@@ -18,8 +18,8 @@ package com.android.databinding;
import com.android.databinding.expr.Expr;
import com.android.databinding.expr.ExprModel;
import org.w3c.dom.Node;
import com.android.databinding.reflection.ReflectionAnalyzer;
import com.android.databinding.reflection.ReflectionClass;
import java.util.ArrayList;
import java.util.List;
@@ -29,7 +29,7 @@ public class BindingTarget {
String mViewClass;
List<Binding> mBindings = new ArrayList<>();
ExprModel mModel;
Class mResolvedClass;
ReflectionClass mResolvedClass;
String mIncludedLayout;
// if this target presents itself in multiple layout files with different view types,
// it receives an interface type and should use it in the getter instead.
@@ -68,9 +68,9 @@ public class BindingTarget {
return mViewClass;
}
public Class getResolvedType() {
public ReflectionClass getResolvedType() {
if (mResolvedClass == null) {
mResolvedClass = ClassAnalyzer.getInstance().findClass(mViewClass);
mResolvedClass = ReflectionAnalyzer.getInstance().findClass(mViewClass);
}
return mResolvedClass;
}

View File

@@ -20,6 +20,7 @@ import com.google.common.base.Preconditions;
import com.android.databinding.expr.Expr;
import com.android.databinding.expr.ExprModel;
import com.android.databinding.reflection.ReflectionAnalyzer;
import org.antlr.v4.runtime.misc.NotNull;
import org.antlr.v4.runtime.tree.ParseTree;
@@ -78,32 +79,38 @@ public class ExpressionVisitor extends BindingExpressionBaseVisitor<Expr> {
@Override
public Expr visitQuestionQuestionOp(@NotNull BindingExpressionParser.QuestionQuestionOpContext ctx) {
final Expr left = ctx.left.accept(this);
return mModel.ternary(
mModel.comparison("==", left, mModel.symbol("null", Object.class)),
return mModel.ternary(mModel.comparison("==", left, mModel.symbol("null", Object.class)),
left, ctx.right.accept(this));
}
@Override
public Expr visitTerminal(@NotNull TerminalNode node) {
final int type = node.getSymbol().getType();
Class classType;
switch (type) {
case BindingExpressionParser.IntegerLiteral:
return mModel.symbol(node.getText(), Integer.class);
classType = int.class;
break;
case BindingExpressionParser.FloatingPointLiteral:
return mModel.symbol(node.getText(), Float.class);
classType = float.class;
break;
case BindingExpressionParser.BooleanLiteral:
return mModel.symbol(node.getText(), Boolean.class);
classType = boolean.class;
break;
case BindingExpressionParser.CharacterLiteral:
return mModel.symbol(node.getText(), Character.class);
classType = char.class;
break;
case BindingExpressionParser.SingleQuoteString:
return mModel.symbol(node.getText(), String.class);
case BindingExpressionParser.DoubleQuoteString:
return mModel.symbol(node.getText(), String.class);
classType = String.class;
break;
case BindingExpressionParser.NullLiteral:
return mModel.symbol(node.getText(), Object.class);
classType = Object.class;
break;
default:
throw new RuntimeException("cannot create expression from terminal node " + node.toString());
}
return mModel.symbol(node.getText(), classType);
}
@Override
@@ -234,8 +241,8 @@ public class ExpressionVisitor extends BindingExpressionBaseVisitor<Expr> {
// @org.jetbrains.annotations.NotNull
// @Override
// public Class<? extends Object> resolveValueType(
// @org.jetbrains.annotations.NotNull ClassAnalyzer classAnalyzer) {
// return classAnalyzer.commonParentOf(aggregate.getResolvedClass(), nextResult.getResolvedClass());
// @org.jetbrains.annotations.NotNull ReflectionAnalyzer reflectionAnalyzer) {
// return reflectionAnalyzer.commonParentOf(aggregate.getResolvedClass(), nextResult.getResolvedClass());
// }
//
// @org.jetbrains.annotations.NotNull

View File

@@ -16,7 +16,9 @@
package com.android.databinding.expr;
import com.android.databinding.ClassAnalyzer;
import com.android.databinding.reflection.ClassClass;
import com.android.databinding.reflection.ReflectionAnalyzer;
import com.android.databinding.reflection.ReflectionClass;
import java.util.List;
import java.util.Map;
@@ -36,24 +38,20 @@ public class BracketExpr extends Expr {
}
@Override
protected Class resolveType(ClassAnalyzer classAnalyzer) {
Class<?> targetType = getTarget().resolveType(classAnalyzer);
protected ReflectionClass resolveType(ReflectionAnalyzer reflectionAnalyzer) {
ReflectionClass targetType = getTarget().resolveType(reflectionAnalyzer);
if (targetType.isArray()) {
mAccessor = BracketAccessor.ARRAY;
} else if (List.class.isAssignableFrom(targetType)) {
} else if (targetType.isList()) {
mAccessor = BracketAccessor.LIST;
} else if (Map.class.isAssignableFrom(targetType)) {
} else if (targetType.isMap()) {
mAccessor = BracketAccessor.MAP;
} else {
throw new IllegalArgumentException("Cannot determine variable type used in [] " +
"expression. Cast the value to List, ObservableList, Map, " +
"Cursor, or array.");
}
if (targetType.isArray()) {
return targetType.getComponentType();
} else {
return Object.class;
}
return targetType.getComponentType();
}
@Override

View File

@@ -16,7 +16,8 @@
package com.android.databinding.expr;
import com.android.databinding.ClassAnalyzer;
import com.android.databinding.reflection.ReflectionAnalyzer;
import com.android.databinding.reflection.ReflectionClass;
import java.util.List;
@@ -33,8 +34,8 @@ public class ComparisonExpr extends Expr {
}
@Override
protected Class resolveType(ClassAnalyzer classAnalyzer) {
return boolean.class;
protected ReflectionClass resolveType(ReflectionAnalyzer reflectionAnalyzer) {
return reflectionAnalyzer.loadPrimitive("boolean");
}
@Override

View File

@@ -28,8 +28,8 @@ import com.google.common.base.Predicate;
import com.google.common.collect.Iterables;
import com.google.common.collect.Lists;
import com.android.databinding.ClassAnalyzer;
import com.android.databinding.util.L;
import com.android.databinding.reflection.ReflectionAnalyzer;
import com.android.databinding.reflection.ReflectionClass;
abstract public class Expr {
@@ -41,7 +41,7 @@ abstract public class Expr {
private Boolean mIsDynamic;
private Class mResolvedType;
private ReflectionClass mResolvedType;
private String mUniqueKey;
@@ -145,7 +145,7 @@ abstract public class Expr {
}
public boolean isObservable() {
return ClassAnalyzer.getInstance().isObservable(getResolvedType());
return ReflectionAnalyzer.getInstance().isObservable(getResolvedType());
}
public BitSet getShouldReadFlags() {
@@ -278,15 +278,15 @@ abstract public class Expr {
}
public Class getResolvedType() {
public ReflectionClass getResolvedType() {
if (mResolvedType == null) {
// TODO not get instance
mResolvedType = resolveType(ClassAnalyzer.getInstance());
mResolvedType = resolveType(ReflectionAnalyzer.getInstance());
}
return mResolvedType;
}
abstract protected Class resolveType(ClassAnalyzer classAnalyzer);
abstract protected ReflectionClass resolveType(ReflectionAnalyzer reflectionAnalyzer);
abstract protected List<Dependency> constructDependencies();
@@ -535,7 +535,7 @@ abstract public class Expr {
}
public String getDefaultValue() {
return ClassAnalyzer.getInstance().getDefaultValue(getResolvedType().getSimpleName());
return ReflectionAnalyzer.getInstance().getDefaultValue(getResolvedType().toJavaCode());
}
protected BitSet getPredicateInvalidFlags() {
@@ -569,7 +569,7 @@ abstract public class Expr {
return mIsUsed;
}
public void updateExpr(ClassAnalyzer classAnalyzer) {
public void updateExpr(ReflectionAnalyzer reflectionAnalyzer) {
}
static class Node {

View File

@@ -21,7 +21,8 @@ import com.google.common.base.Predicate;
import com.google.common.collect.Iterables;
import com.google.common.collect.Lists;
import com.android.databinding.ClassAnalyzer;
import com.android.databinding.reflection.ReflectionAnalyzer;
import com.android.databinding.reflection.ReflectionClass;
import com.android.databinding.util.L;
import com.android.databinding.writer.FlagSet;
@@ -191,7 +192,7 @@ public class ExprModel {
public void seal() {
List<Expr> notifiableExpressions = new ArrayList<>();
//ensure class analyzer. We need to know observables at this point
final ClassAnalyzer classAnalyzer = ClassAnalyzer.getInstance();
final ReflectionAnalyzer reflectionAnalyzer = ReflectionAnalyzer.getInstance();
ArrayList<Expr> processedExprs = new ArrayList<>();
ArrayList<Expr> exprs = new ArrayList<>();
@@ -200,13 +201,13 @@ public class ExprModel {
exprs.addAll(mExprMap.values());
exprs.removeAll(processedExprs);
for (Expr expr: exprs) {
expr.updateExpr(classAnalyzer);
expr.updateExpr(reflectionAnalyzer);
}
processedExprs.addAll(exprs);
} while (!exprs.isEmpty());
int counter = 0;
final Iterable<Expr> observables = filterObservables(classAnalyzer);
final Iterable<Expr> observables = filterObservables(reflectionAnalyzer);
List<String> flagMapping = Lists.newArrayList();
mObservables = Lists.newArrayList();
for (Expr expr : observables) {
@@ -219,7 +220,7 @@ public class ExprModel {
}
// non-observable identifiers gets next ids
final Iterable<Expr> nonObservableIds = filterNonObservableIds(classAnalyzer);
final Iterable<Expr> nonObservableIds = filterNonObservableIds(reflectionAnalyzer);
for (Expr expr : nonObservableIds) {
flagMapping.add(expr.getUniqueKey());
expr.setId(counter++);
@@ -336,23 +337,23 @@ public class ExprModel {
return mFlagMapping[id];
}
private Iterable<Expr> filterNonObservableIds(final ClassAnalyzer classAnalyzer) {
private Iterable<Expr> filterNonObservableIds(final ReflectionAnalyzer reflectionAnalyzer) {
return Iterables.filter(mExprMap.values(), new Predicate<Expr>() {
@Override
public boolean apply(Expr input) {
return input instanceof IdentifierExpr
&& !input.hasId()
&& !classAnalyzer.isObservable(input.getResolvedType())
&& !reflectionAnalyzer.isObservable(input.getResolvedType())
&& input.isDynamic();
}
});
}
private Iterable<Expr> filterObservables(final ClassAnalyzer classAnalyzer) {
private Iterable<Expr> filterObservables(final ReflectionAnalyzer reflectionAnalyzer) {
return Iterables.filter(mExprMap.values(), new Predicate<Expr>() {
@Override
public boolean apply(Expr input) {
return classAnalyzer.isObservable(input.getResolvedType());
return reflectionAnalyzer.isObservable(input.getResolvedType());
}
});
}

View File

@@ -16,18 +16,16 @@
package com.android.databinding.expr;
import com.android.databinding.ClassAnalyzer;
import com.android.databinding.util.L;
import com.android.databinding.reflection.ReflectionAnalyzer;
import com.android.databinding.reflection.Callable;
import com.android.databinding.reflection.ReflectionClass;
import java.lang.reflect.Field;
import java.lang.reflect.Method;
import java.util.ArrayList;
import java.util.Collections;
import java.util.List;
public class FieldAccessExpr extends Expr {
String mName;
ClassAnalyzer.Callable mGetter;
Callable mGetter;
final boolean mIsObservableField;
FieldAccessExpr(Expr parent, String name) {
@@ -46,7 +44,7 @@ public class FieldAccessExpr extends Expr {
return getChildren().get(0);
}
public ClassAnalyzer.Callable getGetter() {
public Callable getGetter() {
if (mGetter == null) {
getResolvedType();
}
@@ -62,7 +60,7 @@ public class FieldAccessExpr extends Expr {
getResolvedType();
}
// maybe this is just a final field in which case cannot be notified as changed
return mGetter.type != ClassAnalyzer.Callable.Type.FIELD || mGetter.isDynamic;
return mGetter.type != Callable.Type.FIELD || mGetter.isDynamic;
}
@Override
@@ -89,10 +87,10 @@ public class FieldAccessExpr extends Expr {
}
@Override
public void updateExpr(ClassAnalyzer classAnalyzer) {
public void updateExpr(ReflectionAnalyzer reflectionAnalyzer) {
if (mGetter == null) {
mGetter = classAnalyzer.findMethodOrField(mChildren.get(0).getResolvedType(), mName);
if (classAnalyzer.isObservableField(mGetter.resolvedType)) {
mGetter = reflectionAnalyzer.findMethodOrField(mChildren.get(0).getResolvedType(), mName);
if (reflectionAnalyzer.isObservableField(mGetter.resolvedType)) {
// Make this the ".get()" and add an extra field access for the observable field
Expr parent = getParent();
parent.getParents().remove(this);
@@ -103,16 +101,16 @@ public class FieldAccessExpr extends Expr {
getChildren().add(observableField);
observableField.getParents().add(this);
mGetter = classAnalyzer.findMethodOrField(mGetter.resolvedType, "get");
mGetter = reflectionAnalyzer.findMethodOrField(mGetter.resolvedType, "get");
mName = "";
}
}
}
@Override
protected Class resolveType(ClassAnalyzer classAnalyzer) {
protected ReflectionClass resolveType(ReflectionAnalyzer reflectionAnalyzer) {
if (mGetter == null) {
mGetter = classAnalyzer.findMethodOrField(mChildren.get(0).getResolvedType(), mName);
mGetter = reflectionAnalyzer.findMethodOrField(mChildren.get(0).getResolvedType(), mName);
}
return mGetter.resolvedType;
}

View File

@@ -16,7 +16,8 @@
package com.android.databinding.expr;
import com.android.databinding.ClassAnalyzer;
import com.android.databinding.reflection.ReflectionAnalyzer;
import com.android.databinding.reflection.ReflectionClass;
import java.util.List;
@@ -26,8 +27,8 @@ public class GroupExpr extends Expr {
}
@Override
protected Class resolveType(ClassAnalyzer classAnalyzer) {
return getWrapped().resolveType(classAnalyzer);
protected ReflectionClass resolveType(ReflectionAnalyzer reflectionAnalyzer) {
return getWrapped().resolveType(reflectionAnalyzer);
}
@Override

View File

@@ -19,7 +19,8 @@ package com.android.databinding.expr;
import com.google.common.base.Preconditions;
import com.google.common.collect.Lists;
import com.android.databinding.ClassAnalyzer;
import com.android.databinding.reflection.ReflectionAnalyzer;
import com.android.databinding.reflection.ReflectionClass;
import java.util.List;
@@ -57,10 +58,10 @@ public class IdentifierExpr extends Expr {
}
@Override
protected Class resolveType(final ClassAnalyzer classAnalyzer) {
protected ReflectionClass resolveType(final ReflectionAnalyzer reflectionAnalyzer) {
Preconditions.checkNotNull(mUserDefinedType,
"Identifiers must have user defined types from the XML file. %s is missing it", mName);
return classAnalyzer.findClass(mUserDefinedType);
return reflectionAnalyzer.findClass(mUserDefinedType);
}
@Override

View File

@@ -16,7 +16,8 @@
package com.android.databinding.expr;
import com.android.databinding.ClassAnalyzer;
import com.android.databinding.reflection.ReflectionAnalyzer;
import com.android.databinding.reflection.ReflectionClass;
import java.util.List;
@@ -33,15 +34,15 @@ public class MathExpr extends Expr {
}
@Override
protected Class resolveType(ClassAnalyzer classAnalyzer) {
protected ReflectionClass resolveType(ReflectionAnalyzer reflectionAnalyzer) {
if ("+".equals(mOp)) {
// TODO we need upper casting etc.
if (String.class.equals(getLeft().getResolvedType())
|| String.class.equals(getRight().getResolvedType())) {
return String.class;
if (getLeft().getResolvedType().isString()
|| getRight().getResolvedType().isString()) {
return reflectionAnalyzer.findClass(String.class);
}
}
return classAnalyzer.findCommonParentOf(getLeft().getResolvedType(),
return reflectionAnalyzer.findCommonParentOf(getLeft().getResolvedType(),
getRight().getResolvedType());
}

View File

@@ -18,7 +18,9 @@ package com.android.databinding.expr;
import com.google.common.collect.Iterables;
import com.android.databinding.ClassAnalyzer;
import com.android.databinding.reflection.ReflectionAnalyzer;
import com.android.databinding.reflection.Callable;
import com.android.databinding.reflection.ReflectionClass;
import java.util.ArrayList;
import java.util.Arrays;
@@ -27,7 +29,7 @@ import java.util.List;
public class MethodCallExpr extends Expr {
final String mName;
ClassAnalyzer.Callable mGetter;
Callable mGetter;
MethodCallExpr(Expr target, String name, List<Expr> args) {
super(Iterables.concat(Arrays.asList(target), args));
@@ -35,13 +37,13 @@ public class MethodCallExpr extends Expr {
}
@Override
protected Class resolveType(ClassAnalyzer classAnalyzer) {
protected ReflectionClass resolveType(ReflectionAnalyzer reflectionAnalyzer) {
if (mGetter == null) {
List<Class> args = new ArrayList<>();
List<ReflectionClass> args = new ArrayList<>();
for (Expr expr : getArgs()) {
args.add(expr.getResolvedType());
}
mGetter = classAnalyzer.findMethod(getTarget().getResolvedType(), mName, args);
mGetter = reflectionAnalyzer.findMethod(getTarget().getResolvedType(), mName, args);
}
return mGetter.resolvedType;
}
@@ -75,7 +77,7 @@ public class MethodCallExpr extends Expr {
return getChildren().subList(1, getChildren().size());
}
public ClassAnalyzer.Callable getGetter() {
public Callable getGetter() {
return mGetter;
}
}

View File

@@ -17,12 +17,10 @@ package com.android.databinding.expr;
import com.google.common.collect.ImmutableMap;
import com.android.databinding.ClassAnalyzer;
import com.android.databinding.reflection.ReflectionAnalyzer;
import com.android.databinding.reflection.ReflectionClass;
import java.io.IOException;
import java.net.URL;
import java.util.Collections;
import java.util.Enumeration;
import java.util.List;
import java.util.Map;
@@ -62,7 +60,7 @@ public class ResourceExpr extends Expr {
}
@Override
protected Class resolveType(ClassAnalyzer classAnalyzer) {
protected ReflectionClass resolveType(ReflectionAnalyzer reflectionAnalyzer) {
String type;
switch (mResourceType) {
case "anim":
@@ -72,43 +70,36 @@ public class ResourceExpr extends Expr {
type = "android.animation.Animator";
break;
case "bool":
return boolean.class;
return reflectionAnalyzer.findClass(boolean.class);
case "color":
return int.class;
case "dimenOffset":
case "dimenSize":
case "id":
case "integer":
case "layout":
case "plurals":
return reflectionAnalyzer.findClass(int.class);
case "colorStateList":
type = "android.content.res.ColorStateList";
break;
case "dimen":
return float.class;
case "dimenOffset":
return int.class;
case "dimenSize":
return int.class;
case "fraction":
return reflectionAnalyzer.findClass(float.class);
case "drawable":
type = "android.graphics.drawable.Drawable";
break;
case "fraction":
return float.class;
case "id":
return int.class;
case "intArray":
return int[].class;
case "integer":
return int.class;
return reflectionAnalyzer.findClass(int[].class);
case "interpolator":
type = "";
break;
case "layout":
return int.class;
case "plurals":
return int.class;
case "stateListAnimator":
type = "android.animation.StateListAnimator";
break;
case "string":
return String.class;
return reflectionAnalyzer.findClass(String.class);
case "stringArray":
return String[].class;
return reflectionAnalyzer.findClass(String[].class);
case "transition":
type = "android.transition.Transition";
break;
@@ -119,7 +110,7 @@ public class ResourceExpr extends Expr {
type = mResourceType;
break;
}
return classAnalyzer.findClass(type);
return reflectionAnalyzer.findClass(type);
}
@Override

View File

@@ -18,7 +18,8 @@ package com.android.databinding.expr;
import com.google.common.collect.Lists;
import com.android.databinding.ClassAnalyzer;
import com.android.databinding.reflection.ReflectionAnalyzer;
import com.android.databinding.reflection.ReflectionClass;
import java.util.List;
@@ -37,8 +38,8 @@ public class SymbolExpr extends Expr {
}
@Override
protected Class resolveType(ClassAnalyzer classAnalyzer) {
return mType;
protected ReflectionClass resolveType(ReflectionAnalyzer reflectionAnalyzer) {
return reflectionAnalyzer.findClass(mType);
}
@Override

View File

@@ -18,7 +18,8 @@ package com.android.databinding.expr;
import com.google.common.collect.Lists;
import com.android.databinding.ClassAnalyzer;
import com.android.databinding.reflection.ReflectionAnalyzer;
import com.android.databinding.reflection.ReflectionClass;
import java.util.BitSet;
import java.util.List;
@@ -46,8 +47,8 @@ public class TernaryExpr extends Expr {
}
@Override
protected Class resolveType(ClassAnalyzer classAnalyzer) {
return classAnalyzer.findCommonParentOf(getIfTrue().getResolvedType(),
protected ReflectionClass resolveType(ReflectionAnalyzer reflectionAnalyzer) {
return reflectionAnalyzer.findCommonParentOf(getIfTrue().getResolvedType(),
getIfFalse().getResolvedType());
}

View File

@@ -0,0 +1,58 @@
/*
* Copyright (C) 2015 The Android Open Source Project
*
* Licensed 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 com.android.databinding.reflection;
public class Callable {
public static enum Type {
METHOD,
FIELD
}
public final Type type;
public final String name;
public final ReflectionClass resolvedType;
public final boolean isDynamic;
public final boolean canBeInvalidated;
public Callable(Type type, String name, ReflectionClass resolvedType, boolean isDynamic,
boolean canBeInvalidated) {
this.type = type;
this.name = name;
this.resolvedType = resolvedType;
this.isDynamic = isDynamic;
this.canBeInvalidated = canBeInvalidated;
}
public String getTypeCodeName() {
return resolvedType.toJavaCode();
}
@Override
public String toString() {
return "Callable{" +
"type=" + type +
", name='" + name + '\'' +
", resolvedType=" + resolvedType +
", isDynamic=" + isDynamic +
", canBeInvalidated=" + canBeInvalidated +
'}';
}
}

View File

@@ -14,7 +14,7 @@
* limitations under the License.
*/
package com.android.databinding;
package com.android.databinding.reflection;
import com.google.common.base.Preconditions;
import com.google.common.collect.ImmutableMap;
@@ -24,14 +24,18 @@ import com.android.databinding.util.L;
import org.apache.commons.lang3.StringUtils;
import java.io.IOException;
import java.lang.reflect.Field;
import java.lang.reflect.Method;
import java.lang.reflect.Modifier;
import java.net.URL;
import java.util.ArrayList;
import java.util.Enumeration;
import java.util.HashMap;
import java.util.List;
import java.util.Map;
public class ClassAnalyzer {
public class ClassAnalyzer extends ReflectionAnalyzer {
private static final String OBSERVABLE_CLASS_NAME = "android.binding.Observable";
private static final String OBSERVABLE_LIST_CLASS_NAME = "android.binding.ObservableList";
@@ -62,7 +66,7 @@ public class ClassAnalyzer {
private static ClassLoader sClassLoader;
private HashMap<String, Class> mClassCache = new HashMap<>();
private HashMap<String, ClassClass> mClassCache = new HashMap<>();
private final ClassLoader mClassLoader;
@@ -124,22 +128,27 @@ public class ClassAnalyzer {
return name;
}
public boolean isDataBinder(Class klass) {
return mIViewDataBinder.isAssignableFrom(klass);
@Override
public boolean isDataBinder(ReflectionClass reflectionClass) {
ClassClass classClass = (ClassClass) reflectionClass;
return mIViewDataBinder.isAssignableFrom(classClass.mClass);
}
static String toCodeName(Class klass) {
return klass.getName().replace("$", ".");
}
public Callable findMethod(Class klass, String name, List<Class> args) {
@Override
public Callable findMethod(ReflectionClass reflectionClass, String name,
List<ReflectionClass> argClasses) {
Class klass = ((ClassClass) reflectionClass).mClass;
ArrayList<Class> args = new ArrayList<>(argClasses.size());
for (int i = 0; i < argClasses.size(); i++) {
args.add(((ClassClass) argClasses.get(i)).mClass);
}
// TODO implement properly
for (String methodName : new String[]{"set" + StringUtils.capitalize(name), name}) {
for (Method method : klass.getMethods()) {
if (methodName.equals(method.getName()) && args.size() == method
.getParameterTypes().length) {
return new Callable(Callable.Type.METHOD, methodName, method.getReturnType(), true,
false);
return new Callable(Callable.Type.METHOD, methodName,
new ClassClass(method.getReturnType()), true, false);
}
}
}
@@ -148,12 +157,20 @@ public class ClassAnalyzer {
"cannot find method " + name + " at class " + klass.getSimpleName());
}
public boolean isObservable(Class klass) {
return mObservable.isAssignableFrom(klass) || mObservableList.isAssignableFrom(klass) ||
mObservableMap.isAssignableFrom(klass);
@Override
public boolean isObservable(ReflectionClass reflectionClass) {
Class klass = ((ClassClass) reflectionClass).mClass;
return isObservable(klass);
}
public boolean isObservableField(Class klass) {
private boolean isObservable(Class klass) {
return mObservable.isAssignableFrom(klass) || mObservableList.isAssignableFrom(klass) ||
mObservableMap.isAssignableFrom(klass);
}
@Override
public boolean isObservableField(ReflectionClass reflectionClass) {
Class klass = ((ClassClass) reflectionClass).mClass;
for (Class observableField : mObservableFields) {
if (observableField.isAssignableFrom(klass)) {
return true;
@@ -162,16 +179,29 @@ public class ClassAnalyzer {
return false;
}
public boolean isBindable(Field field) {
@Override
public boolean isBindable(ReflectionField reflectionField) {
Field field = ((ClassField) reflectionField).mField;
return isBindable(field);
}
@Override
public boolean isBindable(ReflectionMethod reflectionMethod) {
Method method = ((ClassMethod) reflectionMethod).mMethod;
return isBindable(method);
}
private boolean isBindable(Field field) {
return field.getAnnotation(mBindable) != null;
}
public boolean isBindable(Method method) {
private boolean isBindable(Method method) {
return method.getAnnotation(mBindable) != null;
}
public Callable findMethodOrField(Class klass, String name) {
@Override
public Callable findMethodOrField(ReflectionClass reflectionClass, String name) {
Class klass = ((ClassClass) reflectionClass).mClass;
for (String methodName :
new String[]{"get" + StringUtils.capitalize(name),
"is" + StringUtils.capitalize(name), name}) {
@@ -180,9 +210,8 @@ public class ClassAnalyzer {
Field backingField = findField(klass, name, true);
if (Modifier.isPublic(method.getModifiers())) {
final Callable result = new Callable(Callable.Type.METHOD, methodName,
method.getReturnType(),
true, isBindable(method) || (backingField != null && isBindable(
backingField)));
new ClassClass(method.getReturnType()), true,
isBindable(method) || (backingField != null && isBindable(backingField)) );
L.d("backing field for %s is %s", result, backingField);
return result;
}
@@ -193,9 +222,9 @@ public class ClassAnalyzer {
try {
Field field = findField(klass, name, false);
if (Modifier.isPublic(field.getModifiers())) {
return new Callable(Callable.Type.FIELD, name, field.getType(),
return new Callable(Callable.Type.FIELD, name, new ClassClass(field.getType()),
!Modifier.isFinal(field.getModifiers())
|| isObservable(field.getType()), isBindable(field));
|| isObservable(field.getType()), isBindable(field));
}
} catch (Throwable t) {
@@ -247,14 +276,18 @@ public class ClassAnalyzer {
return findField(klass, name, false);
}
public Class findCommonParentOf(Class klass1, Class klass2) {
Class curr = klass1;
@Override
public ReflectionClass findCommonParentOf(ReflectionClass reflectionClass1,
ReflectionClass reflectionClass2) {
ClassClass klass1 = (ClassClass) reflectionClass1;
ClassClass klass2 = (ClassClass) reflectionClass2;
ClassClass curr = klass1;
while (curr != null && !curr.isAssignableFrom(klass2)) {
curr = curr.getSuperclass();
}
if (curr == null) {
Class primitive1 = SetterStore.getPrimitiveType(klass1);
Class primitive2 = SetterStore.getPrimitiveType(klass2);
ClassClass primitive1 = klass1.unbox();
ClassClass primitive2 = klass2.unbox();
if (!klass1.equals(primitive1) || !klass2.equals(primitive2)) {
return findCommonParentOf(primitive1, primitive2);
}
@@ -264,62 +297,31 @@ public class ClassAnalyzer {
return curr;
}
public ClassLoader getClassLoader() {
return mClassLoader;
}
public Class loadPrimitive(String className) {
public ClassClass loadPrimitive(String className) {
if ("int".equals(className)) {
return int.class;
return new ClassClass(int.class);
}
if ("short".equals(className)) {
return short.class;
return new ClassClass(short.class);
}
if ("long".equals(className)) {
return long.class;
return new ClassClass(long.class);
}
if ("float".equals(className)) {
return float.class;
return new ClassClass(float.class);
}
if ("double".equals(className)) {
return double.class;
return new ClassClass(double.class);
}
if ("boolean".equals(className) || "bool".equals(className)) {
return boolean.class;
return new ClassClass(boolean.class);
}
return null;
}
public String getDefaultValue(String className) {
if("int".equals(className)) {
return "0";
}
if("short".equals(className)) {
return "0";
}
if("long".equals(className)) {
return "0L";
}
if("float".equals(className)) {
return "0f";
}
if("double".equals(className)) {
return "0.0";
}
if("boolean".equals(className)) {
return "false";
}
if ("char".equals(className)) {
return "'\\u0000'";
}
if ("byte".equals(className)) {
return "0";
}
return "null";
}
public Class findClass(String className) {
Class loaded = mClassCache.get(className);
@Override
public ClassClass findClass(String className) {
ClassClass loaded = mClassCache.get(className);
if (loaded != null) {
return loaded;
}
@@ -329,11 +331,11 @@ public class ClassAnalyzer {
try {
if (className.startsWith("[") && className.contains("L")) {
int indexOfL = className.indexOf('L');
Class baseClass = findClass(
ClassClass baseClass = findClass(
className.substring(indexOfL + 1, className.length() - 1));
String realClassName = className.substring(0, indexOfL + 1) +
baseClass.getCanonicalName() + ';';
loaded = Class.forName(realClassName, false, mClassLoader);
baseClass.mClass.getCanonicalName() + ';';
loaded = new ClassClass(Class.forName(realClassName, false, mClassLoader));
mClassCache.put(className, loaded);
} else {
loaded = loadRecursively(className);
@@ -345,14 +347,24 @@ public class ClassAnalyzer {
}
}
Preconditions.checkNotNull(loaded, "Tried to load " + className + " but could not find :/");
L.d("loaded class %s", loaded.getCanonicalName());
L.d("loaded class %s", loaded.mClass.getCanonicalName());
return loaded;
}
public Class loadRecursively(String className) throws ClassNotFoundException {
@Override
public boolean isNullable(ReflectionClass reflectionClass) {
return false;
}
@Override
public ReflectionClass findClass(Class classType) {
return new ClassClass(classType);
}
private ClassClass loadRecursively(String className) throws ClassNotFoundException {
try {
L.d("recursively checking %s", className);
return mClassLoader.loadClass(className);
return new ClassClass(mClassLoader.loadClass(className));
} catch (ClassNotFoundException ex) {
int lastIndexOfDot = className.lastIndexOf(".");
if (lastIndexOfDot == -1) {
@@ -370,50 +382,20 @@ public class ClassAnalyzer {
}
}
public static boolean isNullable(Class klass) {
return Object.class.isAssignableFrom(klass);
}
public static class Callable {
public static enum Type {
METHOD,
FIELD
}
public final Type type;
public final String name;
public final Class resolvedType;
public final boolean isDynamic;
public final boolean canBeInvalidated;
public Callable(Type type, String name, Class resolvedType, boolean isDynamic,
boolean canBeInvalidated) {
this.type = type;
this.name = name;
this.resolvedType = resolvedType;
this.isDynamic = isDynamic;
this.canBeInvalidated = canBeInvalidated;
}
public String getTypeCodeName() {
return ClassAnalyzer.toCodeName(resolvedType);
}
@Override
public String toString() {
return "Callable{" +
"type=" + type +
", name='" + name + '\'' +
", resolvedType=" + resolvedType +
", isDynamic=" + isDynamic +
", canBeInvalidated=" + canBeInvalidated +
'}';
@Override
public List<URL> getResources(String name) {
List<URL> urlList = new ArrayList<URL>();
Enumeration<URL> urls = null;
try {
urls = mClassLoader.getResources(name);
if (urls != null) {
while (urls.hasMoreElements()) {
urlList.add(urls.nextElement());
}
}
} catch (IOException e) {
e.printStackTrace();
}
return urlList;
}
}

View File

@@ -0,0 +1,230 @@
/*
* Copyright (C) 2015 The Android Open Source Project
*
* Licensed 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 com.android.databinding.reflection;
import java.lang.reflect.Method;
import java.net.URL;
import java.util.ArrayList;
import java.util.Enumeration;
import java.util.List;
import java.util.Map;
public class ClassClass implements ReflectionClass {
public final Class mClass;
public ClassClass(Class clazz) {
mClass = clazz;
}
@Override
public String toJavaCode() {
return toJavaCode(mClass);
}
private static String toJavaCode(Class aClass) {
if (aClass.isArray()) {
Class component = aClass.getComponentType();
return toJavaCode(component) + "[]";
} else {
return aClass.getCanonicalName().replace('$', '.');
}
}
@Override
public boolean isArray() {
return mClass.isArray();
}
@Override
public ClassClass getComponentType() {
if (mClass.isArray()) {
return new ClassClass(mClass.getComponentType());
} else if (isList() || isMap()) {
return new ClassClass(Object.class);
} else {
return null;
}
}
@Override
public boolean isList() {
return List.class.isAssignableFrom(mClass);
}
@Override
public boolean isMap() {
return Map.class.isAssignableFrom(mClass);
}
@Override
public boolean isString() {
return String.class.equals(mClass);
}
@Override
public boolean isNullable() {
return Object.class.isAssignableFrom(mClass);
}
@Override
public boolean isPrimitive() {
return mClass.isPrimitive();
}
@Override
public boolean isBoolean() {
return boolean.class.equals(mClass);
}
@Override
public boolean isChar() {
return char.class.equals(mClass);
}
@Override
public boolean isByte() {
return byte.class.equals(mClass);
}
@Override
public boolean isShort() {
return short.class.equals(mClass);
}
@Override
public boolean isInt() {
return int.class.equals(mClass);
}
@Override
public boolean isLong() {
return long.class.equals(mClass);
}
@Override
public boolean isFloat() {
return float.class.equals(mClass);
}
@Override
public boolean isDouble() {
return double.class.equals(mClass);
}
@Override
public boolean isObject() {
return Object.class.equals(mClass);
}
@Override
public boolean isVoid() {
return void.class.equals(mClass);
}
@Override
public ClassClass unbox() {
if (mClass.isPrimitive()) {
return this;
}
if (Integer.class.equals(mClass)) {
return new ClassClass(int.class);
} else if (Long.class.equals(mClass)) {
return new ClassClass(long.class);
} else if (Short.class.equals(mClass)) {
return new ClassClass(short.class);
} else if (Byte.class.equals(mClass)) {
return new ClassClass(byte.class);
} else if (Character.class.equals(mClass)) {
return new ClassClass(char.class);
} else if (Double.class.equals(mClass)) {
return new ClassClass(double.class);
} else if (Float.class.equals(mClass)) {
return new ClassClass(float.class);
} else if (Boolean.class.equals(mClass)) {
return new ClassClass(boolean.class);
} else {
// not a boxed type
return this;
}
}
@Override
public ReflectionClass box() {
if (!mClass.isPrimitive()) {
return this;
}
if (int.class.equals(mClass)) {
return new ClassClass(Integer.class);
} else if (long.class.equals(mClass)) {
return new ClassClass(Long.class);
} else if (short.class.equals(mClass)) {
return new ClassClass(Short.class);
} else if (byte.class.equals(mClass)) {
return new ClassClass(Byte.class);
} else if (char.class.equals(mClass)) {
return new ClassClass(Character.class);
} else if (double.class.equals(mClass)) {
return new ClassClass(Double.class);
} else if (float.class.equals(mClass)) {
return new ClassClass(Float.class);
} else if (boolean.class.equals(mClass)) {
return new ClassClass(Boolean.class);
} else {
// not a valid type?
return this;
}
}
@Override
public boolean isAssignableFrom(ReflectionClass that) {
Class thatClass = ((ClassClass) that).mClass;
return mClass.isAssignableFrom(thatClass);
}
@Override
public ReflectionMethod[] getMethods(String name, int numParameters) {
Method[] methods = mClass.getMethods();
ArrayList<ReflectionMethod> matching = new ArrayList<>();
for (Method method : methods) {
if (method.getName().equals(name) &&
method.getParameterTypes().length == numParameters) {
matching.add(new ClassMethod(method));
}
}
return matching.toArray(new ReflectionMethod[matching.size()]);
}
@Override
public ClassClass getSuperclass() {
return new ClassClass(mClass.getSuperclass());
}
@Override
public boolean equals(Object obj) {
if (obj instanceof ClassClass) {
return mClass.equals(((ClassClass) obj).mClass);
} else {
return false;
}
}
@Override
public int hashCode() {
return mClass.hashCode();
}
}

View File

@@ -0,0 +1,27 @@
/*
* Copyright (C) 2015 The Android Open Source Project
*
* Licensed 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 com.android.databinding.reflection;
import java.lang.reflect.Field;
public class ClassField implements ReflectionField {
public final Field mField;
public ClassField(Field field) {
mField = field;
}
}

View File

@@ -0,0 +1,64 @@
/*
* Copyright (C) 2015 The Android Open Source Project
*
* Licensed 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 com.android.databinding.reflection;
import java.lang.reflect.Method;
import java.lang.reflect.Modifier;
public class ClassMethod implements ReflectionMethod {
public final Method mMethod;
public ClassMethod(Method method) {
mMethod = method;
}
@Override
public ReflectionClass getDeclaringClass() {
return new ClassClass(mMethod.getDeclaringClass());
}
@Override
public ReflectionClass[] getParameterTypes() {
Class[] parameterTypes = mMethod.getParameterTypes();
ReflectionClass[] parameterClasses = new ReflectionClass[parameterTypes.length];
for (int i = 0; i < parameterTypes.length; i++) {
parameterClasses[i] = new ClassClass(parameterTypes[i]);
}
return parameterClasses;
}
@Override
public String getName() {
return mMethod.getName();
}
@Override
public ReflectionClass getReturnType() {
return new ClassClass(mMethod.getReturnType());
}
@Override
public boolean isPublic() {
return Modifier.isPublic(mMethod.getModifiers());
}
@Override
public boolean isStatic() {
return Modifier.isStatic(mMethod.getModifiers());
}
}

View File

@@ -0,0 +1,88 @@
/*
* Copyright (C) 2015 The Android Open Source Project
*
* Licensed 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 com.android.databinding.reflection;
import java.net.URL;
import java.util.List;
public abstract class ReflectionAnalyzer {
private static ReflectionAnalyzer sAnalyzer;
public abstract boolean isDataBinder(ReflectionClass reflectionClass);
public abstract Callable findMethod(ReflectionClass reflectionClass, String name,
List<ReflectionClass> args);
public abstract boolean isObservable(ReflectionClass reflectionClass);
public abstract boolean isObservableField(ReflectionClass reflectionClass);
public abstract boolean isBindable(ReflectionField field);
public abstract boolean isBindable(ReflectionMethod method);
public abstract Callable findMethodOrField(ReflectionClass klass, String name);
public abstract ReflectionClass findCommonParentOf(ReflectionClass reflectionClass1,
ReflectionClass reflectionClass2);
public abstract ReflectionClass loadPrimitive(String className);
public static ReflectionAnalyzer getInstance() {
return sAnalyzer;
}
public static void setClassLoader(ClassLoader classLoader) {
ClassAnalyzer.setClassLoader(classLoader);
sAnalyzer = ClassAnalyzer.getInstance();
}
public String getDefaultValue(String className) {
if("int".equals(className)) {
return "0";
}
if("short".equals(className)) {
return "0";
}
if("long".equals(className)) {
return "0L";
}
if("float".equals(className)) {
return "0f";
}
if("double".equals(className)) {
return "0.0";
}
if("boolean".equals(className)) {
return "false";
}
if ("char".equals(className)) {
return "'\\u0000'";
}
if ("byte".equals(className)) {
return "0";
}
return "null";
}
public abstract ReflectionClass findClass(String className);
public abstract boolean isNullable(ReflectionClass reflectionClass);
public abstract List<URL> getResources(String name);
public abstract ReflectionClass findClass(Class classType);
}

View File

@@ -0,0 +1,68 @@
/*
* Copyright (C) 2015 The Android Open Source Project
*
* Licensed 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 com.android.databinding.reflection;
import java.net.URL;
import java.util.List;
public interface ReflectionClass {
String toJavaCode();
boolean isArray();
ReflectionClass getComponentType();
boolean isList();
boolean isMap();
boolean isString();
boolean isNullable();
boolean isPrimitive();
boolean isBoolean();
boolean isChar();
boolean isByte();
boolean isShort();
boolean isInt();
boolean isLong();
boolean isFloat();
boolean isDouble();
boolean isObject();
boolean isVoid();
ReflectionClass unbox();
ReflectionClass box();
boolean isAssignableFrom(ReflectionClass that);
ReflectionMethod[] getMethods(String name, int numParameters);
ReflectionClass getSuperclass();
}

View File

@@ -0,0 +1,20 @@
/*
* Copyright (C) 2015 The Android Open Source Project
*
* Licensed 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 com.android.databinding.reflection;
public interface ReflectionField {
}

View File

@@ -0,0 +1,30 @@
/*
* Copyright (C) 2015 The Android Open Source Project
*
* Licensed 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 com.android.databinding.reflection;
public interface ReflectionMethod {
ReflectionClass getDeclaringClass();
ReflectionClass[] getParameterTypes();
String getName();
ReflectionClass getReturnType();
boolean isPublic();
boolean isStatic();
}

View File

@@ -15,7 +15,9 @@
*/
package com.android.databinding.store;
import com.android.databinding.ClassAnalyzer;
import com.android.databinding.reflection.ReflectionAnalyzer;
import com.android.databinding.reflection.ReflectionClass;
import com.android.databinding.reflection.ReflectionMethod;
import java.io.File;
import java.io.IOException;
@@ -51,10 +53,10 @@ public class SetterStore {
private static SetterStore sStore;
private final IntermediateV1 mStore;
private final ClassAnalyzer mClassAnalyzer;
private final ReflectionAnalyzer mClassAnalyzer;
private SetterStore(ClassAnalyzer classAnalyzer, IntermediateV1 store) {
mClassAnalyzer = classAnalyzer;
private SetterStore(ReflectionAnalyzer reflectionAnalyzer, IntermediateV1 store) {
mClassAnalyzer = reflectionAnalyzer;
mStore = store;
}
@@ -91,24 +93,22 @@ public class SetterStore {
return sStore;
}
public static SetterStore get(ClassAnalyzer classAnalyzer) {
public static SetterStore get(ReflectionAnalyzer reflectionAnalyzer) {
if (sStore == null) {
sStore = load(classAnalyzer);
sStore = load(reflectionAnalyzer);
}
return sStore;
}
private static SetterStore load(ClassAnalyzer classAnalyzer) {
private static SetterStore load(ReflectionAnalyzer reflectionAnalyzer) {
IntermediateV1 store = new IntermediateV1();
String resourceName = SetterStore.class.getPackage().getName().replace('.', '/') +
"/setter_store.bin";
try {
Enumeration<URL> resources = classAnalyzer.getClassLoader().getResources(resourceName);
while (resources.hasMoreElements()) {
URL resource = resources.nextElement();
for (URL resource : reflectionAnalyzer.getResources(resourceName)) {
merge(store, resource);
}
return new SetterStore(classAnalyzer, store);
return new SetterStore(reflectionAnalyzer, store);
} catch (IOException e) {
System.err.println("Could not read SetterStore intermediate file: " +
e.getLocalizedMessage());
@@ -118,7 +118,7 @@ public class SetterStore {
e.getLocalizedMessage());
e.printStackTrace();
}
return new SetterStore(classAnalyzer, store);
return new SetterStore(reflectionAnalyzer, store);
}
private static SetterStore load(InputStream inputStream)
@@ -282,8 +282,8 @@ public class SetterStore {
}
}
public String getSetterCall(String attribute, Class<?> viewType,
Class<?> valueType, String viewExpression, String valueExpression) {
public String getSetterCall(String attribute, ReflectionClass viewType,
ReflectionClass valueType, String viewExpression, String valueExpression) {
if (!attribute.startsWith("android:")) {
int colon = attribute.indexOf(':');
if (colon >= 0) {
@@ -293,9 +293,9 @@ public class SetterStore {
HashMap<AccessorKey, MethodDescription> adapters = mStore.adapterMethods.get(attribute);
MethodDescription adapter = null;
String setterName = null;
Method bestSetterMethod = getBestSetter(viewType, valueType, attribute);
Class<?> bestViewType = null;
Class<?> bestValueType = null;
ReflectionMethod bestSetterMethod = getBestSetter(viewType, valueType, attribute);
ReflectionClass bestViewType = null;
ReflectionClass bestValueType = null;
if (bestSetterMethod != null) {
bestViewType = bestSetterMethod.getDeclaringClass();
bestValueType = bestSetterMethod.getParameterTypes()[0];
@@ -305,10 +305,10 @@ public class SetterStore {
if (adapters != null) {
for (AccessorKey key : adapters.keySet()) {
try {
Class<?> adapterViewType = mClassAnalyzer.findClass(key.viewType);
ReflectionClass adapterViewType = mClassAnalyzer.findClass(key.viewType);
if (adapterViewType.isAssignableFrom(viewType)) {
try {
Class<?> adapterValueType = mClassAnalyzer.findClass(key.valueType);
ReflectionClass adapterValueType = mClassAnalyzer.findClass(key.valueType);
boolean isBetterView = bestViewType == null ||
bestValueType.isAssignableFrom(adapterValueType);
if (isBetterParameter(valueType, adapterValueType, bestValueType,
@@ -327,8 +327,8 @@ public class SetterStore {
}
}
if (Object.class.equals(valueType) && !bestValueType.isAssignableFrom(valueType)) {
valueExpression = "(" + bestValueType.getCanonicalName() + ") " + valueExpression;
if (valueType.isObject() && !bestValueType.isAssignableFrom(valueType)) {
valueExpression = "(" + bestValueType.toJavaCode() + ") " + valueExpression;
}
MethodDescription conversionMethod = getConversionMethod(valueType, bestValueType);
if (conversionMethod != null) {
@@ -346,14 +346,15 @@ public class SetterStore {
}
}
private Method getBestSetter(Class<?> viewType, Class<?> argumentType, String attribute) {
private ReflectionMethod getBestSetter(ReflectionClass viewType, ReflectionClass argumentType,
String attribute) {
String setterName = null;
HashMap<String, MethodDescription> renamed = mStore.renamedMethods.get(attribute);
if (renamed != null) {
for (String className : renamed.keySet()) {
try {
Class<?> renamedViewType = mClassAnalyzer.findClass(className);
ReflectionClass renamedViewType = mClassAnalyzer.findClass(className);
if (renamedViewType.isAssignableFrom(viewType)) {
setterName = renamed.get(className).method;
break;
@@ -366,17 +367,14 @@ public class SetterStore {
if (setterName == null) {
setterName = getDefaultSetter(attribute);
}
Method[] methods = viewType.getMethods();
ReflectionMethod[] methods = viewType.getMethods(setterName, 1);
Class<?> bestParameterType = null;
Method bestMethod = null;
for (Method method : methods) {
Class<?>[] parameterTypes = method.getParameterTypes();
if (parameterTypes.length == 1 && setterName.equals(method.getName()) &&
void.class.equals(method.getReturnType()) &&
!Modifier.isStatic(method.getModifiers()) &&
Modifier.isPublic(method.getModifiers())) {
Class<?> param = parameterTypes[0];
ReflectionClass bestParameterType = null;
ReflectionMethod bestMethod = null;
for (ReflectionMethod method : methods) {
ReflectionClass[] parameterTypes = method.getParameterTypes();
if (method.getReturnType().isVoid() && !method.isStatic() && method.isPublic()) {
ReflectionClass param = parameterTypes[0];
if (isBetterParameter(argumentType, param, bestParameterType, true)) {
bestParameterType = param;
bestMethod = method;
@@ -394,8 +392,8 @@ public class SetterStore {
return "set" + propertyName;
}
private boolean isBetterParameter(Class<?> argument, Class<?> parameter, Class<?> oldParameter,
boolean isBetterViewTypeMatch) {
private boolean isBetterParameter(ReflectionClass argument, ReflectionClass parameter,
ReflectionClass oldParameter, boolean isBetterViewTypeMatch) {
// Right view type. Check the value
if (!isBetterViewTypeMatch && oldParameter.equals(argument)) {
return false;
@@ -430,15 +428,14 @@ public class SetterStore {
if (getConversionMethod(argument, oldParameter) != null) {
return false;
}
return argument.equals(Object.class) && !parameter.isPrimitive();
return argument.isObject() && !parameter.isPrimitive();
}
}
}
private static boolean isImplicitConversion(Class<?> from, Class<?> to) {
private static boolean isImplicitConversion(ReflectionClass from, ReflectionClass to) {
if (from != null && to != null && from.isPrimitive() && to.isPrimitive()) {
if (from.equals(boolean.class) || to.equals(boolean.class) ||
to.equals(char.class)) {
if (from.isBoolean() || to.isBoolean() || to.isChar()) {
return false;
}
int fromConversionLevel = getConversionLevel(from);
@@ -449,17 +446,17 @@ public class SetterStore {
}
}
private MethodDescription getConversionMethod(Class<?> from, Class<?> to) {
private MethodDescription getConversionMethod(ReflectionClass from, ReflectionClass to) {
if (from != null && to != null) {
for (String fromClassName : mStore.conversionMethods.keySet()) {
try {
Class<?> convertFrom = mClassAnalyzer.findClass(fromClassName);
ReflectionClass convertFrom = mClassAnalyzer.findClass(fromClassName);
if (canUseForConversion(from, convertFrom)) {
HashMap<String, MethodDescription> conversion =
mStore.conversionMethods.get(fromClassName);
for (String toClassName : conversion.keySet()) {
try {
Class<?> convertTo = mClassAnalyzer.findClass(toClassName);
ReflectionClass convertTo = mClassAnalyzer.findClass(toClassName);
if (canUseForConversion(convertTo, to)) {
return conversion.get(toClassName);
}
@@ -476,90 +473,40 @@ public class SetterStore {
return null;
}
private static boolean canUseForConversion(Class<?> from, Class<?> to) {
private boolean canUseForConversion(ReflectionClass from, ReflectionClass to) {
return from.equals(to) || isBoxingConversion(from, to) || to.isAssignableFrom(from);
}
private static int getConversionLevel(Class<?> primitive) {
if (byte.class.equals(primitive)) {
private static int getConversionLevel(ReflectionClass primitive) {
if (primitive == null) {
return -1;
} else if (primitive.isByte()) {
return 0;
} else if (char.class.equals(primitive)) {
} else if (primitive.isChar()) {
return 1;
} else if (short.class.equals(primitive)) {
} else if (primitive.isShort()) {
return 2;
} else if (int.class.equals(primitive)) {
} else if (primitive.isInt()) {
return 3;
} else if (long.class.equals(primitive)) {
} else if (primitive.isLong()) {
return 4;
} else if (float.class.equals(primitive)) {
} else if (primitive.isFloat()) {
return 5;
} else if (double.class.equals(primitive)) {
} else if (primitive.isDouble()) {
return 6;
} else {
return -1;
}
}
public static boolean isBoxingConversion(Class<?> class1, Class<?> class2) {
public static boolean isBoxingConversion(ReflectionClass class1, ReflectionClass class2) {
if (class1.isPrimitive() != class2.isPrimitive()) {
return (getWrappedType(class1).equals(getWrappedType(class2)));
return (class1.box().equals(class2.box()));
} else {
return false;
}
}
public static Class<?> getWrappedType(Class<?> type) {
if (!type.isPrimitive()) {
return type;
}
if (int.class.equals(type)) {
return Integer.class;
} else if (long.class.equals(type)) {
return Long.class;
} else if (short.class.equals(type)) {
return Short.class;
} else if (byte.class.equals(type)) {
return Byte.class;
} else if (char.class.equals(type)) {
return Character.class;
} else if (double.class.equals(type)) {
return Double.class;
} else if (float.class.equals(type)) {
return Float.class;
} else if (boolean.class.equals(type)) {
return Boolean.class;
} else {
// what type is this?
return type;
}
}
public static Class<?> getPrimitiveType(Class<?> type) {
if (type.isPrimitive()) {
return type;
}
if (Integer.class.equals(type)) {
return int.class;
} else if (Long.class.equals(type)) {
return long.class;
} else if (Short.class.equals(type)) {
return short.class;
} else if (Byte.class.equals(type)) {
return byte.class;
} else if (Character.class.equals(type)) {
return char.class;
} else if (Double.class.equals(type)) {
return double.class;
} else if (Float.class.equals(type)) {
return float.class;
} else if (Boolean.class.equals(type)) {
return boolean.class;
} else {
// what type is this?
return type;
}
}
private static void merge(IntermediateV1 store,
URL nextUrl) throws IOException, ClassNotFoundException {
InputStream inputStream = null;

View File

@@ -17,7 +17,9 @@ import kotlin.properties.ReadOnlyProperty
import kotlin.properties.Delegates
import com.android.databinding.ext.joinToCamelCase
import com.android.databinding.ext.joinToCamelCaseAsVar
import com.android.databinding.ClassAnalyzer
import com.android.databinding.reflection.ReflectionAnalyzer
import com.android.databinding.reflection.ReflectionClass
import com.android.databinding.reflection.ReflectionAnalyzer
private class LazyExt<K, T>(private val initializer: (k : K) -> T) : ReadOnlyProperty<K, T> {
private val mapping = hashMapOf<K, T>()
@@ -76,7 +78,3 @@ public fun String.toCamelCaseAsVar() : String {
public fun String.br() : String =
"android.binding.BR.${if (this == "") "_all" else this}"
public fun Class<*>.getCodeName() : String = getName().replace("$", ".")
public fun Class<*>.isObservable() : Boolean = ClassAnalyzer.getInstance().isObservable(this)

View File

@@ -50,7 +50,7 @@ import com.android.databinding.util.Log
import com.android.databinding.LayoutBinder
import com.android.databinding.DataBinder
import com.android.databinding.writer.DataBinderWriter
import com.android.databinding.ClassAnalyzer
import com.android.databinding.reflection.ReflectionAnalyzer
import com.android.databinding.util.ParserHelper
import com.google.common.base.Preconditions
@@ -59,7 +59,7 @@ public class KLayoutParser(val appPkg : String, val resourceFolders : kotlin.Ite
val outputBaseDir : File, val outputResBaseDir : File) {
var dbr : DataBinderWriter by Delegates.notNull()
var processed = false
public var classAnalyzer : ClassAnalyzer by Delegates.notNull()
public var reflectionAnalyzer : ReflectionAnalyzer by Delegates.notNull()
val jDataBinder = DataBinder();

View File

@@ -15,7 +15,7 @@ package com.android.databinding.writer
import com.android.databinding.LayoutBinder
import com.android.databinding.expr.Expr
import com.android.databinding.ClassAnalyzer
import com.android.databinding.reflection.ReflectionAnalyzer
import kotlin.properties.Delegates
import com.android.databinding.ext.joinToCamelCaseAsVar
import com.android.databinding.BindingTarget
@@ -42,9 +42,9 @@ import com.android.databinding.ext.androidId
import com.android.databinding.ext.lazy
import com.android.databinding.ext.br
import com.android.databinding.ext.toJavaCode
import com.android.databinding.ext.isObservable
import com.android.databinding.expr.ResourceExpr
import com.android.databinding.expr.BracketExpr
import com.android.databinding.reflection.Callable
fun String.stripNonJava() = this.split("[^a-zA-Z0-9]").map{ it.trim() }.joinToCamelCaseAsVar()
@@ -160,7 +160,7 @@ fun Expr.toCode(full : Boolean = false) : KCode {
}
is FieldAccessExpr -> kcode("") {
app("", it.getParent().toCode())
if (it.getGetter().type == com.android.databinding.ClassAnalyzer.Callable.Type.FIELD) {
if (it.getGetter().type == Callable.Type.FIELD) {
app(".", it.getGetter().name)
} else {
app(".", it.getGetter().name).app("()")
@@ -602,8 +602,7 @@ class LayoutBinderWriter(val layoutBinder : LayoutBinder) {
tab("// read ${expr.getUniqueKey()}")
// create an if case for all dependencies that might be null
val nullables = expr.getDependencies().filter {
it.isMandatory() &&
ClassAnalyzer.isNullable(it.getOther().getResolvedType())
it.isMandatory() && it.getOther().getResolvedType().isNullable()
}
.map { it.getOther() }
if (!expr.isEqualityCheck() && nullables.isNotEmpty()) {

View File

@@ -16,8 +16,6 @@
package com.android.databinding;
import com.android.databinding.ClassAnalyzer;
import com.android.databinding.ExpressionParser;
import com.android.databinding.expr.ComparisonExpr;
import com.android.databinding.expr.Dependency;
import com.android.databinding.expr.Expr;
@@ -27,6 +25,8 @@ import com.android.databinding.expr.IdentifierExpr;
import com.android.databinding.expr.MethodCallExpr;
import com.android.databinding.expr.SymbolExpr;
import com.android.databinding.expr.TernaryExpr;
import com.android.databinding.reflection.Callable;
import com.android.databinding.reflection.ReflectionAnalyzer;
import org.junit.Before;
import org.junit.Test;
@@ -43,7 +43,7 @@ public class ExpressionVisitorTest {
@Before
public void setUp() throws Exception {
ClassAnalyzer.initForTests();
ReflectionAnalyzer.initForTests();
}
private <T extends Expr> T parse(String input, Class<T> klass) {
@@ -69,7 +69,7 @@ public class ExpressionVisitorTest {
@Before
public void setUp() throws Exception {
ClassAnalyzer.initForTests();
ReflectionAnalyzer.initForTests();
}
@Parameterized.Parameters
@@ -143,8 +143,8 @@ public class ExpressionVisitorTest {
final IdentifierExpr id = (IdentifierExpr) parsed.getParent();
id.setUserDefinedType("java.lang.String");
assertSame(int.class, parsed.getResolvedType());
ClassAnalyzer.Callable getter = parsed.getGetter();
assertEquals(ClassAnalyzer.Callable.Type.METHOD, getter.type);
Callable getter = parsed.getGetter();
assertEquals(Callable.Type.METHOD, getter.type);
assertEquals("length", getter.name);
assertEquals(1, parsed.getDependencies().size());
final Dependency dep = parsed.getDependencies().get(0);
@@ -159,8 +159,8 @@ public class ExpressionVisitorTest {
final IdentifierExpr id = (IdentifierExpr) parsed.getParent();
id.setUserDefinedType("java.lang.String");
assertSame(byte[].class, parsed.getResolvedType());
ClassAnalyzer.Callable getter = parsed.getGetter();
assertEquals(ClassAnalyzer.Callable.Type.METHOD, getter.type);
Callable getter = parsed.getGetter();
assertEquals(Callable.Type.METHOD, getter.type);
assertEquals("getBytes", getter.name);
assertEquals(1, parsed.getDependencies().size());
final Dependency dep = parsed.getDependencies().get(0);

View File

@@ -14,13 +14,13 @@
package com.android.databinding;
import com.android.databinding.ClassAnalyzer;
import com.android.databinding.LayoutBinder;
import com.android.databinding.expr.Expr;
import com.android.databinding.expr.ExprModel;
import com.android.databinding.expr.FieldAccessExpr;
import com.android.databinding.expr.IdentifierExpr;
import com.android.databinding.expr.StaticIdentifierExpr;
import com.android.databinding.reflection.Callable;
import com.android.databinding.reflection.ReflectionAnalyzer;
import org.junit.Before;
import org.junit.Test;
@@ -35,7 +35,7 @@ public class LayoutBinderTest {
ExprModel mExprModel;
@Before
public void setUp() throws Exception {
ClassAnalyzer.initForTests();
ReflectionAnalyzer.initForTests();
mLayoutBinder = new LayoutBinder(null);
mExprModel = mLayoutBinder.getModel();
}
@@ -91,8 +91,8 @@ public class LayoutBinderTest {
IdentifierExpr id = mExprModel.identifier("user");
FieldAccessExpr fa = (FieldAccessExpr) item;
fa.getResolvedType();
final ClassAnalyzer.Callable getter = fa.getGetter();
assertTrue(getter.type == ClassAnalyzer.Callable.Type.METHOD);
final Callable getter = fa.getGetter();
assertTrue(getter.type == Callable.Type.METHOD);
assertSame(id, fa.getParent());
assertTrue(fa.isDynamic());
}

View File

@@ -19,7 +19,7 @@ package com.android.databinding.expr;
import com.google.common.base.Predicate;
import com.google.common.collect.Iterables;
import com.android.databinding.ClassAnalyzer;
import com.android.databinding.reflection.ReflectionAnalyzer;
import com.android.databinding.LayoutBinder;
import com.android.databinding.util.L;
@@ -45,7 +45,7 @@ public class ExprModelTest {
}
@Override
protected Class resolveType(ClassAnalyzer classAnalyzer) {
protected Class resolveType(ReflectionAnalyzer reflectionAnalyzer) {
return Integer.class;
}
@@ -77,7 +77,7 @@ public class ExprModelTest {
@Before
public void setUp() throws Exception {
ClassAnalyzer.initForTests();
ReflectionAnalyzer.initForTests();
mExprModel = new ExprModel();
}

View File

@@ -16,7 +16,7 @@
package com.android.databinding.expr;
import com.android.databinding.ClassAnalyzer;
import com.android.databinding.reflection.ReflectionAnalyzer;
import org.junit.Before;
import org.junit.Test;
@@ -37,7 +37,7 @@ public class ExprTest{
}
@Override
protected Class resolveType(ClassAnalyzer classAnalyzer) {
protected Class resolveType(ReflectionAnalyzer reflectionAnalyzer) {
return Integer.class;
}
@@ -59,14 +59,14 @@ public class ExprTest{
@Before
public void setUp() throws Exception {
ClassAnalyzer.initForTests();
ReflectionAnalyzer.initForTests();
}
@Test(expected=IllegalStateException.class)
public void testBadExpr() {
Expr expr = new Expr() {
@Override
protected Class resolveType(ClassAnalyzer classAnalyzer) {
protected Class resolveType(ReflectionAnalyzer reflectionAnalyzer) {
return Integer.class;
}

View File

@@ -44,6 +44,7 @@ import javax.tools.JavaCompiler
import javax.tools.ToolProvider
import java.util.Arrays
import org.apache.commons.io.FileUtils
import com.android.databinding.reflection.ReflectionAnalyzer
class DataBinderPlugin : Plugin<Project> {
var parser: KLayoutParser by Delegates.notNull()
@@ -177,8 +178,8 @@ class DataBinderPlugin : Plugin<Project> {
log("generated urls: ${urls} len: ${urls.size}")
val classLoader = URLClassLoader(urls, androidClassLoader)
log("created class loader")
ClassAnalyzer.setClassLoader(classLoader)
parser.classAnalyzer = ClassAnalyzer.getInstance()
ReflectionAnalyzer.setClassLoader(classLoader)
parser.reflectionAnalyzer = ReflectionAnalyzer.getInstance()
project.task("compileGenerated", MethodClosure(this, "compileGenerated"))
}