Removed reflection-based implementations and renamed classes.

Renamed Model* classes to Annotation*
Renamed Reflection* classes to Model*
Removed Class* classes -- they are no longer needed.

The names were confusing. I think this is better.
This commit is contained in:
George Mount
2015-02-17 16:02:52 -08:00
parent d872e1cdf7
commit 812d215fa6
41 changed files with 1048 additions and 1789 deletions

View File

@@ -1,7 +1,6 @@
package com.android.databinding.annotationprocessor;
import com.android.databinding.reflection.ModelAnalyzer;
import com.android.databinding.reflection.ReflectionAnalyzer;
import android.binding.Bindable;
@@ -18,7 +17,6 @@ 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;
@@ -28,15 +26,11 @@ 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.PackageElement;
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;
@@ -54,7 +48,7 @@ public class ProcessBindable extends AbstractProcessor {
@Override
public synchronized void init(ProcessingEnvironment processingEnv) {
super.init(processingEnv);
ReflectionAnalyzer.setProcessingEnvironment(processingEnv);
ModelAnalyzer.setProcessingEnvironment(processingEnv);
}
@Override

View File

@@ -16,8 +16,8 @@
package com.android.databinding;
import com.android.databinding.reflection.ReflectionAnalyzer;
import com.android.databinding.reflection.ReflectionClass;
import com.android.databinding.reflection.ModelAnalyzer;
import com.android.databinding.reflection.ModelClass;
import com.android.databinding.store.SetterStore;
import com.android.databinding.expr.Expr;
@@ -39,15 +39,15 @@ public class Binding {
}
public String toJavaCode(String targetViewName, String expressionCode) {
ReflectionClass viewType = mTarget.getResolvedType();
return SetterStore.get(ReflectionAnalyzer.getInstance()).getSetterCall(mName, viewType,
ModelClass viewType = mTarget.getResolvedType();
return SetterStore.get(ModelAnalyzer.getInstance()).getSetterCall(mName, viewType,
mExpr.getResolvedType(), targetViewName, expressionCode);
}
// private String resolveJavaCode(ReflectionAnalyzer reflectionAnalyzer) {
// private String resolveJavaCode(ModelAnalyzer modelAnalyzer) {
//
// }
//// return reflectionAnalyzer.findMethod(mTarget.getResolvedType(), mName,
//// return modelAnalyzer.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 com.android.databinding.reflection.ReflectionAnalyzer;
import com.android.databinding.reflection.ReflectionClass;
import com.android.databinding.reflection.ModelAnalyzer;
import com.android.databinding.reflection.ModelClass;
import com.android.databinding.store.ResourceBundle;
import java.util.ArrayList;
@@ -28,7 +28,7 @@ import java.util.List;
public class BindingTarget {
List<Binding> mBindings = new ArrayList<>();
ExprModel mModel;
ReflectionClass mResolvedClass;
ModelClass mResolvedClass;
// 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.
private ResourceBundle.BindingTargetBundle mBundle;
@@ -57,9 +57,9 @@ public class BindingTarget {
return mBundle.getFullClassName();
}
public ReflectionClass getResolvedType() {
public ModelClass getResolvedType() {
if (mResolvedClass == null) {
mResolvedClass = ReflectionAnalyzer.getInstance().findClass(mBundle.getFullClassName());
mResolvedClass = ModelAnalyzer.getInstance().findClass(mBundle.getFullClassName());
}
return mResolvedClass;
}

View File

@@ -20,7 +20,6 @@ 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;
@@ -241,8 +240,8 @@ public class ExpressionVisitor extends BindingExpressionBaseVisitor<Expr> {
// @org.jetbrains.annotations.NotNull
// @Override
// public Class<? extends Object> resolveValueType(
// @org.jetbrains.annotations.NotNull ReflectionAnalyzer reflectionAnalyzer) {
// return reflectionAnalyzer.commonParentOf(aggregate.getResolvedClass(), nextResult.getResolvedClass());
// @org.jetbrains.annotations.NotNull ModelAnalyzer modelAnalyzer) {
// return modelAnalyzer.commonParentOf(aggregate.getResolvedClass(), nextResult.getResolvedClass());
// }
//
// @org.jetbrains.annotations.NotNull

View File

@@ -16,12 +16,10 @@
package com.android.databinding.expr;
import com.android.databinding.reflection.ClassClass;
import com.android.databinding.reflection.ReflectionAnalyzer;
import com.android.databinding.reflection.ReflectionClass;
import com.android.databinding.reflection.ModelAnalyzer;
import com.android.databinding.reflection.ModelClass;
import java.util.List;
import java.util.Map;
public class BracketExpr extends Expr {
@@ -38,8 +36,8 @@ public class BracketExpr extends Expr {
}
@Override
protected ReflectionClass resolveType(ReflectionAnalyzer reflectionAnalyzer) {
ReflectionClass targetType = getTarget().resolveType(reflectionAnalyzer);
protected ModelClass resolveType(ModelAnalyzer modelAnalyzer) {
ModelClass targetType = getTarget().resolveType(modelAnalyzer);
if (targetType.isArray()) {
mAccessor = BracketAccessor.ARRAY;
} else if (targetType.isList()) {

View File

@@ -16,8 +16,8 @@
package com.android.databinding.expr;
import com.android.databinding.reflection.ReflectionAnalyzer;
import com.android.databinding.reflection.ReflectionClass;
import com.android.databinding.reflection.ModelAnalyzer;
import com.android.databinding.reflection.ModelClass;
import java.util.List;
@@ -34,8 +34,8 @@ public class ComparisonExpr extends Expr {
}
@Override
protected ReflectionClass resolveType(ReflectionAnalyzer reflectionAnalyzer) {
return reflectionAnalyzer.loadPrimitive("boolean");
protected ModelClass resolveType(ModelAnalyzer modelAnalyzer) {
return modelAnalyzer.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.reflection.ReflectionAnalyzer;
import com.android.databinding.reflection.ReflectionClass;
import com.android.databinding.reflection.ModelAnalyzer;
import com.android.databinding.reflection.ModelClass;
abstract public class Expr {
@@ -41,7 +41,7 @@ abstract public class Expr {
private Boolean mIsDynamic;
private ReflectionClass mResolvedType;
private ModelClass mResolvedType;
private String mUniqueKey;
@@ -145,7 +145,7 @@ abstract public class Expr {
}
public boolean isObservable() {
return ReflectionAnalyzer.getInstance().isObservable(getResolvedType());
return ModelAnalyzer.getInstance().isObservable(getResolvedType());
}
public BitSet getShouldReadFlags() {
@@ -278,15 +278,15 @@ abstract public class Expr {
}
public ReflectionClass getResolvedType() {
public ModelClass getResolvedType() {
if (mResolvedType == null) {
// TODO not get instance
mResolvedType = resolveType(ReflectionAnalyzer.getInstance());
mResolvedType = resolveType(ModelAnalyzer.getInstance());
}
return mResolvedType;
}
abstract protected ReflectionClass resolveType(ReflectionAnalyzer reflectionAnalyzer);
abstract protected ModelClass resolveType(ModelAnalyzer modelAnalyzer);
abstract protected List<Dependency> constructDependencies();
@@ -535,7 +535,7 @@ abstract public class Expr {
}
public String getDefaultValue() {
return ReflectionAnalyzer.getInstance().getDefaultValue(getResolvedType().toJavaCode());
return ModelAnalyzer.getInstance().getDefaultValue(getResolvedType().toJavaCode());
}
protected BitSet getPredicateInvalidFlags() {
@@ -569,7 +569,7 @@ abstract public class Expr {
return mIsUsed;
}
public void updateExpr(ReflectionAnalyzer reflectionAnalyzer) {
public void updateExpr(ModelAnalyzer modelAnalyzer) {
}
static class Node {

View File

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

View File

@@ -16,11 +16,10 @@
package com.android.databinding.expr;
import com.android.databinding.reflection.ReflectionAnalyzer;
import com.android.databinding.reflection.ModelAnalyzer;
import com.android.databinding.reflection.Callable;
import com.android.databinding.reflection.ReflectionClass;
import com.android.databinding.reflection.ModelClass;
import java.util.Collections;
import java.util.List;
public class FieldAccessExpr extends Expr {
@@ -87,10 +86,10 @@ public class FieldAccessExpr extends Expr {
}
@Override
public void updateExpr(ReflectionAnalyzer reflectionAnalyzer) {
public void updateExpr(ModelAnalyzer modelAnalyzer) {
if (mGetter == null) {
mGetter = reflectionAnalyzer.findMethodOrField(mChildren.get(0).getResolvedType(), mName);
if (reflectionAnalyzer.isObservableField(mGetter.resolvedType)) {
mGetter = modelAnalyzer.findMethodOrField(mChildren.get(0).getResolvedType(), mName);
if (modelAnalyzer.isObservableField(mGetter.resolvedType)) {
// Make this the ".get()" and add an extra field access for the observable field
Expr parent = getParent();
parent.getParents().remove(this);
@@ -101,16 +100,16 @@ public class FieldAccessExpr extends Expr {
getChildren().add(observableField);
observableField.getParents().add(this);
mGetter = reflectionAnalyzer.findMethodOrField(mGetter.resolvedType, "get");
mGetter = modelAnalyzer.findMethodOrField(mGetter.resolvedType, "get");
mName = "";
}
}
}
@Override
protected ReflectionClass resolveType(ReflectionAnalyzer reflectionAnalyzer) {
protected ModelClass resolveType(ModelAnalyzer modelAnalyzer) {
if (mGetter == null) {
mGetter = reflectionAnalyzer.findMethodOrField(mChildren.get(0).getResolvedType(), mName);
mGetter = modelAnalyzer.findMethodOrField(mChildren.get(0).getResolvedType(), mName);
}
return mGetter.resolvedType;
}

View File

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

View File

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

View File

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

View File

@@ -18,9 +18,9 @@ package com.android.databinding.expr;
import com.google.common.collect.Iterables;
import com.android.databinding.reflection.ReflectionAnalyzer;
import com.android.databinding.reflection.ModelAnalyzer;
import com.android.databinding.reflection.Callable;
import com.android.databinding.reflection.ReflectionClass;
import com.android.databinding.reflection.ModelClass;
import java.util.ArrayList;
import java.util.Arrays;
@@ -37,13 +37,13 @@ public class MethodCallExpr extends Expr {
}
@Override
protected ReflectionClass resolveType(ReflectionAnalyzer reflectionAnalyzer) {
protected ModelClass resolveType(ModelAnalyzer modelAnalyzer) {
if (mGetter == null) {
List<ReflectionClass> args = new ArrayList<>();
List<ModelClass> args = new ArrayList<>();
for (Expr expr : getArgs()) {
args.add(expr.getResolvedType());
}
mGetter = reflectionAnalyzer.findMethod(getTarget().getResolvedType(), mName, args);
mGetter = modelAnalyzer.findMethod(getTarget().getResolvedType(), mName, args);
}
return mGetter.resolvedType;
}

View File

@@ -17,8 +17,8 @@ package com.android.databinding.expr;
import com.google.common.collect.ImmutableMap;
import com.android.databinding.reflection.ReflectionAnalyzer;
import com.android.databinding.reflection.ReflectionClass;
import com.android.databinding.reflection.ModelAnalyzer;
import com.android.databinding.reflection.ModelClass;
import java.util.Collections;
import java.util.List;
@@ -60,7 +60,7 @@ public class ResourceExpr extends Expr {
}
@Override
protected ReflectionClass resolveType(ReflectionAnalyzer reflectionAnalyzer) {
protected ModelClass resolveType(ModelAnalyzer modelAnalyzer) {
String type;
switch (mResourceType) {
case "anim":
@@ -70,7 +70,7 @@ public class ResourceExpr extends Expr {
type = "android.animation.Animator";
break;
case "bool":
return reflectionAnalyzer.findClass(boolean.class);
return modelAnalyzer.findClass(boolean.class);
case "color":
case "dimenOffset":
case "dimenSize":
@@ -78,18 +78,18 @@ public class ResourceExpr extends Expr {
case "integer":
case "layout":
case "plurals":
return reflectionAnalyzer.findClass(int.class);
return modelAnalyzer.findClass(int.class);
case "colorStateList":
type = "android.content.res.ColorStateList";
break;
case "dimen":
case "fraction":
return reflectionAnalyzer.findClass(float.class);
return modelAnalyzer.findClass(float.class);
case "drawable":
type = "android.graphics.drawable.Drawable";
break;
case "intArray":
return reflectionAnalyzer.findClass(int[].class);
return modelAnalyzer.findClass(int[].class);
case "interpolator":
type = "";
break;
@@ -97,9 +97,9 @@ public class ResourceExpr extends Expr {
type = "android.animation.StateListAnimator";
break;
case "string":
return reflectionAnalyzer.findClass(String.class);
return modelAnalyzer.findClass(String.class);
case "stringArray":
return reflectionAnalyzer.findClass(String[].class);
return modelAnalyzer.findClass(String[].class);
case "transition":
type = "android.transition.Transition";
break;
@@ -110,7 +110,7 @@ public class ResourceExpr extends Expr {
type = mResourceType;
break;
}
return reflectionAnalyzer.findClass(type);
return modelAnalyzer.findClass(type);
}
@Override

View File

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

View File

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

View File

@@ -0,0 +1,378 @@
/*
* 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 com.google.common.collect.ImmutableMap;
import com.android.databinding.util.L;
import org.apache.commons.lang3.StringUtils;
import android.binding.Bindable;
import java.io.IOException;
import java.net.URL;
import java.util.ArrayList;
import java.util.Enumeration;
import java.util.List;
import java.util.Map;
import javax.annotation.processing.ProcessingEnvironment;
import javax.lang.model.element.ExecutableElement;
import javax.lang.model.element.Modifier;
import javax.lang.model.element.TypeElement;
import javax.lang.model.element.VariableElement;
import javax.lang.model.type.DeclaredType;
import javax.lang.model.type.TypeKind;
import javax.lang.model.type.TypeMirror;
import javax.lang.model.util.ElementFilter;
import javax.lang.model.util.Elements;
import javax.lang.model.util.Types;
import javax.tools.Diagnostic;
public class AnnotationAnalyzer extends ModelAnalyzer {
public static final String LIST_CLASS_NAME = "java.util.List";
public static final String MAP_CLASS_NAME = "java.util.Map";
public static final String STRING_CLASS_NAME = "java.lang.String";
public static final String OBJECT_CLASS_NAME = "java.lang.Object";
static AnnotationAnalyzer instance;
private static final String OBSERVABLE_CLASS_NAME = "android.binding.Observable";
private static final String OBSERVABLE_LIST_CLASS_NAME = "android.binding.ObservableList";
private static final String OBSERVABLE_MAP_CLASS_NAME = "android.binding.ObservableMap";
private static final String[] OBSERVABLE_FIELDS = {
"com.android.databinding.library.ObservableBoolean",
"com.android.databinding.library.ObservableByte",
"com.android.databinding.library.ObservableChar",
"com.android.databinding.library.ObservableShort",
"com.android.databinding.library.ObservableInt",
"com.android.databinding.library.ObservableLong",
"com.android.databinding.library.ObservableFloat",
"com.android.databinding.library.ObservableDouble",
"com.android.databinding.library.ObservableField",
};
private static final String I_VIEW_DATA_BINDER = "com.android.databinding.library.IViewDataBinder";
private static final Map<String, TypeKind> PRIMITIVE_TYPES =
new ImmutableMap.Builder<String, TypeKind>()
.put("boolean", TypeKind.BOOLEAN)
.put("byte", TypeKind.BYTE)
.put("short", TypeKind.SHORT)
.put("char", TypeKind.CHAR)
.put("int", TypeKind.INT)
.put("long", TypeKind.LONG)
.put("float", TypeKind.FLOAT)
.put("double", TypeKind.DOUBLE)
.build();
public final ProcessingEnvironment processingEnv;
public final TypeMirror listType;
public final TypeMirror mapType;
public final TypeMirror stringType;
public final TypeMirror objectType;
private final AnnotationClass mObservableType;
private final AnnotationClass mObservableListType;
private final AnnotationClass mObservableMapType;
private final AnnotationClass[] mObservableFieldTypes;
private final AnnotationClass mIViewDataBinderType;
public AnnotationAnalyzer(ProcessingEnvironment processingEnvironment) {
processingEnv = processingEnvironment;
instance = this;
Types typeUtil = processingEnv.getTypeUtils();
listType = typeUtil.erasure(findType(LIST_CLASS_NAME).asType());
mapType = typeUtil.erasure(findType(MAP_CLASS_NAME).asType());
stringType = typeUtil.erasure(findType(STRING_CLASS_NAME).asType());
objectType = typeUtil.erasure(findType(OBJECT_CLASS_NAME).asType());
mObservableType = new AnnotationClass(findType(OBSERVABLE_CLASS_NAME).asType());
mObservableListType = new AnnotationClass(typeUtil.erasure(
findType(OBSERVABLE_LIST_CLASS_NAME).asType()));
mObservableMapType = new AnnotationClass(typeUtil.erasure(
findType(OBSERVABLE_MAP_CLASS_NAME).asType()));
mIViewDataBinderType = new AnnotationClass(findType(I_VIEW_DATA_BINDER).asType());
mObservableFieldTypes = new AnnotationClass[OBSERVABLE_FIELDS.length];
for (int i = 0; i < OBSERVABLE_FIELDS.length; i++) {
mObservableFieldTypes[i] = new AnnotationClass(findType(OBSERVABLE_FIELDS[i]).asType());
}
}
TypeElement findType(String type) {
return processingEnv.getElementUtils().getTypeElement(type);
}
@Override
public boolean isDataBinder(ModelClass modelClass) {
return mIViewDataBinderType.isAssignableFrom(modelClass);
}
@Override
public Callable findMethod(ModelClass modelClass, String name,
List<ModelClass> args) {
AnnotationClass clazz = (AnnotationClass)modelClass;
// TODO implement properly
for (String methodName : new String[]{"set" + StringUtils.capitalize(name), name}) {
for (ModelMethod method : clazz.getMethods(methodName, args.size())) {
ModelClass[] parameters = method.getParameterTypes();
boolean parametersMatch = true;
boolean isVarArgs = ((AnnotationMethod)method).mMethod.isVarArgs();
for (int i = 0; i < parameters.length; i++) {
if (isVarArgs && i == parameters.length - 1) {
ModelClass component = parameters[i].getComponentType();
for (int j = i; j < args.size(); j++) {
if (!component.isAssignableFrom(args.get(j))) {
parametersMatch = false;
break;
}
}
} else if (!parameters[i].isAssignableFrom(args.get(i))) {
parametersMatch = false;
break;
}
}
if (parametersMatch) {
return new Callable(Callable.Type.METHOD, methodName,
method.getReturnType(args), true, false);
}
}
}
String message = "cannot find method " + name + " at class " + clazz.toJavaCode();
printMessage(Diagnostic.Kind.ERROR, message);
throw new IllegalArgumentException(message);
}
@Override
public boolean isObservable(ModelClass modelClass) {
AnnotationClass annotationClass = (AnnotationClass)modelClass;
return mObservableType.isAssignableFrom(annotationClass) ||
mObservableListType.isAssignableFrom(annotationClass) ||
mObservableMapType.isAssignableFrom(annotationClass);
}
@Override
public boolean isObservableField(ModelClass modelClass) {
AnnotationClass annotationClass = (AnnotationClass)modelClass;
AnnotationClass erasure = new AnnotationClass(getTypeUtils().erasure(annotationClass.mTypeMirror));
for (AnnotationClass observableField : mObservableFieldTypes) {
if (observableField.isAssignableFrom(erasure)) {
return true;
}
}
return false;
}
@Override
public boolean isBindable(ModelField field) {
VariableElement fieldElement = ((AnnotationField) field).mField;
return fieldElement.getAnnotation(Bindable.class) != null;
}
@Override
public boolean isBindable(ModelMethod method) {
ExecutableElement methodElement = ((AnnotationMethod) method).mMethod;
return methodElement.getAnnotation(Bindable.class) != null;
}
@Override
public Callable findMethodOrField(ModelClass modelClass, String name) {
AnnotationClass annotationClass = (AnnotationClass)modelClass;
for (String methodName :
new String[]{"get" + StringUtils.capitalize(name),
"is" + StringUtils.capitalize(name), name}) {
ModelMethod[] methods = modelClass.getMethods(methodName, 0);
for (ModelMethod modelMethod : methods) {
AnnotationMethod method = (AnnotationMethod) modelMethod;
if (method.isPublic()) {
final AnnotationField backingField = findField(annotationClass, name, true);
final Callable result = new Callable(Callable.Type.METHOD, methodName,
method.getReturnType(null), true, isBindable(method) ||
(backingField != null && isBindable(backingField)));
L.d("backing field for %s is %s", result, backingField);
return result;
}
}
}
AnnotationField field = findField(annotationClass, name, false);
if (field != null && field.mField.getModifiers().contains(Modifier.PUBLIC)) {
AnnotationClass fieldType = new AnnotationClass(field.mField.asType());
return new Callable(Callable.Type.FIELD, name, fieldType,
!field.mField.getModifiers().contains(Modifier.FINAL)
|| isObservable(fieldType), isBindable(field));
}
throw new IllegalArgumentException(
"cannot find " + name + " in " +
((DeclaredType) annotationClass.mTypeMirror).asElement().getSimpleName());
}
private AnnotationField findField(AnnotationClass clazz, String name, boolean allowNonPublic) {
if (clazz == null || clazz.mTypeMirror.getKind() != TypeKind.DECLARED) {
return null;
}
TypeElement typeElement = (TypeElement) ((DeclaredType) clazz.mTypeMirror).asElement();
for (VariableElement field : ElementFilter.fieldsIn(
getElementUtils().getAllMembers(typeElement))) {
if (name.equals(stripFieldName(field.getSimpleName().toString()))) {
if (allowNonPublic || field.getModifiers().contains(Modifier.PUBLIC)) {
return new AnnotationField(typeElement, field);
}
}
}
return null; // nothing found
}
@Override
public AnnotationClass loadPrimitive(String className) {
TypeKind typeKind = PRIMITIVE_TYPES.get(className);
if (typeKind == null) {
return null;
} else {
Types typeUtils = getTypeUtils();
return new AnnotationClass(typeUtils.getPrimitiveType(typeKind));
}
}
private static String stripFieldName(String fieldName) {
if (fieldName.length() > 2) {
final char start = fieldName.charAt(2);
if (fieldName.startsWith("m_") && Character.isJavaIdentifierStart(start)) {
return Character.toLowerCase(start) + fieldName.substring(3);
}
}
if (fieldName.length() > 1) {
final char start = fieldName.charAt(1);
final char fieldIdentifier = fieldName.charAt(0);
final boolean strip;
if (fieldIdentifier == '_') {
strip = true;
} else if (fieldIdentifier == 'm' && Character.isJavaIdentifierStart(start) &&
!Character.isLowerCase(start)) {
strip = true;
} else {
strip = false; // not mUppercase format
}
if (strip) {
return Character.toLowerCase(start) + fieldName.substring(2);
}
}
return fieldName;
}
@Override
public AnnotationClass findClass(String className) {
className = className.trim();
int numDimensions = 0;
while (className.endsWith("[]")) {
numDimensions++;
className = className.substring(0, className.length() - 2);
}
AnnotationClass primitive = loadPrimitive(className);
if (primitive != null) {
return addDimension(primitive.mTypeMirror, numDimensions);
}
int templateOpenIndex = className.indexOf('<');
DeclaredType declaredType;
if (templateOpenIndex < 0) {
Elements elementUtils = getElementUtils();
TypeElement typeElement = elementUtils.getTypeElement(className);
declaredType = (DeclaredType) typeElement.asType();
} else {
int templateCloseIndex = className.lastIndexOf('>');
String paramStr = className.substring(templateOpenIndex + 1, templateCloseIndex - 1);
Elements elementUtils = getElementUtils();
String baseClassName = className.substring(0, templateOpenIndex);
TypeElement typeElement = elementUtils.getTypeElement(baseClassName);
ArrayList<String> templateParameters = splitTemplateParameters(paramStr);
TypeMirror[] typeArgs = new TypeMirror[templateParameters.size()];
for (int i = 0; i < typeArgs.length; i++) {
typeArgs[i] = findClass(templateParameters.get(i)).mTypeMirror;
}
Types typeUtils = getTypeUtils();
declaredType = typeUtils.getDeclaredType(typeElement, typeArgs);
}
return addDimension(declaredType, numDimensions);
}
private AnnotationClass addDimension(TypeMirror type, int numDimensions) {
while (numDimensions > 0) {
type = getTypeUtils().getArrayType(type);
numDimensions--;
}
return new AnnotationClass(type);
}
private ArrayList<String> splitTemplateParameters(String templateParameters) {
ArrayList<String> list = new ArrayList<>();
int index = 0;
int openCount = 0;
StringBuilder arg = new StringBuilder();
while (index < templateParameters.length()) {
char c = templateParameters.charAt(index);
if (c == ',' && openCount == 0) {
list.add(arg.toString());
arg.delete(0, arg.length());
} else if (!Character.isWhitespace(c)) {
arg.append(c);
if (c == '<') {
openCount++;
} else if (c == '>') {
openCount--;
}
}
}
list.add(arg.toString());
return list;
}
@Override
public List<URL> getResources(String name) {
ArrayList<URL> urls = new ArrayList<>();
try {
Enumeration<URL> resources = getClass().getClassLoader().getResources(name);
while (resources.hasMoreElements()) {
urls.add(resources.nextElement());
}
} catch (IOException e) {
printMessage(Diagnostic.Kind.ERROR, "IOException while getting resources: " +
e.getLocalizedMessage());
}
return urls;
}
@Override
public ModelClass findClass(Class classType) {
return findClass(classType.getCanonicalName());
}
public Types getTypeUtils() {
return processingEnv.getTypeUtils();
}
public Elements getElementUtils() {
return processingEnv.getElementUtils();
}
public void printMessage(Diagnostic.Kind kind, String message) {
processingEnv.getMessager().printMessage(kind, message);
}
}

View File

@@ -0,0 +1,317 @@
/*
* 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.util.ArrayList;
import java.util.List;
import javax.lang.model.element.ExecutableElement;
import javax.lang.model.element.TypeElement;
import javax.lang.model.element.VariableElement;
import javax.lang.model.type.ArrayType;
import javax.lang.model.type.DeclaredType;
import javax.lang.model.type.PrimitiveType;
import javax.lang.model.type.TypeKind;
import javax.lang.model.type.TypeMirror;
import javax.lang.model.util.ElementFilter;
import javax.lang.model.util.Elements;
import javax.lang.model.util.Types;
import javax.tools.Diagnostic;
public class AnnotationClass implements ModelClass {
final TypeMirror mTypeMirror;
public AnnotationClass(TypeMirror typeMirror) {
mTypeMirror = typeMirror;
}
@Override
public String toJavaCode() {
return toJavaCode(mTypeMirror);
}
private static String toJavaCode(TypeMirror typeElement) {
return typeElement.toString();
}
@Override
public boolean isArray() {
return mTypeMirror.getKind() == TypeKind.ARRAY;
}
@Override
public AnnotationClass getComponentType() {
TypeMirror component;
if (isArray()) {
component = ((ArrayType) mTypeMirror).getComponentType();
} else if (isList()) {
DeclaredType listType = findInterface(getListType());
if (listType == null) {
return null;
}
component = listType.getTypeArguments().get(0);
} else {
DeclaredType mapType = findInterface(getMapType());
if (mapType == null) {
return null;
}
component = mapType.getTypeArguments().get(1);
}
return new AnnotationClass(component);
}
private DeclaredType findInterface(TypeMirror interfaceType) {
Types typeUtil = getTypeUtils();
TypeMirror foundInterface = null;
if (typeUtil.isSameType(interfaceType, typeUtil.erasure(mTypeMirror))) {
foundInterface = mTypeMirror;
} else if (mTypeMirror.getKind() == TypeKind.DECLARED) {
DeclaredType declaredType = (DeclaredType) mTypeMirror;
TypeElement typeElement = (TypeElement) declaredType.asElement();
for (TypeMirror type : typeElement.getInterfaces()) {
if (typeUtil.isSameType(interfaceType, typeUtil.erasure(type))) {
foundInterface = type;
break;
}
}
if (foundInterface == null) {
printMessage(Diagnostic.Kind.ERROR,
"Detected " + interfaceType + " type for " + mTypeMirror +
", but not able to find the implemented interface.");
return null;
}
}
if (foundInterface.getKind() != TypeKind.DECLARED) {
printMessage(Diagnostic.Kind.ERROR,
"Found " + interfaceType + " type for " + mTypeMirror +
", but it isn't a declared type: " + foundInterface);
return null;
}
return (DeclaredType) foundInterface;
}
@Override
public boolean isList() {
AnnotationAnalyzer analyzer = AnnotationAnalyzer.instance;
Types typeUtil = getTypeUtils();
return typeUtil.isAssignable(typeUtil.erasure(mTypeMirror), getListType());
}
@Override
public boolean isMap() {
AnnotationAnalyzer analyzer = AnnotationAnalyzer.instance;
Types typeUtil = getTypeUtils();
return typeUtil.isAssignable(typeUtil.erasure(mTypeMirror), getMapType());
}
@Override
public boolean isString() {
return getTypeUtils().isSameType(mTypeMirror, getStringType());
}
@Override
public boolean isNullable() {
switch (mTypeMirror.getKind()) {
case ARRAY:
case DECLARED:
case NULL:
return true;
default:
return false;
}
}
@Override
public boolean isPrimitive() {
switch (mTypeMirror.getKind()) {
case BOOLEAN:
case BYTE:
case SHORT:
case INT:
case LONG:
case CHAR:
case FLOAT:
case DOUBLE:
return true;
default:
return false;
}
}
@Override
public boolean isBoolean() {
return mTypeMirror.getKind() == TypeKind.BOOLEAN;
}
@Override
public boolean isChar() {
return mTypeMirror.getKind() == TypeKind.CHAR;
}
@Override
public boolean isByte() {
return mTypeMirror.getKind() == TypeKind.BYTE;
}
@Override
public boolean isShort() {
return mTypeMirror.getKind() == TypeKind.SHORT;
}
@Override
public boolean isInt() {
return mTypeMirror.getKind() == TypeKind.INT;
}
@Override
public boolean isLong() {
return mTypeMirror.getKind() == TypeKind.LONG;
}
@Override
public boolean isFloat() {
return mTypeMirror.getKind() == TypeKind.FLOAT;
}
@Override
public boolean isDouble() {
return mTypeMirror.getKind() == TypeKind.DOUBLE;
}
@Override
public boolean isObject() {
return getTypeUtils().isSameType(mTypeMirror, getObjectType());
}
@Override
public boolean isVoid() {
return mTypeMirror.getKind() == TypeKind.VOID;
}
@Override
public AnnotationClass unbox() {
if (!isNullable()) {
return this;
}
try {
return new AnnotationClass(getTypeUtils().unboxedType(mTypeMirror));
} catch (IllegalArgumentException e) {
// I'm being lazy. This is much easier than checking every type.
return this;
}
}
@Override
public ModelClass box() {
if (!isPrimitive()) {
return this;
}
return new AnnotationClass(getTypeUtils().boxedClass((PrimitiveType) mTypeMirror).asType());
}
@Override
public boolean isAssignableFrom(ModelClass that) {
TypeMirror thatType = ((AnnotationClass)that).mTypeMirror;
return getTypeUtils().isAssignable(thatType, mTypeMirror);
}
@Override
public ModelMethod[] getMethods(String name, int numParameters) {
ArrayList<AnnotationMethod> matching = new ArrayList<>();
if (mTypeMirror.getKind() == TypeKind.DECLARED) {
DeclaredType declaredType = (DeclaredType) mTypeMirror;
getMethods(declaredType, matching, name, numParameters);
}
return matching.toArray(new ModelMethod[matching.size()]);
}
private static void getMethods(DeclaredType declaredType, ArrayList<AnnotationMethod> methods,
String name, int numParameters) {
Elements elementUtils = getElementUtils();
for (ExecutableElement element :
ElementFilter.methodsIn(elementUtils.getAllMembers((TypeElement)declaredType.asElement()))) {
if (element.getSimpleName().toString().equals(name)) {
List<? extends VariableElement> parameters = element.getParameters();
if (parameters.size() == numParameters ||
(element.isVarArgs() && parameters.size() <= numParameters - 1)) {
methods.add(new AnnotationMethod(declaredType, element));
}
}
}
}
@Override
public AnnotationClass getSuperclass() {
if (mTypeMirror.getKind() == TypeKind.DECLARED) {
DeclaredType declaredType = (DeclaredType) mTypeMirror;
TypeElement typeElement = (TypeElement) declaredType.asElement();
TypeMirror superClass = typeElement.getSuperclass();
if (superClass.getKind() == TypeKind.DECLARED) {
return new AnnotationClass(superClass);
}
}
return null;
}
@Override
public boolean equals(Object obj) {
if (obj instanceof AnnotationClass) {
return getTypeUtils().isSameType(mTypeMirror, ((AnnotationClass) obj).mTypeMirror);
} else {
return false;
}
}
@Override
public int hashCode() {
return mTypeMirror.toString().hashCode();
}
private static Types getTypeUtils() {
return AnnotationAnalyzer.instance.processingEnv.getTypeUtils();
}
private static Elements getElementUtils() {
return AnnotationAnalyzer.instance.processingEnv.getElementUtils();
}
private static TypeMirror getListType() {
return AnnotationAnalyzer.instance.listType;
}
private static TypeMirror getMapType() {
return AnnotationAnalyzer.instance.mapType;
}
private static TypeMirror getStringType() {
return AnnotationAnalyzer.instance.stringType;
}
private static TypeMirror getObjectType() {
return AnnotationAnalyzer.instance.objectType;
}
private static void printMessage(Diagnostic.Kind kind, String message) {
AnnotationAnalyzer.instance.printMessage(kind, message);
}
@Override
public String toString() {
return mTypeMirror.toString();
}
}

View File

@@ -0,0 +1,47 @@
/*
* 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 javax.lang.model.element.TypeElement;
import javax.lang.model.element.VariableElement;
public class AnnotationField implements ModelField {
final VariableElement mField;
final TypeElement mDeclaredClass;
public AnnotationField(TypeElement declaredClass, VariableElement field) {
mDeclaredClass = declaredClass;
mField = field;
}
@Override
public String toString() {
return mField.toString();
}
@Override
public boolean equals(Object obj) {
if (obj instanceof AnnotationField) {
AnnotationField that = (AnnotationField) obj;
return mDeclaredClass.equals(that.mDeclaredClass) && AnnotationAnalyzer.instance
.getTypeUtils().isSameType(mField.asType(), that.mField.asType());
} else {
return false;
}
}
}

View File

@@ -0,0 +1,74 @@
/*
* 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.util.List;
import javax.lang.model.element.ExecutableElement;
import javax.lang.model.element.Modifier;
import javax.lang.model.element.VariableElement;
import javax.lang.model.type.DeclaredType;
import javax.lang.model.type.ExecutableType;
import javax.lang.model.type.TypeMirror;
public class AnnotationMethod implements ModelMethod {
final ExecutableElement mMethod;
final DeclaredType mDeclaringType;
public AnnotationMethod(DeclaredType declaringType, ExecutableElement method) {
mDeclaringType = declaringType;
mMethod = method;
}
@Override
public ModelClass getDeclaringClass() {
return new AnnotationClass(mDeclaringType);
}
@Override
public ModelClass[] getParameterTypes() {
List<? extends VariableElement> parameters = mMethod.getParameters();
ModelClass[] parameterTypes = new ModelClass[parameters.size()];
for (int i = 0; i < parameters.size(); i++) {
parameterTypes[i] = new AnnotationClass(parameters.get(i).asType());
}
return parameterTypes;
}
@Override
public String getName() {
return mMethod.getSimpleName().toString();
}
@Override
public ModelClass getReturnType(List<ModelClass> args) {
ExecutableType executableType = (ExecutableType) AnnotationAnalyzer.instance.getTypeUtils().asMemberOf(mDeclaringType, mMethod);
TypeMirror returnType = executableType.getReturnType();
// TODO: support argument-supplied types
// for example: public T[] toArray(T[] arr)
return new AnnotationClass(returnType);
}
@Override
public boolean isPublic() {
return mMethod.getModifiers().contains(Modifier.PUBLIC);
}
@Override
public boolean isStatic() {
return mMethod.getModifiers().contains(Modifier.STATIC);
}
}

View File

@@ -26,13 +26,13 @@ public class Callable {
public final String name;
public final ReflectionClass resolvedType;
public final ModelClass resolvedType;
public final boolean isDynamic;
public final boolean canBeInvalidated;
public Callable(Type type, String name, ReflectionClass resolvedType, boolean isDynamic,
public Callable(Type type, String name, ModelClass resolvedType, boolean isDynamic,
boolean canBeInvalidated) {
this.type = type;
this.name = name;

View File

@@ -1,371 +0,0 @@
/*
* 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 com.google.common.base.Preconditions;
import com.google.common.collect.ImmutableMap;
import com.android.databinding.store.SetterStore;
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 extends ReflectionAnalyzer {
private static final String OBSERVABLE_CLASS_NAME = "android.binding.Observable";
private static final String OBSERVABLE_LIST_CLASS_NAME = "android.binding.ObservableList";
private static final String OBSERVABLE_MAP_CLASS_NAME = "android.binding.ObservableMap";
private static final String BINDABLE_ANNOTATION_NAME = "android.binding.Bindable";
private static final String[] OBSERVABLE_FIELDS = {
"com.android.databinding.library.ObservableBoolean",
"com.android.databinding.library.ObservableByte",
"com.android.databinding.library.ObservableChar",
"com.android.databinding.library.ObservableShort",
"com.android.databinding.library.ObservableInt",
"com.android.databinding.library.ObservableLong",
"com.android.databinding.library.ObservableFloat",
"com.android.databinding.library.ObservableDouble",
"com.android.databinding.library.ObservableField",
};
private static final String I_VIEW_DATA_BINDER = "com.android.databinding.library.IViewDataBinder";
private static Map<String, String> sTestClassNameMapping = ImmutableMap.of(
OBSERVABLE_CLASS_NAME, "com.android.databinding.MockObservable",
BINDABLE_ANNOTATION_NAME, "com.android.databinding.MockBindable",
OBSERVABLE_LIST_CLASS_NAME, "com.android.databinding.MockObservableLsit",
OBSERVABLE_MAP_CLASS_NAME, "com.android.databinding.MockObservableMap",
I_VIEW_DATA_BINDER, "com.android.databinding.MockIViewDataBinder"
);
private static ClassAnalyzer sClassAnalyzer;
private static ClassLoader sClassLoader;
private HashMap<String, ClassClass> mClassCache = new HashMap<>();
private final ClassLoader mClassLoader;
private final Class mObservable;
private final Class mObservableList;
private final Class mObservableMap;
private final Class[] mObservableFields;
private final Class mBindable;
private final boolean mTestMode;
private final Class mIViewDataBinder;
private ClassAnalyzer(ClassLoader classLoader, boolean testMode) {
mClassLoader = classLoader;
mTestMode = testMode;
try {
mIViewDataBinder = classLoader.loadClass(getClassName(I_VIEW_DATA_BINDER));
mObservable = classLoader.loadClass(getClassName(OBSERVABLE_CLASS_NAME));
mObservableList = classLoader.loadClass(getClassName(OBSERVABLE_LIST_CLASS_NAME));
mObservableMap = classLoader.loadClass(getClassName(OBSERVABLE_MAP_CLASS_NAME));
mBindable = classLoader.loadClass(getClassName(BINDABLE_ANNOTATION_NAME));
mObservableFields = new Class[OBSERVABLE_FIELDS.length];
for (int i = 0; i < OBSERVABLE_FIELDS.length; i++) {
mObservableFields[i] = classLoader.loadClass(getClassName(OBSERVABLE_FIELDS[i]));
}
} catch (ClassNotFoundException e) {
throw new RuntimeException(e);
}
}
public static void setClassLoader(ClassLoader classLoader) {
if (sClassAnalyzer != null) {
throw new IllegalStateException("class analyzer is already created, you cannot "
+ "change class loader after that");
}
L.d("setting class loader to %s", classLoader);
sClassLoader = classLoader;
}
public static ClassAnalyzer getInstance() {
if (sClassAnalyzer == null) {
Preconditions.checkNotNull(sClassLoader,
"cannot access class analyzer before class loader is ready");
sClassAnalyzer = new ClassAnalyzer(sClassLoader, false);
}
return sClassAnalyzer;
}
private String getClassName(String name) {
if (mTestMode && sTestClassNameMapping.containsKey(name)) {
return sTestClassNameMapping.get(name);
}
return name;
}
@Override
public boolean isDataBinder(ReflectionClass reflectionClass) {
ClassClass classClass = (ClassClass) reflectionClass;
return mIViewDataBinder.isAssignableFrom(classClass.mClass);
}
@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,
new ClassClass(method.getReturnType()), true, false);
}
}
}
L.e(new Exception(), "cannot find method %s in %s", name, klass);
throw new IllegalArgumentException(
"cannot find method " + name + " at class " + klass.getSimpleName());
}
@Override
public boolean isObservable(ReflectionClass reflectionClass) {
Class klass = ((ClassClass) reflectionClass).mClass;
return isObservable(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;
}
}
return false;
}
@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;
}
private boolean isBindable(Method method) {
return method.getAnnotation(mBindable) != null;
}
@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}) {
try {
Method method = klass.getMethod(methodName);
Field backingField = findField(klass, name, true);
if (Modifier.isPublic(method.getModifiers())) {
final Callable result = new Callable(Callable.Type.METHOD, methodName,
new ClassClass(method.getReturnType()), true,
isBindable(method) || (backingField != null && isBindable(backingField)) );
L.d("backing field for %s is %s", result, backingField);
return result;
}
} catch (Throwable t) {
}
}
try {
Field field = findField(klass, name, false);
if (Modifier.isPublic(field.getModifiers())) {
return new Callable(Callable.Type.FIELD, name, new ClassClass(field.getType()),
!Modifier.isFinal(field.getModifiers())
|| isObservable(field.getType()), isBindable(field));
}
} catch (Throwable t) {
}
throw new IllegalArgumentException(
"cannot find " + name + " in " + klass.getCanonicalName());
}
private Field findField(Class klass, String name, boolean allowNonPublic) {
try {
return getField(klass, name, allowNonPublic);
} catch (NoSuchFieldException e) {
}
String capitalizedName = StringUtils.capitalize(name);
try {
return getField(klass, "m" + capitalizedName, allowNonPublic);
} catch (Throwable t){}
try {
return getField(klass, "_" + name, allowNonPublic);
} catch (Throwable t){}
try {
return getField(klass, "_" + capitalizedName, allowNonPublic);
} catch (Throwable t){}
try {
return getField(klass, "m_" + name, allowNonPublic);
} catch (Throwable t){}
try {
return getField(klass, "m_" + capitalizedName, allowNonPublic);
} catch (Throwable t){}
return null;
}
private Field getField(Class klass, String exactName, boolean allowNonPublic)
throws NoSuchFieldException {
try {
return klass.getField(exactName);
} catch (NoSuchFieldException e) {
if (allowNonPublic) {
return klass.getDeclaredField(exactName);
} else {
throw e;
}
}
}
public ClassClass loadPrimitive(String className) {
if ("int".equals(className)) {
return new ClassClass(int.class);
}
if ("short".equals(className)) {
return new ClassClass(short.class);
}
if ("long".equals(className)) {
return new ClassClass(long.class);
}
if ("float".equals(className)) {
return new ClassClass(float.class);
}
if ("double".equals(className)) {
return new ClassClass(double.class);
}
if ("boolean".equals(className) || "bool".equals(className)) {
return new ClassClass(boolean.class);
}
return null;
}
@Override
public ClassClass findClass(String className) {
ClassClass loaded = mClassCache.get(className);
if (loaded != null) {
return loaded;
}
L.d("trying to load class %s from %s", className, mClassLoader.toString());
loaded = loadPrimitive(className);
if (loaded == null) {
try {
if (className.startsWith("[") && className.contains("L")) {
int indexOfL = className.indexOf('L');
ClassClass baseClass = findClass(
className.substring(indexOfL + 1, className.length() - 1));
String realClassName = className.substring(0, indexOfL + 1) +
baseClass.mClass.getCanonicalName() + ';';
loaded = new ClassClass(Class.forName(realClassName, false, mClassLoader));
mClassCache.put(className, loaded);
} else {
loaded = loadRecursively(className);
mClassCache.put(className, loaded);
}
} catch (Throwable t) {
L.e(t, "cannot load class " + className);
}
}
Preconditions.checkNotNull(loaded, "Tried to load " + className + " but could not find :/");
L.d("loaded class %s", loaded.mClass.getCanonicalName());
return loaded;
}
@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 new ClassClass(mClassLoader.loadClass(className));
} catch (ClassNotFoundException ex) {
int lastIndexOfDot = className.lastIndexOf(".");
if (lastIndexOfDot == -1) {
throw ex;
}
return loadRecursively(className.substring(0, lastIndexOfDot) + "$" + className
.substring(lastIndexOfDot + 1));
}
}
public static void initForTests() {
if (sClassAnalyzer == null) {
setClassLoader(ClassLoader.getSystemClassLoader());
sClassAnalyzer = new ClassAnalyzer(sClassLoader, true);
}
}
@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

@@ -1,230 +0,0 @@
/*
* 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

@@ -1,27 +0,0 @@
/*
* 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

@@ -1,65 +0,0 @@
/*
* 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;
import java.util.List;
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(List<ReflectionClass> args) {
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

@@ -15,381 +15,92 @@
*/
package com.android.databinding.reflection;
import com.google.common.collect.ImmutableMap;
import com.google.common.base.Preconditions;
import com.android.databinding.util.L;
import org.apache.commons.lang3.StringUtils;
import android.binding.Bindable;
import java.io.IOException;
import java.net.URL;
import java.util.ArrayList;
import java.util.Enumeration;
import java.util.List;
import java.util.Map;
import javax.annotation.processing.ProcessingEnvironment;
import javax.lang.model.element.Element;
import javax.lang.model.element.ElementKind;
import javax.lang.model.element.ExecutableElement;
import javax.lang.model.element.Modifier;
import javax.lang.model.element.TypeElement;
import javax.lang.model.element.VariableElement;
import javax.lang.model.type.DeclaredType;
import javax.lang.model.type.TypeKind;
import javax.lang.model.type.TypeMirror;
import javax.lang.model.util.ElementFilter;
import javax.lang.model.util.Elements;
import javax.lang.model.util.Types;
import javax.tools.Diagnostic;
import javax.tools.FileObject;
import javax.tools.StandardLocation;
public class ModelAnalyzer extends ReflectionAnalyzer {
public abstract class ModelAnalyzer {
private static ModelAnalyzer sAnalyzer;
public static final String LIST_CLASS_NAME = "java.util.List";
public abstract boolean isDataBinder(ModelClass modelClass);
public static final String MAP_CLASS_NAME = "java.util.Map";
public abstract Callable findMethod(ModelClass modelClass, String name,
List<ModelClass> args);
public static final String STRING_CLASS_NAME = "java.lang.String";
public abstract boolean isObservable(ModelClass modelClass);
public static final String OBJECT_CLASS_NAME = "java.lang.Object";
public abstract boolean isObservableField(ModelClass modelClass);
static ModelAnalyzer instance;
private static final String OBSERVABLE_CLASS_NAME = "android.binding.Observable";
private static final String OBSERVABLE_LIST_CLASS_NAME = "android.binding.ObservableList";
private static final String OBSERVABLE_MAP_CLASS_NAME = "android.binding.ObservableMap";
private static final String[] OBSERVABLE_FIELDS = {
"com.android.databinding.library.ObservableBoolean",
"com.android.databinding.library.ObservableByte",
"com.android.databinding.library.ObservableChar",
"com.android.databinding.library.ObservableShort",
"com.android.databinding.library.ObservableInt",
"com.android.databinding.library.ObservableLong",
"com.android.databinding.library.ObservableFloat",
"com.android.databinding.library.ObservableDouble",
"com.android.databinding.library.ObservableField",
};
private static final String I_VIEW_DATA_BINDER = "com.android.databinding.library.IViewDataBinder";
private static final Map<String, TypeKind> PRIMITIVE_TYPES =
new ImmutableMap.Builder<String, TypeKind>()
.put("boolean", TypeKind.BOOLEAN)
.put("byte", TypeKind.BYTE)
.put("short", TypeKind.SHORT)
.put("char", TypeKind.CHAR)
.put("int", TypeKind.INT)
.put("long", TypeKind.LONG)
.put("float", TypeKind.FLOAT)
.put("double", TypeKind.DOUBLE)
.build();
public abstract boolean isBindable(ModelField field);
public final ProcessingEnvironment processingEnv;
public final TypeMirror listType;
public final TypeMirror mapType;
public final TypeMirror stringType;
public final TypeMirror objectType;
public abstract boolean isBindable(ModelMethod method);
private final ModelClass mObservableType;
private final ModelClass mObservableListType;
private final ModelClass mObservableMapType;
private final ModelClass[] mObservableFieldTypes;
private final ModelClass mIViewDataBinderType;
public abstract Callable findMethodOrField(ModelClass modelClass, String name);
public ModelAnalyzer(ProcessingEnvironment processingEnvironment) {
processingEnv = processingEnvironment;
instance = this;
Types typeUtil = processingEnv.getTypeUtils();
listType = typeUtil.erasure(findType(LIST_CLASS_NAME).asType());
mapType = typeUtil.erasure(findType(MAP_CLASS_NAME).asType());
stringType = typeUtil.erasure(findType(STRING_CLASS_NAME).asType());
objectType = typeUtil.erasure(findType(OBJECT_CLASS_NAME).asType());
mObservableType = new ModelClass(findType(OBSERVABLE_CLASS_NAME).asType());
mObservableListType = new ModelClass(typeUtil.erasure(findType(OBSERVABLE_LIST_CLASS_NAME).asType()));
mObservableMapType = new ModelClass(typeUtil.erasure(
findType(OBSERVABLE_MAP_CLASS_NAME).asType()));
mIViewDataBinderType = new ModelClass(findType(I_VIEW_DATA_BINDER).asType());
mObservableFieldTypes = new ModelClass[OBSERVABLE_FIELDS.length];
for (int i = 0; i < OBSERVABLE_FIELDS.length; i++) {
mObservableFieldTypes[i] = new ModelClass(findType(OBSERVABLE_FIELDS[i]).asType());
public ModelClass findCommonParentOf(ModelClass modelClass1,
ModelClass modelClass2) {
ModelClass curr = modelClass1;
while (curr != null && !curr.isAssignableFrom(modelClass2)) {
curr = curr.getSuperclass();
}
}
TypeElement findType(String type) {
return processingEnv.getElementUtils().getTypeElement(type);
}
@Override
public boolean isDataBinder(ReflectionClass reflectionClass) {
return mIViewDataBinderType.isAssignableFrom(reflectionClass);
}
@Override
public ReflectionClass findCommonParentOf(ReflectionClass reflectionClass1,
ReflectionClass reflectionClass2) {
return super.findCommonParentOf(toModel(reflectionClass1), toModel(reflectionClass2));
}
@Override
public Callable findMethod(ReflectionClass reflectionClass, String name,
List<ReflectionClass> args) {
ModelClass clazz = toModel(reflectionClass);
// TODO implement properly
for (String methodName : new String[]{"set" + StringUtils.capitalize(name), name}) {
for (ReflectionMethod method : clazz.getMethods(methodName, args.size())) {
ReflectionClass[] parameters = method.getParameterTypes();
boolean parametersMatch = true;
boolean isVarArgs = ((ModelMethod)method).mMethod.isVarArgs();
for (int i = 0; i < parameters.length; i++) {
if (isVarArgs && i == parameters.length - 1) {
ReflectionClass component = parameters[i].getComponentType();
for (int j = i; j < args.size(); j++) {
if (!component.isAssignableFrom(args.get(j))) {
parametersMatch = false;
break;
}
}
} else if (!parameters[i].isAssignableFrom(args.get(i))) {
parametersMatch = false;
break;
}
}
if (parametersMatch) {
return new Callable(Callable.Type.METHOD, methodName,
method.getReturnType(args), true, false);
}
if (curr == null) {
ModelClass primitive1 = modelClass1.unbox();
ModelClass primitive2 = modelClass2.unbox();
if (!modelClass1.equals(primitive1) || !modelClass2.equals(primitive2)) {
return findCommonParentOf(primitive1, primitive2);
}
}
String message = "cannot find method " + name + " at class " + clazz.toJavaCode();
printMessage(Diagnostic.Kind.ERROR, message);
throw new IllegalArgumentException(message);
Preconditions.checkNotNull(curr,
"must be able to find a common parent for " + modelClass1 + " and " + modelClass2);
return curr;
}
@Override
public boolean isObservable(ReflectionClass reflectionClass) {
return mObservableType.isAssignableFrom(reflectionClass) ||
mObservableListType.isAssignableFrom(reflectionClass) ||
mObservableMapType.isAssignableFrom(reflectionClass);
public abstract ModelClass loadPrimitive(String className);
public static ModelAnalyzer getInstance() {
return sAnalyzer;
}
@Override
public boolean isObservableField(ReflectionClass reflectionClass) {
ModelClass modelClass = toModel(reflectionClass);
ModelClass erasure = new ModelClass(getTypeUtils().erasure(modelClass.mTypeMirror));
for (ModelClass observableField : mObservableFieldTypes) {
if (observableField.isAssignableFrom(erasure)) {
return true;
}
public static void setProcessingEnvironment(ProcessingEnvironment processingEnvironment) {
AnnotationAnalyzer annotationAnalyzer = new AnnotationAnalyzer(processingEnvironment);
sAnalyzer = annotationAnalyzer;
}
public String getDefaultValue(String className) {
if("int".equals(className)) {
return "0";
}
return false;
}
@Override
public boolean isBindable(ReflectionField field) {
VariableElement fieldElement = ((ModelField) field).mField;
return fieldElement.getAnnotation(Bindable.class) != null;
}
@Override
public boolean isBindable(ReflectionMethod method) {
ExecutableElement methodElement = ((ModelMethod) method).mMethod;
return methodElement.getAnnotation(Bindable.class) != null;
}
@Override
public Callable findMethodOrField(ReflectionClass reflectionClass, String name) {
ModelClass modelClass = toModel(reflectionClass);
for (String methodName :
new String[]{"get" + StringUtils.capitalize(name),
"is" + StringUtils.capitalize(name), name}) {
ReflectionMethod[] methods = reflectionClass.getMethods(methodName, 0);
for (ReflectionMethod reflectionMethod : methods) {
ModelMethod method = (ModelMethod) reflectionMethod;
if (method.isPublic()) {
final ModelField backingField = findField(modelClass, name, true);
final Callable result = new Callable(Callable.Type.METHOD, methodName,
method.getReturnType(null), true, isBindable(method) ||
(backingField != null && isBindable(backingField)));
L.d("backing field for %s is %s", result, backingField);
return result;
}
}
if("short".equals(className)) {
return "0";
}
ModelField field = findField(modelClass, name, false);
if (field != null && field.mField.getModifiers().contains(Modifier.PUBLIC)) {
ModelClass fieldType = new ModelClass(field.mField.asType());
return new Callable(Callable.Type.FIELD, name, fieldType,
!field.mField.getModifiers().contains(Modifier.FINAL)
|| isObservable(fieldType), isBindable(field));
if("long".equals(className)) {
return "0L";
}
throw new IllegalArgumentException(
"cannot find " + name + " in " +
((DeclaredType)modelClass.mTypeMirror).asElement().getSimpleName());
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";
}
private ModelField findField(ModelClass clazz, String name, boolean allowNonPublic) {
if (clazz == null || clazz.mTypeMirror.getKind() != TypeKind.DECLARED) {
return null;
}
TypeElement typeElement = (TypeElement) ((DeclaredType) clazz.mTypeMirror).asElement();
for (VariableElement field : ElementFilter.fieldsIn(
getElementUtils().getAllMembers(typeElement))) {
if (name.equals(stripFieldName(field.getSimpleName().toString()))) {
if (allowNonPublic || field.getModifiers().contains(Modifier.PUBLIC)) {
return new ModelField(typeElement, field);
}
}
}
return null; // nothing found
}
public abstract ModelClass findClass(String className);
@Override
public ModelClass loadPrimitive(String className) {
TypeKind typeKind = PRIMITIVE_TYPES.get(className);
if (typeKind == null) {
return null;
} else {
Types typeUtils = getTypeUtils();
return new ModelClass(typeUtils.getPrimitiveType(typeKind));
}
}
public abstract List<URL> getResources(String name);
private static String stripFieldName(String fieldName) {
if (fieldName.length() > 2) {
final char start = fieldName.charAt(2);
if (fieldName.startsWith("m_") && Character.isJavaIdentifierStart(start)) {
return Character.toLowerCase(start) + fieldName.substring(3);
}
}
if (fieldName.length() > 1) {
final char start = fieldName.charAt(1);
final char fieldIdentifier = fieldName.charAt(0);
final boolean strip;
if (fieldIdentifier == '_') {
strip = true;
} else if (fieldIdentifier == 'm' && Character.isJavaIdentifierStart(start) &&
!Character.isLowerCase(start)) {
strip = true;
} else {
strip = false; // not mUppercase format
}
if (strip) {
return Character.toLowerCase(start) + fieldName.substring(2);
}
}
return fieldName;
}
@Override
public ModelClass findClass(String className) {
className = className.trim();
int numDimensions = 0;
while (className.endsWith("[]")) {
numDimensions++;
className = className.substring(0, className.length() - 2);
}
ModelClass primitive = loadPrimitive(className);
if (primitive != null) {
return addDimension(primitive.mTypeMirror, numDimensions);
}
int templateOpenIndex = className.indexOf('<');
DeclaredType declaredType;
if (templateOpenIndex < 0) {
Elements elementUtils = getElementUtils();
TypeElement typeElement = elementUtils.getTypeElement(className);
declaredType = (DeclaredType) typeElement.asType();
} else {
int templateCloseIndex = className.lastIndexOf('>');
String paramStr = className.substring(templateOpenIndex + 1, templateCloseIndex - 1);
Elements elementUtils = getElementUtils();
String baseClassName = className.substring(0, templateOpenIndex);
TypeElement typeElement = elementUtils.getTypeElement(baseClassName);
ArrayList<String> templateParameters = splitTemplateParameters(paramStr);
TypeMirror[] typeArgs = new TypeMirror[templateParameters.size()];
for (int i = 0; i < typeArgs.length; i++) {
typeArgs[i] = findClass(templateParameters.get(i)).mTypeMirror;
}
Types typeUtils = getTypeUtils();
declaredType = typeUtils.getDeclaredType(typeElement, typeArgs);
}
return addDimension(declaredType, numDimensions);
}
private ModelClass addDimension(TypeMirror type, int numDimensions) {
while (numDimensions > 0) {
type = getTypeUtils().getArrayType(type);
numDimensions--;
}
return new ModelClass(type);
}
private ArrayList<String> splitTemplateParameters(String templateParameters) {
ArrayList<String> list = new ArrayList<>();
int index = 0;
int openCount = 0;
StringBuilder arg = new StringBuilder();
while (index < templateParameters.length()) {
char c = templateParameters.charAt(index);
if (c == ',' && openCount == 0) {
list.add(arg.toString());
arg.delete(0, arg.length());
} else if (!Character.isWhitespace(c)) {
arg.append(c);
if (c == '<') {
openCount++;
} else if (c == '>') {
openCount--;
}
}
}
list.add(arg.toString());
return list;
}
@Override
public List<URL> getResources(String name) {
ArrayList<URL> urls = new ArrayList<>();
try {
Enumeration<URL> resources = getClass().getClassLoader().getResources(name);
while (resources.hasMoreElements()) {
urls.add(resources.nextElement());
}
} catch (IOException e) {
printMessage(Diagnostic.Kind.ERROR, "IOException while getting resources: " +
e.getLocalizedMessage());
}
return urls;
}
@Override
public ReflectionClass findClass(Class classType) {
return new ClassClass(classType);
}
public Types getTypeUtils() {
return processingEnv.getTypeUtils();
}
public Elements getElementUtils() {
return processingEnv.getElementUtils();
}
public void printMessage(Diagnostic.Kind kind, String message) {
processingEnv.getMessager().printMessage(kind, message);
}
public ModelClass toModel(ReflectionClass reflectionClass) {
if (reflectionClass instanceof ClassClass) {
String name = ((ClassClass) reflectionClass).mClass.getCanonicalName();
return findClass(name);
} else {
return (ModelClass) reflectionClass;
}
}
public abstract ModelClass findClass(Class classType);
}

View File

@@ -15,305 +15,51 @@
*/
package com.android.databinding.reflection;
import java.util.ArrayList;
import java.util.List;
public interface ModelClass {
import javax.lang.model.element.Element;
import javax.lang.model.element.ElementKind;
import javax.lang.model.element.ExecutableElement;
import javax.lang.model.element.TypeElement;
import javax.lang.model.element.VariableElement;
import javax.lang.model.type.ArrayType;
import javax.lang.model.type.DeclaredType;
import javax.lang.model.type.PrimitiveType;
import javax.lang.model.type.TypeKind;
import javax.lang.model.type.TypeMirror;
import javax.lang.model.util.ElementFilter;
import javax.lang.model.util.Elements;
import javax.lang.model.util.Types;
import javax.tools.Diagnostic;
String toJavaCode();
public class ModelClass implements ReflectionClass {
boolean isArray();
final TypeMirror mTypeMirror;
ModelClass getComponentType();
public ModelClass(TypeMirror typeMirror) {
mTypeMirror = typeMirror;
}
boolean isList();
@Override
public String toJavaCode() {
return toJavaCode(mTypeMirror);
}
boolean isMap();
private static String toJavaCode(TypeMirror typeElement) {
return typeElement.toString();
}
boolean isString();
@Override
public boolean isArray() {
return mTypeMirror.getKind() == TypeKind.ARRAY;
}
boolean isNullable();
@Override
public ModelClass getComponentType() {
TypeMirror component;
if (isArray()) {
component = ((ArrayType) mTypeMirror).getComponentType();
} else if (isList()) {
DeclaredType listType = findInterface(getListType());
if (listType == null) {
return null;
}
component = listType.getTypeArguments().get(0);
} else {
DeclaredType mapType = findInterface(getMapType());
if (mapType == null) {
return null;
}
component = mapType.getTypeArguments().get(1);
}
boolean isPrimitive();
return new ModelClass(component);
}
boolean isBoolean();
private DeclaredType findInterface(TypeMirror interfaceType) {
Types typeUtil = getTypeUtils();
TypeMirror foundInterface = null;
if (typeUtil.isSameType(interfaceType, typeUtil.erasure(mTypeMirror))) {
foundInterface = mTypeMirror;
} else if (mTypeMirror.getKind() == TypeKind.DECLARED) {
DeclaredType declaredType = (DeclaredType) mTypeMirror;
TypeElement typeElement = (TypeElement) declaredType.asElement();
for (TypeMirror type : typeElement.getInterfaces()) {
if (typeUtil.isSameType(interfaceType, typeUtil.erasure(type))) {
foundInterface = type;
break;
}
}
if (foundInterface == null) {
printMessage(Diagnostic.Kind.ERROR,
"Detected " + interfaceType + " type for " + mTypeMirror +
", but not able to find the implemented interface.");
return null;
}
}
if (foundInterface.getKind() != TypeKind.DECLARED) {
printMessage(Diagnostic.Kind.ERROR,
"Found " + interfaceType + " type for " + mTypeMirror +
", but it isn't a declared type: " + foundInterface);
return null;
}
return (DeclaredType) foundInterface;
}
boolean isChar();
@Override
public boolean isList() {
ModelAnalyzer analyzer = ModelAnalyzer.instance;
Types typeUtil = getTypeUtils();
return typeUtil.isAssignable(typeUtil.erasure(mTypeMirror), getListType());
}
boolean isByte();
@Override
public boolean isMap() {
ModelAnalyzer analyzer = ModelAnalyzer.instance;
Types typeUtil = getTypeUtils();
return typeUtil.isAssignable(typeUtil.erasure(mTypeMirror), getMapType());
}
boolean isShort();
@Override
public boolean isString() {
return getTypeUtils().isSameType(mTypeMirror, getStringType());
}
boolean isInt();
@Override
public boolean isNullable() {
switch (mTypeMirror.getKind()) {
case ARRAY:
case DECLARED:
case NULL:
return true;
default:
return false;
}
}
boolean isLong();
@Override
public boolean isPrimitive() {
switch (mTypeMirror.getKind()) {
case BOOLEAN:
case BYTE:
case SHORT:
case INT:
case LONG:
case CHAR:
case FLOAT:
case DOUBLE:
return true;
default:
return false;
}
}
boolean isFloat();
@Override
public boolean isBoolean() {
return mTypeMirror.getKind() == TypeKind.BOOLEAN;
}
boolean isDouble();
@Override
public boolean isChar() {
return mTypeMirror.getKind() == TypeKind.CHAR;
}
boolean isObject();
@Override
public boolean isByte() {
return mTypeMirror.getKind() == TypeKind.BYTE;
}
boolean isVoid();
@Override
public boolean isShort() {
return mTypeMirror.getKind() == TypeKind.SHORT;
}
ModelClass unbox();
@Override
public boolean isInt() {
return mTypeMirror.getKind() == TypeKind.INT;
}
ModelClass box();
@Override
public boolean isLong() {
return mTypeMirror.getKind() == TypeKind.LONG;
}
boolean isAssignableFrom(ModelClass that);
@Override
public boolean isFloat() {
return mTypeMirror.getKind() == TypeKind.FLOAT;
}
ModelMethod[] getMethods(String name, int numParameters);
@Override
public boolean isDouble() {
return mTypeMirror.getKind() == TypeKind.DOUBLE;
}
@Override
public boolean isObject() {
return getTypeUtils().isSameType(mTypeMirror, getObjectType());
}
@Override
public boolean isVoid() {
return mTypeMirror.getKind() == TypeKind.VOID;
}
@Override
public ModelClass unbox() {
if (!isNullable()) {
return this;
}
try {
return new ModelClass(getTypeUtils().unboxedType(mTypeMirror));
} catch (IllegalArgumentException e) {
// I'm being lazy. This is much easier than checking every type.
return this;
}
}
@Override
public ReflectionClass box() {
if (!isPrimitive()) {
return this;
}
return new ModelClass(getTypeUtils().boxedClass((PrimitiveType) mTypeMirror).asType());
}
@Override
public boolean isAssignableFrom(ReflectionClass that) {
TypeMirror thatType = ModelAnalyzer.instance.toModel(that).mTypeMirror;
return getTypeUtils().isAssignable(thatType, mTypeMirror);
}
@Override
public ReflectionMethod[] getMethods(String name, int numParameters) {
ArrayList<ModelMethod> matching = new ArrayList<>();
if (mTypeMirror.getKind() == TypeKind.DECLARED) {
DeclaredType declaredType = (DeclaredType) mTypeMirror;
getMethods(declaredType, matching, name, numParameters);
}
return matching.toArray(new ReflectionMethod[matching.size()]);
}
private static void getMethods(DeclaredType declaredType, ArrayList<ModelMethod> methods,
String name, int numParameters) {
Elements elementUtils = getElementUtils();
for (ExecutableElement element :
ElementFilter.methodsIn(elementUtils.getAllMembers((TypeElement)declaredType.asElement()))) {
if (element.getSimpleName().toString().equals(name)) {
List<? extends VariableElement> parameters = element.getParameters();
if (parameters.size() == numParameters ||
(element.isVarArgs() && parameters.size() <= numParameters - 1)) {
methods.add(new ModelMethod(declaredType, element));
}
}
}
}
@Override
public ModelClass getSuperclass() {
if (mTypeMirror.getKind() == TypeKind.DECLARED) {
DeclaredType declaredType = (DeclaredType) mTypeMirror;
TypeElement typeElement = (TypeElement) declaredType.asElement();
TypeMirror superClass = typeElement.getSuperclass();
if (superClass.getKind() == TypeKind.DECLARED) {
return new ModelClass(superClass);
}
}
return null;
}
@Override
public boolean equals(Object obj) {
if (obj instanceof ModelClass) {
return getTypeUtils().isSameType(mTypeMirror, ((ModelClass) obj).mTypeMirror);
} else {
return false;
}
}
@Override
public int hashCode() {
return mTypeMirror.toString().hashCode();
}
private static Types getTypeUtils() {
return ModelAnalyzer.instance.processingEnv.getTypeUtils();
}
private static Elements getElementUtils() {
return ModelAnalyzer.instance.processingEnv.getElementUtils();
}
private static TypeMirror getListType() {
return ModelAnalyzer.instance.listType;
}
private static TypeMirror getMapType() {
return ModelAnalyzer.instance.mapType;
}
private static TypeMirror getStringType() {
return ModelAnalyzer.instance.stringType;
}
private static TypeMirror getObjectType() {
return ModelAnalyzer.instance.objectType;
}
private static void printMessage(Diagnostic.Kind kind, String message) {
ModelAnalyzer.instance.printMessage(kind, message);
}
@Override
public String toString() {
return mTypeMirror.toString();
}
ModelClass getSuperclass();
}

View File

@@ -15,33 +15,5 @@
*/
package com.android.databinding.reflection;
import javax.lang.model.element.TypeElement;
import javax.lang.model.element.VariableElement;
public class ModelField implements ReflectionField {
final VariableElement mField;
final TypeElement mDeclaredClass;
public ModelField(TypeElement declaredClass, VariableElement field) {
mDeclaredClass = declaredClass;
mField = field;
}
@Override
public String toString() {
return mField.toString();
}
@Override
public boolean equals(Object obj) {
if (obj instanceof ModelField) {
ModelField that = (ModelField) obj;
return mDeclaredClass.equals(that.mDeclaredClass) && ModelAnalyzer.instance
.getTypeUtils().isSameType(mField.asType(), that.mField.asType());
} else {
return false;
}
}
public interface ModelField {
}

View File

@@ -17,62 +17,16 @@ package com.android.databinding.reflection;
import java.util.List;
import javax.lang.model.element.Element;
import javax.lang.model.element.ExecutableElement;
import javax.lang.model.element.Modifier;
import javax.lang.model.element.TypeElement;
import javax.lang.model.element.TypeParameterElement;
import javax.lang.model.element.VariableElement;
import javax.lang.model.type.DeclaredType;
import javax.lang.model.type.ExecutableType;
import javax.lang.model.type.TypeKind;
import javax.lang.model.type.TypeMirror;
public interface ModelMethod {
ModelClass getDeclaringClass();
public class ModelMethod implements ReflectionMethod {
final ExecutableElement mMethod;
final DeclaredType mDeclaringType;
ModelClass[] getParameterTypes();
public ModelMethod(DeclaredType declaringType, ExecutableElement method) {
mDeclaringType = declaringType;
mMethod = method;
}
String getName();
@Override
public ReflectionClass getDeclaringClass() {
return new ModelClass(mDeclaringType);
}
ModelClass getReturnType(List<ModelClass> args);
@Override
public ReflectionClass[] getParameterTypes() {
List<? extends VariableElement> parameters = mMethod.getParameters();
ReflectionClass[] parameterTypes = new ReflectionClass[parameters.size()];
for (int i = 0; i < parameters.size(); i++) {
parameterTypes[i] = new ModelClass(parameters.get(i).asType());
}
return parameterTypes;
}
boolean isPublic();
@Override
public String getName() {
return mMethod.getSimpleName().toString();
}
@Override
public ReflectionClass getReturnType(List<ReflectionClass> args) {
ExecutableType executableType = (ExecutableType) ModelAnalyzer.instance.getTypeUtils().asMemberOf(mDeclaringType, mMethod);
TypeMirror returnType = executableType.getReturnType();
// TODO: support argument-supplied types
// for example: public T[] toArray(T[] arr)
return new ModelClass(returnType);
}
@Override
public boolean isPublic() {
return mMethod.getModifiers().contains(Modifier.PUBLIC);
}
@Override
public boolean isStatic() {
return mMethod.getModifiers().contains(Modifier.STATIC);
}
boolean isStatic();
}

View File

@@ -1,111 +0,0 @@
/*
* 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 com.google.common.base.Preconditions;
import java.net.URL;
import java.util.List;
import javax.annotation.processing.ProcessingEnvironment;
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 reflectionClass, String name);
public ReflectionClass findCommonParentOf(ReflectionClass reflectionClass1,
ReflectionClass reflectionClass2) {
ReflectionClass curr = reflectionClass1;
while (curr != null && !curr.isAssignableFrom(reflectionClass2)) {
curr = curr.getSuperclass();
}
if (curr == null) {
ReflectionClass primitive1 = reflectionClass1.unbox();
ReflectionClass primitive2 = reflectionClass2.unbox();
if (!reflectionClass1.equals(primitive1) || !reflectionClass2.equals(primitive2)) {
return findCommonParentOf(primitive1, primitive2);
}
}
Preconditions.checkNotNull(curr,
"must be able to find a common parent for " + reflectionClass1 + " and " + reflectionClass2);
return curr;
}
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 static void setProcessingEnvironment(ProcessingEnvironment processingEnvironment) {
ModelAnalyzer modelAnalyzer = new ModelAnalyzer(processingEnvironment);
sAnalyzer = modelAnalyzer;
}
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 List<URL> getResources(String name);
public abstract ReflectionClass findClass(Class classType);
}

View File

@@ -1,68 +0,0 @@
/*
* 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

@@ -1,19 +0,0 @@
/*
* 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

@@ -1,32 +0,0 @@
/*
* 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.util.List;
public interface ReflectionMethod {
ReflectionClass getDeclaringClass();
ReflectionClass[] getParameterTypes();
String getName();
ReflectionClass getReturnType(List<ReflectionClass> args);
boolean isPublic();
boolean isStatic();
}

View File

@@ -15,9 +15,9 @@
*/
package com.android.databinding.store;
import com.android.databinding.reflection.ReflectionAnalyzer;
import com.android.databinding.reflection.ReflectionClass;
import com.android.databinding.reflection.ReflectionMethod;
import com.android.databinding.reflection.ModelAnalyzer;
import com.android.databinding.reflection.ModelClass;
import com.android.databinding.reflection.ModelMethod;
import java.io.File;
import java.io.IOException;
@@ -27,7 +27,6 @@ import java.io.ObjectOutputStream;
import java.io.Serializable;
import java.net.URL;
import java.util.ArrayList;
import java.util.Collections;
import java.util.HashMap;
import java.util.List;
import java.util.Objects;
@@ -53,10 +52,10 @@ public class SetterStore {
private static SetterStore sStore;
private final IntermediateV1 mStore;
private final ReflectionAnalyzer mClassAnalyzer;
private final ModelAnalyzer mClassAnalyzer;
private SetterStore(ReflectionAnalyzer reflectionAnalyzer, IntermediateV1 store) {
mClassAnalyzer = reflectionAnalyzer;
private SetterStore(ModelAnalyzer modelAnalyzer, IntermediateV1 store) {
mClassAnalyzer = modelAnalyzer;
mStore = store;
}
@@ -93,22 +92,22 @@ public class SetterStore {
return sStore;
}
public static SetterStore get(ReflectionAnalyzer reflectionAnalyzer) {
public static SetterStore get(ModelAnalyzer modelAnalyzer) {
if (sStore == null) {
sStore = load(reflectionAnalyzer);
sStore = load(modelAnalyzer);
}
return sStore;
}
private static SetterStore load(ReflectionAnalyzer reflectionAnalyzer) {
private static SetterStore load(ModelAnalyzer modelAnalyzer) {
IntermediateV1 store = new IntermediateV1();
String resourceName = SetterStore.class.getPackage().getName().replace('.', '/') +
'/' + SETTER_STORE_FILE_NAME;
try {
for (URL resource : reflectionAnalyzer.getResources(resourceName)) {
for (URL resource : modelAnalyzer.getResources(resourceName)) {
merge(store, resource);
}
return new SetterStore(reflectionAnalyzer, store);
return new SetterStore(modelAnalyzer, store);
} catch (IOException e) {
System.err.println("Could not read SetterStore intermediate file: " +
e.getLocalizedMessage());
@@ -118,7 +117,7 @@ public class SetterStore {
e.getLocalizedMessage());
e.printStackTrace();
}
return new SetterStore(reflectionAnalyzer, store);
return new SetterStore(modelAnalyzer, store);
}
private static SetterStore load(InputStream inputStream)
@@ -255,8 +254,8 @@ public class SetterStore {
}
}
public String getSetterCall(String attribute, ReflectionClass viewType,
ReflectionClass valueType, String viewExpression, String valueExpression) {
public String getSetterCall(String attribute, ModelClass viewType,
ModelClass valueType, String viewExpression, String valueExpression) {
if (!attribute.startsWith("android:")) {
int colon = attribute.indexOf(':');
if (colon >= 0) {
@@ -266,9 +265,9 @@ public class SetterStore {
HashMap<AccessorKey, MethodDescription> adapters = mStore.adapterMethods.get(attribute);
MethodDescription adapter = null;
String setterName = null;
ReflectionMethod bestSetterMethod = getBestSetter(viewType, valueType, attribute);
ReflectionClass bestViewType = null;
ReflectionClass bestValueType = null;
ModelMethod bestSetterMethod = getBestSetter(viewType, valueType, attribute);
ModelClass bestViewType = null;
ModelClass bestValueType = null;
if (bestSetterMethod != null) {
bestViewType = bestSetterMethod.getDeclaringClass();
bestValueType = bestSetterMethod.getParameterTypes()[0];
@@ -278,10 +277,10 @@ public class SetterStore {
if (adapters != null) {
for (AccessorKey key : adapters.keySet()) {
try {
ReflectionClass adapterViewType = mClassAnalyzer.findClass(key.viewType);
ModelClass adapterViewType = mClassAnalyzer.findClass(key.viewType);
if (adapterViewType.isAssignableFrom(viewType)) {
try {
ReflectionClass adapterValueType = mClassAnalyzer.findClass(key.valueType);
ModelClass adapterValueType = mClassAnalyzer.findClass(key.valueType);
boolean isBetterView = bestViewType == null ||
bestValueType.isAssignableFrom(adapterValueType);
if (isBetterParameter(valueType, adapterValueType, bestValueType,
@@ -317,7 +316,7 @@ public class SetterStore {
}
}
private ReflectionMethod getBestSetter(ReflectionClass viewType, ReflectionClass argumentType,
private ModelMethod getBestSetter(ModelClass viewType, ModelClass argumentType,
String attribute) {
String setterName = null;
@@ -325,7 +324,7 @@ public class SetterStore {
if (renamed != null) {
for (String className : renamed.keySet()) {
try {
ReflectionClass renamedViewType = mClassAnalyzer.findClass(className);
ModelClass renamedViewType = mClassAnalyzer.findClass(className);
if (renamedViewType.isAssignableFrom(viewType)) {
setterName = renamed.get(className).method;
break;
@@ -338,16 +337,16 @@ public class SetterStore {
if (setterName == null) {
setterName = getDefaultSetter(attribute);
}
ReflectionMethod[] methods = viewType.getMethods(setterName, 1);
ModelMethod[] methods = viewType.getMethods(setterName, 1);
ReflectionClass bestParameterType = null;
ReflectionMethod bestMethod = null;
List<ReflectionClass> args = new ArrayList<>();
ModelClass bestParameterType = null;
ModelMethod bestMethod = null;
List<ModelClass> args = new ArrayList<>();
args.add(argumentType);
for (ReflectionMethod method : methods) {
ReflectionClass[] parameterTypes = method.getParameterTypes();
for (ModelMethod method : methods) {
ModelClass[] parameterTypes = method.getParameterTypes();
if (method.getReturnType(args).isVoid() && !method.isStatic() && method.isPublic()) {
ReflectionClass param = parameterTypes[0];
ModelClass param = parameterTypes[0];
if (isBetterParameter(argumentType, param, bestParameterType, true)) {
bestParameterType = param;
bestMethod = method;
@@ -365,8 +364,8 @@ public class SetterStore {
return "set" + propertyName;
}
private boolean isBetterParameter(ReflectionClass argument, ReflectionClass parameter,
ReflectionClass oldParameter, boolean isBetterViewTypeMatch) {
private boolean isBetterParameter(ModelClass argument, ModelClass parameter,
ModelClass oldParameter, boolean isBetterViewTypeMatch) {
// Right view type. Check the value
if (!isBetterViewTypeMatch && oldParameter.equals(argument)) {
return false;
@@ -406,7 +405,7 @@ public class SetterStore {
}
}
private static boolean isImplicitConversion(ReflectionClass from, ReflectionClass to) {
private static boolean isImplicitConversion(ModelClass from, ModelClass to) {
if (from != null && to != null && from.isPrimitive() && to.isPrimitive()) {
if (from.isBoolean() || to.isBoolean() || to.isChar()) {
return false;
@@ -419,17 +418,17 @@ public class SetterStore {
}
}
private MethodDescription getConversionMethod(ReflectionClass from, ReflectionClass to) {
private MethodDescription getConversionMethod(ModelClass from, ModelClass to) {
if (from != null && to != null) {
for (String fromClassName : mStore.conversionMethods.keySet()) {
try {
ReflectionClass convertFrom = mClassAnalyzer.findClass(fromClassName);
ModelClass convertFrom = mClassAnalyzer.findClass(fromClassName);
if (canUseForConversion(from, convertFrom)) {
HashMap<String, MethodDescription> conversion =
mStore.conversionMethods.get(fromClassName);
for (String toClassName : conversion.keySet()) {
try {
ReflectionClass convertTo = mClassAnalyzer.findClass(toClassName);
ModelClass convertTo = mClassAnalyzer.findClass(toClassName);
if (canUseForConversion(convertTo, to)) {
return conversion.get(toClassName);
}
@@ -446,11 +445,11 @@ public class SetterStore {
return null;
}
private boolean canUseForConversion(ReflectionClass from, ReflectionClass to) {
private boolean canUseForConversion(ModelClass from, ModelClass to) {
return from.equals(to) || isBoxingConversion(from, to) || to.isAssignableFrom(from);
}
private static int getConversionLevel(ReflectionClass primitive) {
private static int getConversionLevel(ModelClass primitive) {
if (primitive == null) {
return -1;
} else if (primitive.isByte()) {
@@ -472,7 +471,7 @@ public class SetterStore {
}
}
public static boolean isBoxingConversion(ReflectionClass class1, ReflectionClass class2) {
public static boolean isBoxingConversion(ModelClass class1, ModelClass class2) {
if (class1.isPrimitive() != class2.isPrimitive()) {
return (class1.box().equals(class2.box()));
} else {

View File

@@ -17,9 +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.reflection.ReflectionAnalyzer
import com.android.databinding.reflection.ReflectionClass
import com.android.databinding.reflection.ReflectionAnalyzer
import com.android.databinding.reflection.ModelAnalyzer
import com.android.databinding.reflection.ModelClass
import com.android.databinding.reflection.ModelAnalyzer
private class LazyExt<K, T>(private val initializer: (k : K) -> T) : ReadOnlyProperty<K, T> {
private val mapping = hashMapOf<K, T>()

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.reflection.ReflectionAnalyzer
import com.android.databinding.reflection.ModelAnalyzer
import kotlin.properties.Delegates
import com.android.databinding.ext.joinToCamelCaseAsVar
import com.android.databinding.BindingTarget

View File

@@ -26,7 +26,7 @@ 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 com.android.databinding.reflection.ModelAnalyzer;
import org.junit.Before;
import org.junit.Test;
@@ -43,7 +43,7 @@ public class ExpressionVisitorTest {
@Before
public void setUp() throws Exception {
ReflectionAnalyzer.initForTests();
ModelAnalyzer.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 {
ReflectionAnalyzer.initForTests();
ModelAnalyzer.initForTests();
}
@Parameterized.Parameters

View File

@@ -20,7 +20,7 @@ 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 com.android.databinding.reflection.ModelAnalyzer;
import org.junit.Before;
import org.junit.Test;
@@ -35,7 +35,7 @@ public class LayoutBinderTest {
ExprModel mExprModel;
@Before
public void setUp() throws Exception {
ReflectionAnalyzer.initForTests();
ModelAnalyzer.initForTests();
mLayoutBinder = new LayoutBinder(null);
mExprModel = mLayoutBinder.getModel();
}

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.reflection.ReflectionAnalyzer;
import com.android.databinding.reflection.ModelAnalyzer;
import com.android.databinding.LayoutBinder;
import com.android.databinding.util.L;
@@ -45,7 +45,7 @@ public class ExprModelTest {
}
@Override
protected Class resolveType(ReflectionAnalyzer reflectionAnalyzer) {
protected Class resolveType(ModelAnalyzer modelAnalyzer) {
return Integer.class;
}
@@ -77,7 +77,7 @@ public class ExprModelTest {
@Before
public void setUp() throws Exception {
ReflectionAnalyzer.initForTests();
ModelAnalyzer.initForTests();
mExprModel = new ExprModel();
}

View File

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

View File

@@ -43,7 +43,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
import com.android.databinding.reflection.ModelAnalyzer
import com.android.databinding.writer.JavaFileWriter
import java.io.FileInputStream
import java.io.FileOutputStream
@@ -202,11 +202,6 @@ class DataBinderPlugin : Plugin<Project> {
val cpFiles = arrayListOf<File>()
cpFiles.addAll(dexTask.getInputFiles())
cpFiles.addAll(jCompileTask.getClasspath().getFiles())
val urls = cpFiles.map { it.toURI().toURL() }.copyToArray()
log("generated urls: ${urls} len: ${urls.size}")
val classLoader = URLClassLoader(urls, androidClassLoader)
log("created class loader")
ReflectionAnalyzer.setClassLoader(classLoader)
//project.task("compileGenerated", MethodClosure(this, "compileGenerated"))
}
fun compileGenerated(o : Any?) {