diff options
author | Qiwen Zhao <zhao@google.com> | 2015-04-08 15:58:36 -0700 |
---|---|---|
committer | Qiwen Zhao <zhao@google.com> | 2015-04-08 15:58:36 -0700 |
commit | 4e7bb6312f2126bda6f5f94eae9d5d8d6e0169d5 (patch) | |
tree | 490e018e0c16eac55fd153111c9f217d6fefc495 /tools | |
parent | 0ceef71ac4c97492df43ccffc41dce8864249b4f (diff) | |
parent | 6ad27159112b844f7782c605711eb688db377232 (diff) | |
download | frameworks_base-4e7bb6312f2126bda6f5f94eae9d5d8d6e0169d5.zip frameworks_base-4e7bb6312f2126bda6f5f94eae9d5d8d6e0169d5.tar.gz frameworks_base-4e7bb6312f2126bda6f5f94eae9d5d8d6e0169d5.tar.bz2 |
Merge KDataBinder/ from platform/vendor/google/prototypes/data-binding to tools/data-binding/
Diffstat (limited to 'tools')
438 files changed, 34707 insertions, 0 deletions
diff --git a/tools/data-binding/annotationprocessor/build.gradle b/tools/data-binding/annotationprocessor/build.gradle new file mode 100644 index 0000000..a639abb --- /dev/null +++ b/tools/data-binding/annotationprocessor/build.gradle @@ -0,0 +1,54 @@ +/* + * Copyright (C) 2014 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. + */ + +apply plugin: 'java' +apply plugin: 'maven' + +sourceCompatibility = 1.7 +targetCompatibility = 1.7 + +buildscript { + repositories { + mavenLocal() + mavenCentral() + } +} + +repositories { + mavenCentral() +} + +sourceSets { + main { + java { + srcDir 'src/main/java' + } + } +} + +dependencies { + compile project(":baseLibrary") + compile project(":compiler") + compile 'commons-codec:commons-codec:1.10' +} + +uploadArchives { + repositories { + mavenDeployer { + pom.artifactId = 'annotationprocessor' + } + } +} diff --git a/tools/data-binding/annotationprocessor/src/main/java/android/databinding/annotationprocessor/AnnotationUtil.java b/tools/data-binding/annotationprocessor/src/main/java/android/databinding/annotationprocessor/AnnotationUtil.java new file mode 100644 index 0000000..c82a976 --- /dev/null +++ b/tools/data-binding/annotationprocessor/src/main/java/android/databinding/annotationprocessor/AnnotationUtil.java @@ -0,0 +1,41 @@ +/* + * 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 android.databinding.annotationprocessor; + +import java.lang.annotation.Annotation; +import java.util.ArrayList; +import java.util.List; + +import javax.annotation.processing.RoundEnvironment; +import javax.lang.model.element.Element; + +class AnnotationUtil { + + /** + * Returns only the elements that are annotated with the given class. For some reason + * RoundEnvironment is returning elements annotated by other annotations. + */ + static List<Element> getElementsAnnotatedWith(RoundEnvironment roundEnv, + Class<? extends Annotation> annotationClass) { + ArrayList<Element> elements = new ArrayList<>(); + for (Element element : roundEnv.getElementsAnnotatedWith(annotationClass)) { + if (element.getAnnotation(annotationClass) != null) { + elements.add(element); + } + } + return elements; + } +} diff --git a/tools/data-binding/annotationprocessor/src/main/java/android/databinding/annotationprocessor/BuildInfoUtil.java b/tools/data-binding/annotationprocessor/src/main/java/android/databinding/annotationprocessor/BuildInfoUtil.java new file mode 100644 index 0000000..df525c1 --- /dev/null +++ b/tools/data-binding/annotationprocessor/src/main/java/android/databinding/annotationprocessor/BuildInfoUtil.java @@ -0,0 +1,51 @@ +/* + * 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 android.databinding.annotationprocessor; + +import com.google.common.base.Preconditions; + +import android.databinding.BindingBuildInfo; + +import java.lang.annotation.Annotation; + +import javax.annotation.processing.RoundEnvironment; +import javax.lang.model.element.Element; + +public class BuildInfoUtil { + private static BindingBuildInfo sCached; + public static BindingBuildInfo load(RoundEnvironment roundEnvironment) { + if (sCached == null) { + sCached = extractNotNull(roundEnvironment, BindingBuildInfo.class); + } + return sCached; + } + + private static <T extends Annotation> T extractNotNull(RoundEnvironment roundEnv, + Class<T> annotationClass) { + T result = null; + for (Element element : roundEnv.getElementsAnnotatedWith(annotationClass)) { + final T info = element.getAnnotation(annotationClass); + if (info == null) { + continue; // It gets confused between BindingAppInfo and BinderBundle + } + Preconditions.checkState(result == null, "Should have only one %s", + annotationClass.getCanonicalName()); + result = info; + } + return result; + } +} diff --git a/tools/data-binding/annotationprocessor/src/main/java/android/databinding/annotationprocessor/ProcessBindable.java b/tools/data-binding/annotationprocessor/src/main/java/android/databinding/annotationprocessor/ProcessBindable.java new file mode 100644 index 0000000..d4582cb --- /dev/null +++ b/tools/data-binding/annotationprocessor/src/main/java/android/databinding/annotationprocessor/ProcessBindable.java @@ -0,0 +1,304 @@ +/* + * 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 android.databinding.annotationprocessor; + +import com.google.common.base.Preconditions; + +import android.databinding.Bindable; +import android.databinding.BindingBuildInfo; +import android.databinding.tool.CompilerChef.BindableHolder; +import android.databinding.tool.util.GenerationalClassUtil; +import android.databinding.tool.util.L; + +import java.io.Serializable; +import java.util.ArrayList; +import java.util.Collections; +import java.util.HashMap; +import java.util.HashSet; +import java.util.List; +import java.util.Set; + +import javax.annotation.processing.ProcessingEnvironment; +import javax.annotation.processing.RoundEnvironment; +import javax.annotation.processing.SupportedSourceVersion; +import javax.lang.model.SourceVersion; +import javax.lang.model.element.Element; +import javax.lang.model.element.ElementKind; +import javax.lang.model.element.ExecutableElement; +import javax.lang.model.element.Name; +import javax.lang.model.element.TypeElement; +import javax.lang.model.element.VariableElement; +import javax.lang.model.type.TypeKind; + +// binding app info and library info are necessary to trigger this. +@SupportedSourceVersion(SourceVersion.RELEASE_7) +public class ProcessBindable extends ProcessDataBinding.ProcessingStep implements BindableHolder { + private static final String INTERMEDIATE_FILE_EXT = "-br.bin"; + Intermediate mProperties; + HashMap<String, HashSet<String>> mLayoutVariables = new HashMap<>(); + + @Override + public boolean onHandleStep(RoundEnvironment roundEnv, ProcessingEnvironment processingEnv, + BindingBuildInfo buildInfo) { + if (mProperties == null) { + mProperties = new IntermediateV1(buildInfo.modulePackage()); + mergeLayoutVariables(); + mLayoutVariables.clear(); + for (Element element : AnnotationUtil + .getElementsAnnotatedWith(roundEnv, Bindable.class)) { + Element enclosingElement = element.getEnclosingElement(); + ElementKind kind = enclosingElement.getKind(); + if (kind != ElementKind.CLASS && kind != ElementKind.INTERFACE) { + L.e("Bindable must be on a member field or method. The enclosing type is %s", + enclosingElement.getKind()); + } + TypeElement enclosing = (TypeElement) enclosingElement; + String name = getPropertyName(element); + if (name != null) { + Preconditions + .checkNotNull(mProperties, "Must receive app / library info before " + + "Bindable fields."); + mProperties.addProperty(enclosing.getQualifiedName().toString(), name); + } + } + if (mProperties.hasValues()) { + GenerationalClassUtil.writeIntermediateFile(processingEnv, + mProperties.getPackage(), + createIntermediateFileName(mProperties.getPackage()), mProperties); + generateBRClasses(!buildInfo.isLibrary(), mProperties.getPackage()); + } + } + return false; + } + + @Override + public void addVariable(String variableName, String containingClassName) { + HashSet<String> variableNames = mLayoutVariables.get(containingClassName); + if (variableNames == null) { + variableNames = new HashSet<>(); + mLayoutVariables.put(containingClassName, variableNames); + } + variableNames.add(variableName); + } + + @Override + public void onProcessingOver(RoundEnvironment roundEnvironment, + ProcessingEnvironment processingEnvironment, BindingBuildInfo buildInfo) { + } + + private String createIntermediateFileName(String appPkg) { + return appPkg + INTERMEDIATE_FILE_EXT; + } + + private void generateBRClasses(boolean useFinalFields, String pkg) { + L.d("************* Generating BR file %s. use final: %s", pkg, useFinalFields); + HashSet<String> properties = new HashSet<>(); + mProperties.captureProperties(properties); + List<Intermediate> previousIntermediates = loadPreviousBRFiles(); + for (Intermediate intermediate : previousIntermediates) { + intermediate.captureProperties(properties); + } + writeBRClass(useFinalFields, pkg, properties); + if (useFinalFields) { + // generate BR for all previous packages + for (Intermediate intermediate : previousIntermediates) { + writeBRClass(true, intermediate.getPackage(), + properties); + } + } + } + + private void writeBRClass(boolean useFinalFields, String pkg, HashSet<String> properties) { + ArrayList<String> sortedProperties = new ArrayList<String>(); + sortedProperties.addAll(properties); + Collections.sort(sortedProperties); + StringBuilder out = new StringBuilder(); + String modifier = "public static " + (useFinalFields ? "final" : "") + " int "; + out.append("package " + pkg + ";\n\n" + + "public class BR {\n" + + " " + modifier + "_all = 0;\n" + ); + int id = 0; + for (String property : sortedProperties) { + id++; + out.append(" " + modifier + property + " = " + id + ";\n"); + } + out.append(" public static int getId(String key) {\n"); + out.append(" switch(key) {\n"); + id = 0; + for (String property : sortedProperties) { + id++; + out.append(" case \"" + property + "\": return " + id + ";\n"); + } + out.append(" }\n"); + out.append(" return -1;\n"); + out.append(" }"); + out.append("}\n"); + + getWriter().writeToFile(pkg + ".BR", out.toString() ); + } + + private String getPropertyName(Element element) { + switch (element.getKind()) { + case FIELD: + return stripPrefixFromField((VariableElement) element); + case METHOD: + return stripPrefixFromMethod((ExecutableElement) element); + default: + L.e("@Bindable is not allowed on %s", element.getKind()); + return null; + } + } + + private static String stripPrefixFromField(VariableElement element) { + Name name = element.getSimpleName(); + if (name.length() >= 2) { + char firstChar = name.charAt(0); + char secondChar = name.charAt(1); + if (name.length() > 2 && firstChar == 'm' && secondChar == '_') { + char thirdChar = name.charAt(2); + if (Character.isJavaIdentifierStart(thirdChar)) { + return "" + Character.toLowerCase(thirdChar) + + name.subSequence(3, name.length()); + } + } else if ((firstChar == 'm' && Character.isUpperCase(secondChar)) || + (firstChar == '_' && Character.isJavaIdentifierStart(secondChar))) { + return "" + Character.toLowerCase(secondChar) + name.subSequence(2, name.length()); + } + } + return name.toString(); + } + + private String stripPrefixFromMethod(ExecutableElement element) { + Name name = element.getSimpleName(); + CharSequence propertyName; + if (isGetter(element) || isSetter(element)) { + propertyName = name.subSequence(3, name.length()); + } else if (isBooleanGetter(element)) { + propertyName = name.subSequence(2, name.length()); + } else { + L.e("@Bindable associated with method must follow JavaBeans convention %s", element); + return null; + } + char firstChar = propertyName.charAt(0); + return "" + Character.toLowerCase(firstChar) + + propertyName.subSequence(1, propertyName.length()); + } + + private void mergeLayoutVariables() { + for (String containingClass : mLayoutVariables.keySet()) { + for (String variable : mLayoutVariables.get(containingClass)) { + mProperties.addProperty(containingClass, variable); + } + } + } + + private static boolean prefixes(CharSequence sequence, String prefix) { + boolean prefixes = false; + if (sequence.length() > prefix.length()) { + int count = prefix.length(); + prefixes = true; + for (int i = 0; i < count; i++) { + if (sequence.charAt(i) != prefix.charAt(i)) { + prefixes = false; + break; + } + } + } + return prefixes; + } + + private static boolean isGetter(ExecutableElement element) { + Name name = element.getSimpleName(); + return prefixes(name, "get") && + Character.isJavaIdentifierStart(name.charAt(3)) && + element.getParameters().isEmpty() && + element.getReturnType().getKind() != TypeKind.VOID; + } + + private static boolean isSetter(ExecutableElement element) { + Name name = element.getSimpleName(); + return prefixes(name, "set") && + Character.isJavaIdentifierStart(name.charAt(3)) && + element.getParameters().size() == 1 && + element.getReturnType().getKind() == TypeKind.VOID; + } + + private static boolean isBooleanGetter(ExecutableElement element) { + Name name = element.getSimpleName(); + return prefixes(name, "is") && + Character.isJavaIdentifierStart(name.charAt(2)) && + element.getParameters().isEmpty() && + element.getReturnType().getKind() == TypeKind.BOOLEAN; + } + + private List<Intermediate> loadPreviousBRFiles() { + return GenerationalClassUtil + .loadObjects(getClass().getClassLoader(), + new GenerationalClassUtil.ExtensionFilter(INTERMEDIATE_FILE_EXT)); + } + + private interface Intermediate extends Serializable { + + void captureProperties(Set<String> properties); + + void addProperty(String className, String propertyName); + + boolean hasValues(); + + String getPackage(); + } + + private static class IntermediateV1 implements Serializable, Intermediate { + + private static final long serialVersionUID = 2L; + + private String mPackage; + private final HashMap<String, HashSet<String>> mProperties = new HashMap<>(); + + public IntermediateV1(String aPackage) { + mPackage = aPackage; + } + + @Override + public void captureProperties(Set<String> properties) { + for (HashSet<String> propertySet : mProperties.values()) { + properties.addAll(propertySet); + } + } + + @Override + public void addProperty(String className, String propertyName) { + HashSet<String> properties = mProperties.get(className); + if (properties == null) { + properties = new HashSet<>(); + mProperties.put(className, properties); + } + properties.add(propertyName); + } + + @Override + public boolean hasValues() { + return !mProperties.isEmpty(); + } + + @Override + public String getPackage() { + return mPackage; + } + } +} diff --git a/tools/data-binding/annotationprocessor/src/main/java/android/databinding/annotationprocessor/ProcessDataBinding.java b/tools/data-binding/annotationprocessor/src/main/java/android/databinding/annotationprocessor/ProcessDataBinding.java new file mode 100644 index 0000000..944cc20a --- /dev/null +++ b/tools/data-binding/annotationprocessor/src/main/java/android/databinding/annotationprocessor/ProcessDataBinding.java @@ -0,0 +1,128 @@ +/* + * 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 android.databinding.annotationprocessor; + +import android.databinding.BindingBuildInfo; +import android.databinding.tool.reflection.ModelAnalyzer; +import android.databinding.tool.writer.AnnotationJavaFileWriter; +import android.databinding.tool.writer.JavaFileWriter; + +import java.util.Arrays; +import java.util.List; +import java.util.Set; + +import javax.annotation.processing.AbstractProcessor; +import javax.annotation.processing.ProcessingEnvironment; +import javax.annotation.processing.RoundEnvironment; +import javax.annotation.processing.SupportedAnnotationTypes; +import javax.annotation.processing.SupportedSourceVersion; +import javax.lang.model.SourceVersion; +import javax.lang.model.element.TypeElement; + +@SupportedAnnotationTypes({ + "android.databinding.BindingAdapter", + "android.databinding.Untaggable", + "android.databinding.BindingMethods", + "android.databinding.BindingConversion", + "android.databinding.BindingBuildInfo"} +) +@SupportedSourceVersion(SourceVersion.RELEASE_7) +/** + * Parent annotation processor that dispatches sub steps to ensure execution order. + * Use initProcessingSteps to add a new step. + */ +public class ProcessDataBinding extends AbstractProcessor { + private List<ProcessingStep> mProcessingSteps; + @Override + public boolean process(Set<? extends TypeElement> annotations, RoundEnvironment roundEnv) { + if (mProcessingSteps == null) { + initProcessingSteps(); + } + final BindingBuildInfo buildInfo = BuildInfoUtil.load(roundEnv); + if (buildInfo == null) { + return false; + } + boolean done = true; + for (ProcessingStep step : mProcessingSteps) { + done = step.runStep(roundEnv, processingEnv, buildInfo) && done; + } + if (roundEnv.processingOver()) { + for (ProcessingStep step : mProcessingSteps) { + step.onProcessingOver(roundEnv, processingEnv, buildInfo); + } + } + return done; + } + + private void initProcessingSteps() { + ProcessBindable processBindable = new ProcessBindable(); + mProcessingSteps = Arrays.asList( + new ProcessMethodAdapters(), + new ProcessExpressions(processBindable), + processBindable + ); + AnnotationJavaFileWriter javaFileWriter = new AnnotationJavaFileWriter(processingEnv); + for (ProcessingStep step : mProcessingSteps) { + step.mJavaFileWriter = javaFileWriter; + } + } + + @Override + public synchronized void init(ProcessingEnvironment processingEnv) { + super.init(processingEnv); + ModelAnalyzer.setProcessingEnvironment(processingEnv); + } + + /** + * To ensure execution order and binding build information, we use processing steps. + */ + public abstract static class ProcessingStep { + private boolean mDone; + private JavaFileWriter mJavaFileWriter; + + protected JavaFileWriter getWriter() { + return mJavaFileWriter; + } + + private boolean runStep(RoundEnvironment roundEnvironment, + ProcessingEnvironment processingEnvironment, + BindingBuildInfo buildInfo) { + if (mDone) { + return true; + } + mDone = onHandleStep(roundEnvironment, processingEnvironment, buildInfo); + return mDone; + } + + /** + * Invoked in each annotation processing step. + * + * @return True if it is done and should never be invoked again. + */ + abstract public boolean onHandleStep(RoundEnvironment roundEnvironment, + ProcessingEnvironment processingEnvironment, + BindingBuildInfo buildInfo); + + /** + * Invoked when processing is done. A good place to generate the output if the + * processor requires multiple steps. + */ + abstract public void onProcessingOver(RoundEnvironment roundEnvironment, + ProcessingEnvironment processingEnvironment, + BindingBuildInfo buildInfo); + } +} diff --git a/tools/data-binding/annotationprocessor/src/main/java/android/databinding/annotationprocessor/ProcessExpressions.java b/tools/data-binding/annotationprocessor/src/main/java/android/databinding/annotationprocessor/ProcessExpressions.java new file mode 100644 index 0000000..d62715a --- /dev/null +++ b/tools/data-binding/annotationprocessor/src/main/java/android/databinding/annotationprocessor/ProcessExpressions.java @@ -0,0 +1,196 @@ +/* + * 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 android.databinding.annotationprocessor; + +import org.apache.commons.io.FileUtils; +import org.apache.commons.io.IOUtils; + +import android.databinding.BindingBuildInfo; +import android.databinding.tool.CompilerChef; +import android.databinding.tool.reflection.SdkUtil; +import android.databinding.tool.store.ResourceBundle; +import android.databinding.tool.util.GenerationalClassUtil; +import android.databinding.tool.util.L; + +import java.io.File; +import java.io.FilenameFilter; +import java.io.IOException; +import java.io.InputStream; +import java.io.Serializable; +import java.util.HashMap; +import java.util.List; +import java.util.Map; + +import javax.annotation.processing.ProcessingEnvironment; +import javax.annotation.processing.RoundEnvironment; +import javax.xml.bind.JAXBContext; +import javax.xml.bind.JAXBException; +import javax.xml.bind.Unmarshaller; + +public class ProcessExpressions extends ProcessDataBinding.ProcessingStep { + + private static final String LAYOUT_INFO_FILE_SUFFIX = "-layoutinfo.bin"; + + private final ProcessBindable mProcessBindable; + + public ProcessExpressions(ProcessBindable processBindable) { + mProcessBindable = processBindable; + } + + + @Override + public boolean onHandleStep(RoundEnvironment roundEnvironment, + ProcessingEnvironment processingEnvironment, BindingBuildInfo buildInfo) { + ResourceBundle resourceBundle; + SdkUtil.initialize(buildInfo.minSdk(), new File(buildInfo.sdkRoot())); + resourceBundle = new ResourceBundle(buildInfo.modulePackage()); + List<Intermediate> intermediateList = + GenerationalClassUtil.loadObjects(getClass().getClassLoader(), + new GenerationalClassUtil.ExtensionFilter(LAYOUT_INFO_FILE_SUFFIX)); + IntermediateV1 mine = createIntermediateFromLayouts(buildInfo.layoutInfoDir()); + if (mine != null) { + mine.removeOverridden(intermediateList); + intermediateList.add(mine); + saveIntermediate(processingEnvironment, buildInfo, mine); + } + // generate them here so that bindable parser can read + try { + generateBinders(resourceBundle, buildInfo, intermediateList); + } catch (Throwable t) { + L.e(t, "cannot generate view binders"); + } + return true; + } + + private void saveIntermediate(ProcessingEnvironment processingEnvironment, + BindingBuildInfo buildInfo, IntermediateV1 intermediate) { + GenerationalClassUtil.writeIntermediateFile(processingEnvironment, + buildInfo.modulePackage(), buildInfo.modulePackage() + LAYOUT_INFO_FILE_SUFFIX, + intermediate); + } + + @Override + public void onProcessingOver(RoundEnvironment roundEnvironment, + ProcessingEnvironment processingEnvironment, BindingBuildInfo buildInfo) { + } + + private void generateBinders(ResourceBundle resourceBundle, BindingBuildInfo buildInfo, + List<Intermediate> intermediates) + throws Throwable { + for (Intermediate intermediate : intermediates) { + intermediate.appendTo(resourceBundle); + } + writeResourceBundle(resourceBundle, buildInfo.isLibrary(), buildInfo.minSdk()); + } + + private IntermediateV1 createIntermediateFromLayouts(String layoutInfoFolderPath) { + final File layoutInfoFolder = new File(layoutInfoFolderPath); + if (!layoutInfoFolder.isDirectory()) { + L.d("layout info folder does not exist, skipping for %s", layoutInfoFolderPath); + return null; + } + IntermediateV1 result = new IntermediateV1(); + for (File layoutFile : layoutInfoFolder.listFiles(new FilenameFilter() { + @Override + public boolean accept(File dir, String name) { + return name.endsWith(".xml"); + } + })) { + try { + result.addEntry(layoutFile.getName(), FileUtils.readFileToString(layoutFile)); + } catch (IOException e) { + L.e(e, "cannot load layout file information. Try a clean build"); + } + } + return result; + } + + private void writeResourceBundle(ResourceBundle resourceBundle, boolean forLibraryModule, + int minSdk) + throws JAXBException { + CompilerChef compilerChef = CompilerChef.createChef(resourceBundle, getWriter()); + if (compilerChef.hasAnythingToGenerate()) { + compilerChef.addBRVariables(mProcessBindable); + compilerChef.writeViewBinderInterfaces(forLibraryModule); + if (!forLibraryModule) { + compilerChef.writeDbrFile(minSdk); + compilerChef.writeViewBinders(); + } + } + } + + public static interface Intermediate extends Serializable { + + Intermediate upgrade(); + + public void appendTo(ResourceBundle resourceBundle) throws Throwable; + } + + public static class IntermediateV1 implements Intermediate { + + transient Unmarshaller mUnmarshaller; + + // name to xml content map + Map<String, String> mLayoutInfoMap = new HashMap<>(); + + @Override + public Intermediate upgrade() { + return this; + } + + @Override + public void appendTo(ResourceBundle resourceBundle) throws JAXBException { + if (mUnmarshaller == null) { + JAXBContext context = JAXBContext + .newInstance(ResourceBundle.LayoutFileBundle.class); + mUnmarshaller = context.createUnmarshaller(); + } + for (String content : mLayoutInfoMap.values()) { + final InputStream is = IOUtils.toInputStream(content); + try { + final ResourceBundle.LayoutFileBundle bundle + = (ResourceBundle.LayoutFileBundle) mUnmarshaller.unmarshal(is); + resourceBundle.addLayoutBundle(bundle); + L.d("loaded layout info file %s", bundle); + } finally { + IOUtils.closeQuietly(is); + } + } + } + + public void addEntry(String name, String contents) { + mLayoutInfoMap.put(name, contents); + } + + public void removeOverridden(List<Intermediate> existing) { + // this is the way we get rid of files that are copied from previous modules + // it is important to do this before saving the intermediate file + for (Intermediate old : existing) { + if (old instanceof IntermediateV1) { + IntermediateV1 other = (IntermediateV1) old; + for (String key : other.mLayoutInfoMap.keySet()) { + // TODO we should consider the original file as the key here + // but aapt probably cannot provide that information + if (mLayoutInfoMap.remove(key) != null) { + L.d("removing %s from bundle because it came from another module", key); + } + } + } + } + } + } +} diff --git a/tools/data-binding/annotationprocessor/src/main/java/android/databinding/annotationprocessor/ProcessMethodAdapters.java b/tools/data-binding/annotationprocessor/src/main/java/android/databinding/annotationprocessor/ProcessMethodAdapters.java new file mode 100644 index 0000000..98da839 --- /dev/null +++ b/tools/data-binding/annotationprocessor/src/main/java/android/databinding/annotationprocessor/ProcessMethodAdapters.java @@ -0,0 +1,177 @@ +/* + * 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 android.databinding.annotationprocessor; + +import com.google.common.base.Preconditions; + +import android.databinding.BindingAdapter; +import android.databinding.BindingBuildInfo; +import android.databinding.BindingConversion; +import android.databinding.BindingMethod; +import android.databinding.BindingMethods; +import android.databinding.Untaggable; +import android.databinding.tool.reflection.ModelAnalyzer; +import android.databinding.tool.store.SetterStore; +import android.databinding.tool.util.L; + +import java.io.IOException; +import java.util.HashSet; +import java.util.List; + +import javax.annotation.processing.ProcessingEnvironment; +import javax.annotation.processing.RoundEnvironment; +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.TypeKind; +import javax.tools.Diagnostic; + +public class ProcessMethodAdapters extends ProcessDataBinding.ProcessingStep { + public ProcessMethodAdapters() { + } + + @Override + public boolean onHandleStep(RoundEnvironment roundEnv, + ProcessingEnvironment processingEnvironment, BindingBuildInfo buildInfo) { + L.d("processing adapters"); + final ModelAnalyzer modelAnalyzer = ModelAnalyzer.getInstance(); + Preconditions.checkNotNull(modelAnalyzer, "Model analyzer should be" + + " initialized first"); + SetterStore store = SetterStore.get(modelAnalyzer); + clearIncrementalClasses(roundEnv, store); + + addBindingAdapters(roundEnv, processingEnvironment, store); + addRenamed(roundEnv, processingEnvironment, store); + addConversions(roundEnv, processingEnvironment, store); + addUntaggable(roundEnv, processingEnvironment, store); + + try { + store.write(buildInfo.modulePackage(), processingEnvironment); + } catch (IOException e) { + L.e(e, "Could not write BindingAdapter intermediate file."); + } + return true; + } + + @Override + public void onProcessingOver(RoundEnvironment roundEnvironment, + ProcessingEnvironment processingEnvironment, BindingBuildInfo buildInfo) { + + } + + private void addBindingAdapters(RoundEnvironment roundEnv, ProcessingEnvironment + processingEnv, SetterStore store) { + for (Element element : AnnotationUtil + .getElementsAnnotatedWith(roundEnv, BindingAdapter.class)) { + if (element.getKind() != ElementKind.METHOD || + !element.getModifiers().contains(Modifier.STATIC) || + !element.getModifiers().contains(Modifier.PUBLIC)) { + L.e("@BindingAdapter on invalid element: %s", element); + continue; + } + BindingAdapter bindingAdapter = element.getAnnotation(BindingAdapter.class); + + ExecutableElement executableElement = (ExecutableElement) element; + List<? extends VariableElement> parameters = executableElement.getParameters(); + if (parameters.size() != 2) { + L.e("@BindingAdapter does not take two parameters: %s",element); + continue; + } + try { + L.d("------------------ @BindingAdapter for %s", element); + store.addBindingAdapter(bindingAdapter.value(), executableElement); + } catch (IllegalArgumentException e) { + L.e(e, "@BindingAdapter for duplicate View and parameter type: %s", element); + } + } + } + + private void addRenamed(RoundEnvironment roundEnv, ProcessingEnvironment processingEnv, + SetterStore store) { + for (Element element : AnnotationUtil + .getElementsAnnotatedWith(roundEnv, BindingMethods.class)) { + BindingMethods bindingMethods = element.getAnnotation(BindingMethods.class); + for (BindingMethod bindingMethod : bindingMethods.value()) { + store.addRenamedMethod(bindingMethod.attribute(), + bindingMethod.type(), bindingMethod.method(), (TypeElement) element); + } + } + } + + private void addConversions(RoundEnvironment roundEnv, + ProcessingEnvironment processingEnv, SetterStore store) { + for (Element element : AnnotationUtil + .getElementsAnnotatedWith(roundEnv, BindingConversion.class)) { + if (element.getKind() != ElementKind.METHOD || + !element.getModifiers().contains(Modifier.STATIC) || + !element.getModifiers().contains(Modifier.PUBLIC)) { + processingEnv.getMessager().printMessage(Diagnostic.Kind.ERROR, + "@BindingConversion is only allowed on public static methods: " + element); + continue; + } + + ExecutableElement executableElement = (ExecutableElement) element; + if (executableElement.getParameters().size() != 1) { + processingEnv.getMessager().printMessage(Diagnostic.Kind.ERROR, + "@BindingConversion method should have one parameter: " + element); + continue; + } + if (executableElement.getReturnType().getKind() == TypeKind.VOID) { + processingEnv.getMessager().printMessage(Diagnostic.Kind.ERROR, + "@BindingConversion method must return a value: " + element); + continue; + } + processingEnv.getMessager().printMessage(Diagnostic.Kind.NOTE, + "added conversion: " + element); + store.addConversionMethod(executableElement); + } + } + + private void addUntaggable(RoundEnvironment roundEnv, + ProcessingEnvironment processingEnv, SetterStore store) { + for (Element element : AnnotationUtil.getElementsAnnotatedWith(roundEnv, Untaggable.class)) { + Untaggable untaggable = element.getAnnotation(Untaggable.class); + store.addUntaggableTypes(untaggable.value(), (TypeElement) element); + } + } + + private void clearIncrementalClasses(RoundEnvironment roundEnv, SetterStore store) { + HashSet<String> classes = new HashSet<>(); + + for (Element element : AnnotationUtil + .getElementsAnnotatedWith(roundEnv, BindingAdapter.class)) { + TypeElement containingClass = (TypeElement) element.getEnclosingElement(); + classes.add(containingClass.getQualifiedName().toString()); + } + for (Element element : AnnotationUtil + .getElementsAnnotatedWith(roundEnv, BindingMethods.class)) { + classes.add(((TypeElement) element).getQualifiedName().toString()); + } + for (Element element : AnnotationUtil + .getElementsAnnotatedWith(roundEnv, BindingConversion.class)) { + classes.add(((TypeElement) element.getEnclosingElement()).getQualifiedName(). + toString()); + } + for (Element element : AnnotationUtil.getElementsAnnotatedWith(roundEnv, Untaggable.class)) { + classes.add(((TypeElement) element).getQualifiedName().toString()); + } + store.clear(classes); + } + +} diff --git a/tools/data-binding/annotationprocessor/src/main/resources/META-INF/services/javax.annotation.processing.Processor b/tools/data-binding/annotationprocessor/src/main/resources/META-INF/services/javax.annotation.processing.Processor new file mode 100644 index 0000000..482452e --- /dev/null +++ b/tools/data-binding/annotationprocessor/src/main/resources/META-INF/services/javax.annotation.processing.Processor @@ -0,0 +1 @@ +android.databinding.annotationprocessor.ProcessDataBinding
\ No newline at end of file diff --git a/tools/data-binding/baseLibrary/build.gradle b/tools/data-binding/baseLibrary/build.gradle new file mode 100644 index 0000000..ecb7205 --- /dev/null +++ b/tools/data-binding/baseLibrary/build.gradle @@ -0,0 +1,58 @@ +/* + * Copyright (C) 2014 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. + */ + +apply plugin: 'java' +apply plugin: 'maven' +apply plugin: 'application' + +sourceCompatibility = config.javaTargetCompatibility +targetCompatibility = config.javaSourceCompatibility + +buildscript { + repositories { + mavenLocal() + mavenCentral() + } +} + +repositories { + mavenCentral() +} + +sourceSets { + main { + java { + srcDir 'src/main/java' + } + } + test { + java { + srcDir 'src/test/java' + } + } +} + +dependencies { + testCompile 'junit:junit:4.11' +} + +uploadArchives { + repositories { + mavenDeployer { + pom.artifactId = 'baseLibrary' + } + } +} diff --git a/tools/data-binding/baseLibrary/src/main/java/android/databinding/Bindable.java b/tools/data-binding/baseLibrary/src/main/java/android/databinding/Bindable.java new file mode 100644 index 0000000..3dcebdd --- /dev/null +++ b/tools/data-binding/baseLibrary/src/main/java/android/databinding/Bindable.java @@ -0,0 +1,26 @@ +/* + * Copyright (C) 2014 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 android.databinding; + +import java.lang.annotation.ElementType; +import java.lang.annotation.Retention; +import java.lang.annotation.RetentionPolicy; +import java.lang.annotation.Target; + +@Target({ElementType.FIELD, ElementType.METHOD}) +@Retention(RetentionPolicy.RUNTIME) // this is necessary for java analyzer to work +public @interface Bindable { +} diff --git a/tools/data-binding/baseLibrary/src/main/java/android/databinding/BindingAdapter.java b/tools/data-binding/baseLibrary/src/main/java/android/databinding/BindingAdapter.java new file mode 100644 index 0000000..3d50ed3 --- /dev/null +++ b/tools/data-binding/baseLibrary/src/main/java/android/databinding/BindingAdapter.java @@ -0,0 +1,24 @@ +/* + * 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 android.databinding; + +import java.lang.annotation.ElementType; +import java.lang.annotation.Target; + +@Target(ElementType.METHOD) +public @interface BindingAdapter { + String value(); +} diff --git a/tools/data-binding/baseLibrary/src/main/java/android/databinding/BindingBuildInfo.java b/tools/data-binding/baseLibrary/src/main/java/android/databinding/BindingBuildInfo.java new file mode 100644 index 0000000..cbe1e99 --- /dev/null +++ b/tools/data-binding/baseLibrary/src/main/java/android/databinding/BindingBuildInfo.java @@ -0,0 +1,36 @@ +/* + * 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 android.databinding; + +import java.lang.annotation.ElementType; +import java.lang.annotation.Retention; +import java.lang.annotation.RetentionPolicy; +import java.lang.annotation.Target; + +@Target({ElementType.TYPE}) +@Retention(RetentionPolicy.SOURCE) +public @interface BindingBuildInfo { + String buildId(); + String modulePackage(); + String sdkRoot(); + int minSdk(); + + /** + * The folder that includes xml files which are exported by aapt or gradle plugin from layout files + */ + String layoutInfoDir(); + boolean isLibrary(); +} diff --git a/tools/data-binding/baseLibrary/src/main/java/android/databinding/BindingConversion.java b/tools/data-binding/baseLibrary/src/main/java/android/databinding/BindingConversion.java new file mode 100644 index 0000000..9942f5c --- /dev/null +++ b/tools/data-binding/baseLibrary/src/main/java/android/databinding/BindingConversion.java @@ -0,0 +1,24 @@ +/* + * 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 android.databinding; + + +import java.lang.annotation.ElementType; +import java.lang.annotation.Target; + +@Target({ElementType.METHOD}) +public @interface BindingConversion { +} diff --git a/tools/data-binding/baseLibrary/src/main/java/android/databinding/BindingMethod.java b/tools/data-binding/baseLibrary/src/main/java/android/databinding/BindingMethod.java new file mode 100644 index 0000000..6384409 --- /dev/null +++ b/tools/data-binding/baseLibrary/src/main/java/android/databinding/BindingMethod.java @@ -0,0 +1,22 @@ +/* + * 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 android.databinding; + +public @interface BindingMethod { + String type(); + String attribute(); + String method(); +} diff --git a/tools/data-binding/baseLibrary/src/main/java/android/databinding/BindingMethods.java b/tools/data-binding/baseLibrary/src/main/java/android/databinding/BindingMethods.java new file mode 100644 index 0000000..a3d39f8 --- /dev/null +++ b/tools/data-binding/baseLibrary/src/main/java/android/databinding/BindingMethods.java @@ -0,0 +1,24 @@ +/* + * 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 android.databinding; + +import java.lang.annotation.ElementType; +import java.lang.annotation.Target; + +@Target({ElementType.TYPE}) +public @interface BindingMethods { + BindingMethod[] value(); +} diff --git a/tools/data-binding/baseLibrary/src/main/java/android/databinding/CallbackRegistry.java b/tools/data-binding/baseLibrary/src/main/java/android/databinding/CallbackRegistry.java new file mode 100644 index 0000000..002692f --- /dev/null +++ b/tools/data-binding/baseLibrary/src/main/java/android/databinding/CallbackRegistry.java @@ -0,0 +1,407 @@ +/* + * Copyright (C) 2014 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 android.databinding; + +import java.util.ArrayList; +import java.util.List; + +/** + * Tracks callbacks for the event. This class supports reentrant modification + * of the callbacks during notification without adversely disrupting notifications. + * A common pattern for callbacks is to receive a notification and then remove + * themselves. This class handles this behavior with constant memory under + * most circumstances. + * + * <p>A subclass of {@link CallbackRegistry.NotifierCallback} must be passed to + * the constructor to define how notifications should be called. That implementation + * does the actual notification on the listener.</p> + * + * <p>This class supports only callbacks with at most two parameters. + * Typically, these are the notification originator and a parameter, but these may + * be used as required. If more than two parameters are required or primitive types + * must be used, <code>A</code> should be some kind of containing structure that + * the subclass may reuse between notifications.</p> + * + * @param <C> The callback type. + * @param <T> The notification sender type. Typically this is the containing class. + * @param <A> Opaque argument used to pass additional data beyond an int. + */ +public class CallbackRegistry<C, T, A> implements Cloneable { + private static final String TAG = "CallbackRegistry"; + + /** An ordered collection of listeners waiting to be notified. */ + private List<C> mCallbacks = new ArrayList<C>(); + + /** + * A bit flag for the first 64 listeners that are removed during notification. + * The lowest significant bit corresponds to the 0th index into mCallbacks. + * For a small number of callbacks, no additional array of objects needs to + * be allocated. + */ + private long mFirst64Removed = 0x0; + + /** + * Bit flags for the remaining callbacks that are removed during notification. + * When there are more than 64 callbacks and one is marked for removal, a dynamic + * array of bits are allocated for the callbacks. + */ + private long[] mRemainderRemoved; + + /** The recursion level of the notification */ + private int mNotificationLevel; + + /** The notification mechanism for notifying an event. */ + private final NotifierCallback<C, T, A> mNotifier; + + /** + * Creates an EventRegistry that notifies the event with notifier. + * @param notifier The class to use to notify events. + */ + public CallbackRegistry(NotifierCallback<C, T, A> notifier) { + mNotifier = notifier; + } + + /** + * Notify all callbacks. + * + * @param sender The originator. This is an opaque parameter passed to + * {@link CallbackRegistry.NotifierCallback#onNotifyCallback(Object, Object, int, A)} + * @param arg An opaque parameter passed to + * {@link CallbackRegistry.NotifierCallback#onNotifyCallback(Object, Object, int, A)} + * @param arg2 An opaque parameter passed to + * {@link CallbackRegistry.NotifierCallback#onNotifyCallback(Object, Object, int, A)} + */ + public synchronized void notifyCallbacks(T sender, int arg, A arg2) { + mNotificationLevel++; + notifyRecurse(sender, arg, arg2); + mNotificationLevel--; + if (mNotificationLevel == 0) { + if (mRemainderRemoved != null) { + for (int i = mRemainderRemoved.length - 1; i >= 0; i--) { + final long removedBits = mRemainderRemoved[i]; + if (removedBits != 0) { + removeRemovedCallbacks((i + 1) * Long.SIZE, removedBits); + mRemainderRemoved[i] = 0; + } + } + } + if (mFirst64Removed != 0) { + removeRemovedCallbacks(0, mFirst64Removed); + mFirst64Removed = 0; + } + } + } + + /** + * Notify up to the first Long.SIZE callbacks that don't have a bit set in <code>removed</code>. + * + * @param sender The originator. This is an opaque parameter passed to + * {@link CallbackRegistry.NotifierCallback#onNotifyCallback(Object, Object, int, A)} + * @param arg An opaque parameter passed to + * {@link CallbackRegistry.NotifierCallback#onNotifyCallback(Object, Object, int, A)} + * @param arg2 An opaque parameter passed to + * {@link CallbackRegistry.NotifierCallback#onNotifyCallback(Object, Object, int, A)} + */ + private void notifyFirst64(T sender, int arg, A arg2) { + final int maxNotified = Math.min(Long.SIZE, mCallbacks.size()); + notifyCallbacks(sender, arg, arg2, 0, maxNotified, mFirst64Removed); + } + + /** + * Notify all callbacks using a recursive algorithm to avoid allocating on the heap. + * This part captures the callbacks beyond Long.SIZE that have no bits allocated for + * removal before it recurses into {@link #notifyRemainder(Object, int, A, int)}. + * + * <p>Recursion is used to avoid allocating temporary state on the heap.</p> + * + * @param sender The originator. This is an opaque parameter passed to + * {@link CallbackRegistry.NotifierCallback#onNotifyCallback(Object, Object, int, A)} + * @param arg An opaque parameter passed to + * {@link CallbackRegistry.NotifierCallback#onNotifyCallback(Object, Object, int, A)} + * @param arg2 An opaque parameter passed to + * {@link CallbackRegistry.NotifierCallback#onNotifyCallback(Object, Object, int, A)} + */ + private void notifyRecurse(T sender, int arg, A arg2) { + final int callbackCount = mCallbacks.size(); + final int remainderIndex = mRemainderRemoved == null ? -1 : mRemainderRemoved.length - 1; + + // Now we've got all callbakcs that have no mRemainderRemoved value, so notify the + // others. + notifyRemainder(sender, arg, arg2, remainderIndex); + + // notifyRemainder notifies all at maxIndex, so we'd normally start at maxIndex + 1 + // However, we must also keep track of those in mFirst64Removed, so we add 2 instead: + final int startCallbackIndex = (remainderIndex + 2) * Long.SIZE; + + // The remaining have no bit set + notifyCallbacks(sender, arg, arg2, startCallbackIndex, callbackCount, 0); + } + + /** + * Notify callbacks that have mRemainderRemoved bits set for remainderIndex. If + * remainderIndex is -1, the first 64 will be notified instead. + * + * @param sender The originator. This is an opaque parameter passed to + * {@link CallbackRegistry.NotifierCallback#onNotifyCallback(Object, Object, int, A)} + * @param arg An opaque parameter passed to + * {@link CallbackRegistry.NotifierCallback#onNotifyCallback(Object, Object, int, A)} + * @param arg2 An opaque parameter passed to + * {@link CallbackRegistry.NotifierCallback#onNotifyCallback(Object, Object, int, A)} + * @param remainderIndex The index into mRemainderRemoved that should be notified. + */ + private void notifyRemainder(T sender, int arg, A arg2, int remainderIndex) { + if (remainderIndex < 0) { + notifyFirst64(sender, arg, arg2); + } else { + final long bits = mRemainderRemoved[remainderIndex]; + final int startIndex = (remainderIndex + 1) * Long.SIZE; + final int endIndex = Math.min(mCallbacks.size(), startIndex + Long.SIZE); + notifyRemainder(sender, arg, arg2, remainderIndex - 1); + notifyCallbacks(sender, arg, arg2, startIndex, endIndex, bits); + } + } + + /** + * Notify callbacks from startIndex to endIndex, using bits as the bit status + * for whether they have been removed or not. bits should be from mRemainderRemoved or + * mFirst64Removed. bits set to 0 indicates that all callbacks from startIndex to + * endIndex should be notified. + * + * @param sender The originator. This is an opaque parameter passed to + * {@link CallbackRegistry.NotifierCallback#onNotifyCallback(Object, Object, int, A)} + * @param arg An opaque parameter passed to + * {@link CallbackRegistry.NotifierCallback#onNotifyCallback(Object, Object, int, A)} + * @param arg2 An opaque parameter passed to + * {@link CallbackRegistry.NotifierCallback#onNotifyCallback(Object, Object, int, A)} + * @param startIndex The index into the mCallbacks to start notifying. + * @param endIndex One past the last index into mCallbacks to notify. + * @param bits A bit field indicating which callbacks have been removed and shouldn't + * be notified. + */ + private void notifyCallbacks(T sender, int arg, A arg2, final int startIndex, + final int endIndex, final long bits) { + long bitMask = 1; + for (int i = startIndex; i < endIndex; i++) { + if ((bits & bitMask) == 0) { + mNotifier.onNotifyCallback(mCallbacks.get(i), sender, arg, arg2); + } + bitMask <<= 1; + } + } + + /** + * Add a callback to be notified. If the callback is already in the list, another won't + * be added. This does not affect current notifications. + * @param callback The callback to add. + */ + public synchronized void add(C callback) { + int index = mCallbacks.lastIndexOf(callback); + if (index < 0 || isRemoved(index)) { + mCallbacks.add(callback); + } + } + + /** + * Returns true if the callback at index has been marked for removal. + * + * @param index The index into mCallbacks to check. + * @return true if the callback at index has been marked for removal. + */ + private boolean isRemoved(int index) { + if (index < Long.SIZE) { + // It is in the first 64 callbacks, just check the bit. + final long bitMask = 1L << index; + return (mFirst64Removed & bitMask) != 0; + } else if (mRemainderRemoved == null) { + // It is after the first 64 callbacks, but nothing else was marked for removal. + return false; + } else { + final int maskIndex = (index / Long.SIZE) - 1; + if (maskIndex >= mRemainderRemoved.length) { + // There are some items in mRemainderRemoved, but nothing at the given index. + return false; + } else { + // There is something marked for removal, so we have to check the bit. + final long bits = mRemainderRemoved[maskIndex]; + final long bitMask = 1L << (index % Long.SIZE); + return (bits & bitMask) != 0; + } + } + } + + /** + * Removes callbacks from startIndex to startIndex + Long.SIZE, based + * on the bits set in removed. + * @param startIndex The index into the mCallbacks to start removing callbacks. + * @param removed The bits indicating removal, where each bit is set for one callback + * to be removed. + */ + private void removeRemovedCallbacks(int startIndex, long removed) { + // The naive approach should be fine. There may be a better bit-twiddling approach. + final int endIndex = startIndex + Long.SIZE; + + long bitMask = 1L << (Long.SIZE - 1); + for (int i = endIndex - 1; i >= startIndex; i--) { + if ((removed & bitMask) != 0) { + mCallbacks.remove(i); + } + bitMask >>>= 1; + } + } + + /** + * Remove a callback. This callback won't be notified after this call completes. + * @param callback The callback to remove. + */ + public synchronized void remove(C callback) { + if (mNotificationLevel == 0) { + mCallbacks.remove(callback); + } else { + int index = mCallbacks.lastIndexOf(callback); + if (index >= 0) { + setRemovalBit(index); + } + } + } + + private void setRemovalBit(int index) { + if (index < Long.SIZE) { + // It is in the first 64 callbacks, just check the bit. + final long bitMask = 1L << index; + mFirst64Removed |= bitMask; + } else { + final int remainderIndex = (index / Long.SIZE) - 1; + if (mRemainderRemoved == null) { + mRemainderRemoved = new long[mCallbacks.size() / Long.SIZE]; + } else if (mRemainderRemoved.length < remainderIndex) { + // need to make it bigger + long[] newRemainders = new long[mCallbacks.size() / Long.SIZE]; + System.arraycopy(mRemainderRemoved, 0, newRemainders, 0, mRemainderRemoved.length); + mRemainderRemoved = newRemainders; + } + final long bitMask = 1L << (index % Long.SIZE); + mRemainderRemoved[remainderIndex] |= bitMask; + } + } + + /* + private void clearRemovalBit(int index) { + if (index < Long.SIZE) { + // It is in the first 64 callbacks, just check the bit. + final long bitMask = 1L << index; + mFirst64Removed &= ~bitMask; + } else if (mRemainderRemoved != null) { + final int maskIndex = (index / Long.SIZE) - 1; + if (maskIndex < mRemainderRemoved.length) { + // There is something marked for removal, so we have to check the bit. + final long bitMask = 1L << (index % Long.SIZE); + mRemainderRemoved[maskIndex] &= ~bitMask; + } + } + } + */ + + /** + * Makes a copy of the registered callbacks and returns it. + * + * @return a copy of the registered callbacks. + */ + public synchronized ArrayList<C> copyListeners() { + ArrayList<C> callbacks = new ArrayList<C>(mCallbacks.size()); + int numListeners = mCallbacks.size(); + for (int i = 0; i < numListeners; i++) { + if (!isRemoved(i)) { + callbacks.add(mCallbacks.get(i)); + } + } + return callbacks; + } + + /** + * Returns true if there are no registered callbacks or false otherwise. + * + * @return true if there are no registered callbacks or false otherwise. + */ + public synchronized boolean isEmpty() { + if (mCallbacks.isEmpty()) { + return true; + } else if (mNotificationLevel == 0) { + return false; + } else { + int numListeners = mCallbacks.size(); + for (int i = 0; i < numListeners; i++) { + if (!isRemoved(i)) { + return false; + } + } + return true; + } + } + + /** + * Removes all callbacks from the list. + */ + public synchronized void clear() { + if (mNotificationLevel == 0) { + mCallbacks.clear(); + } else if (!mCallbacks.isEmpty()) { + for (int i = mCallbacks.size() - 1; i >= 0; i--) { + setRemovalBit(i); + } + } + } + + public synchronized CallbackRegistry<C, T, A> clone() { + CallbackRegistry<C, T, A> clone = null; + try { + clone = (CallbackRegistry<C, T, A>) super.clone(); + clone.mFirst64Removed = 0; + clone.mRemainderRemoved = null; + clone.mNotificationLevel = 0; + clone.mCallbacks = new ArrayList<C>(); + final int numListeners = mCallbacks.size(); + for (int i = 0; i < numListeners; i++) { + if (!isRemoved(i)) { + clone.mCallbacks.add(mCallbacks.get(i)); + } + } + } catch (CloneNotSupportedException e) { + e.printStackTrace(); + } + return clone; + } + + /** + * Class used to notify events from CallbackRegistry. + * + * @param <C> The callback type. + * @param <T> The notification sender type. Typically this is the containing class. + * @param <A> An opaque argument to pass to the notifier + */ + public abstract static class NotifierCallback<C, T, A> { + /** + * Used to notify the callback. + * + * @param callback The callback to notify. + * @param sender The opaque sender object. + * @param arg The opaque notification parameter. + * @param arg2 An opaque argument passed in + * {@link CallbackRegistry#notifyCallbacks} + * @see CallbackRegistry#CallbackRegistry(CallbackRegistry.NotifierCallback) + */ + public abstract void onNotifyCallback(C callback, T sender, int arg, A arg2); + } +} diff --git a/tools/data-binding/baseLibrary/src/main/java/android/databinding/Observable.java b/tools/data-binding/baseLibrary/src/main/java/android/databinding/Observable.java new file mode 100644 index 0000000..caca167 --- /dev/null +++ b/tools/data-binding/baseLibrary/src/main/java/android/databinding/Observable.java @@ -0,0 +1,24 @@ +/* + * Copyright (C) 2014 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 android.databinding; + +public interface Observable { + + public void addOnPropertyChangedListener(OnPropertyChangedListener listener); + + public void removeOnPropertyChangedListener(OnPropertyChangedListener listener); +} diff --git a/tools/data-binding/baseLibrary/src/main/java/android/databinding/ObservableList.java b/tools/data-binding/baseLibrary/src/main/java/android/databinding/ObservableList.java new file mode 100644 index 0000000..3b82cf1 --- /dev/null +++ b/tools/data-binding/baseLibrary/src/main/java/android/databinding/ObservableList.java @@ -0,0 +1,23 @@ +/* + * 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 android.databinding; + +import java.util.List; + +public interface ObservableList<T> extends List<T> { + void addOnListChangedListener(OnListChangedListener listener); + void removeOnListChangedListener(OnListChangedListener listener); +} diff --git a/tools/data-binding/baseLibrary/src/main/java/android/databinding/ObservableMap.java b/tools/data-binding/baseLibrary/src/main/java/android/databinding/ObservableMap.java new file mode 100644 index 0000000..9240c48 --- /dev/null +++ b/tools/data-binding/baseLibrary/src/main/java/android/databinding/ObservableMap.java @@ -0,0 +1,23 @@ +/* + * 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 android.databinding; + +import java.util.Map; + +public interface ObservableMap<K, V> extends Map<K, V> { + void addOnMapChangedListener(OnMapChangedListener<? extends ObservableMap<K, V>, K> listener); + void removeOnMapChangedListener(OnMapChangedListener<? extends ObservableMap<K, V>, K> listener); +} diff --git a/tools/data-binding/baseLibrary/src/main/java/android/databinding/OnListChangedListener.java b/tools/data-binding/baseLibrary/src/main/java/android/databinding/OnListChangedListener.java new file mode 100644 index 0000000..a76269e --- /dev/null +++ b/tools/data-binding/baseLibrary/src/main/java/android/databinding/OnListChangedListener.java @@ -0,0 +1,24 @@ +/* + * 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 android.databinding; + +public interface OnListChangedListener { + void onChanged(); + void onItemRangeChanged(int positionStart, int itemCount); + void onItemRangeInserted(int positionStart, int itemCount); + void onItemRangeMoved(int fromPosition, int toPosition, int itemCount); + void onItemRangeRemoved(int positionStart, int itemCount); +} diff --git a/tools/data-binding/baseLibrary/src/main/java/android/databinding/OnMapChangedListener.java b/tools/data-binding/baseLibrary/src/main/java/android/databinding/OnMapChangedListener.java new file mode 100644 index 0000000..647b1f7 --- /dev/null +++ b/tools/data-binding/baseLibrary/src/main/java/android/databinding/OnMapChangedListener.java @@ -0,0 +1,20 @@ +/* + * Copyright (C) 2015 The Android Open Source Project + * + * Licensed under the Apache License, Version 2.0 (the "License"); + * you may not use this file except in compliance with the License. + * You may obtain a copy of the License at + * + * http://www.apache.org/licenses/LICENSE-2.0 + * + * Unless required by applicable law or agreed to in writing, software + * distributed under the License is distributed on an "AS IS" BASIS, + * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. + * See the License for the specific language governing permissions and + * limitations under the License. + */ +package android.databinding; + +public interface OnMapChangedListener<T extends ObservableMap<K, ?>, K> { + void onMapChanged(T sender, K key); +} diff --git a/tools/data-binding/baseLibrary/src/main/java/android/databinding/OnPropertyChangedListener.java b/tools/data-binding/baseLibrary/src/main/java/android/databinding/OnPropertyChangedListener.java new file mode 100644 index 0000000..4103f07 --- /dev/null +++ b/tools/data-binding/baseLibrary/src/main/java/android/databinding/OnPropertyChangedListener.java @@ -0,0 +1,21 @@ +/* + * Copyright (C) 2014 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 android.databinding; + +public interface OnPropertyChangedListener { + public void onPropertyChanged(Observable sender, int fieldId); +} diff --git a/tools/data-binding/baseLibrary/src/main/java/android/databinding/Untaggable.java b/tools/data-binding/baseLibrary/src/main/java/android/databinding/Untaggable.java new file mode 100644 index 0000000..a1ce3ac --- /dev/null +++ b/tools/data-binding/baseLibrary/src/main/java/android/databinding/Untaggable.java @@ -0,0 +1,24 @@ +/* + * 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 android.databinding; + +import java.lang.annotation.ElementType; +import java.lang.annotation.Target; + +@Target({ElementType.TYPE}) +public @interface Untaggable { + String[] value(); +} diff --git a/tools/data-binding/build.gradle b/tools/data-binding/build.gradle new file mode 100644 index 0000000..675f196 --- /dev/null +++ b/tools/data-binding/build.gradle @@ -0,0 +1,74 @@ +Properties databindingProperties = new Properties() +databindingProperties.load(new FileInputStream("${projectDir}/databinding.properties")) +databindingProperties.mavenRepoDir = "${projectDir}/${databindingProperties.mavenRepoName}" +ext.config = databindingProperties + +println "local maven repo is ${ext.config.mavenRepoDir}." + +new File(ext.config.mavenRepoDir).mkdir() +subprojects { + apply plugin: 'maven' + group = config.group + version = config.snapshotVersion + repositories { + mavenCentral() + maven { + url "file://${config.mavenRepoDir}" + } + } + uploadArchives { + repositories { + mavenDeployer { + repository(url: "file://${config.mavenRepoDir}") + } + } + } +} + +task deleteRepo(type: Delete) { + delete "${config.mavenRepoDir}" +} + +def buildExtensionsTask = project.tasks.create "buildExtensionsTask", Exec +buildExtensionsTask.workingDir file('extensions').getAbsolutePath() +//on linux +buildExtensionsTask.commandLine './gradlew' +buildExtensionsTask.args 'clean', 'uploadArchives', '--info', '--stacktrace' +buildExtensionsTask.dependsOn subprojects.uploadArchives + +file('integration-tests').listFiles().findAll { it.isDirectory() }.each { + println("Creating run test task for ${it.getAbsolutePath()}.") + def testTask = project.tasks.create "runTestsOf${it.getName().capitalize()}", Exec + testTask.workingDir it.getAbsolutePath() + //on linux + testTask.commandLine './gradlew' + testTask.args 'clean', 'connectedCheck', '--info', '--stacktrace' + testTask.dependsOn subprojects.uploadArchives + testTask.dependsOn buildExtensionsTask +} + +task runIntegrationTests { + dependsOn tasks.findAll { task -> task.name.startsWith('runTestsOf') } +} + +task runAllTests { + dependsOn runIntegrationTests +} + +allprojects { + afterEvaluate { project -> + runAllTests.dependsOn project.tasks.findAll {task -> task.name.equals('test')} + runAllTests.dependsOn project.tasks.findAll {task -> task.name.equals('connectedCheck')} + } +} + +subprojects.uploadArchives.each { it.shouldRunAfter deleteRepo } +buildExtensionsTask.shouldRunAfter deleteRepo +tasks['runTestsOfMultiModuleTestApp'].dependsOn tasks['runTestsOfIndependentLibrary'] + + +task rebuildRepo() { + dependsOn deleteRepo + dependsOn subprojects.uploadArchives + dependsOn buildExtensionsTask +}
\ No newline at end of file diff --git a/tools/data-binding/compiler/build.gradle b/tools/data-binding/compiler/build.gradle new file mode 100644 index 0000000..af0aaf0 --- /dev/null +++ b/tools/data-binding/compiler/build.gradle @@ -0,0 +1,63 @@ +/* + * Copyright (C) 2014 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. + */ + +apply plugin: 'java' +apply plugin: "kotlin" + + +sourceCompatibility = config.javaTargetCompatibility +targetCompatibility = config.javaSourceCompatibility + +buildscript { + repositories { + mavenCentral() + } + dependencies { + classpath "org.jetbrains.kotlin:kotlin-gradle-plugin:${config.kotlinVersion}" + } +} + + +dependencies { + compile 'junit:junit:4.12' + compile 'org.apache.commons:commons-lang3:3.3.2' + compile 'org.apache.commons:commons-io:1.3.2' + compile 'com.google.guava:guava:18.0' + compile "org.jetbrains.kotlin:kotlin-stdlib:${config.kotlinVersion}" + compile 'commons-codec:commons-codec:1.10' + compile project(":baseLibrary") + compile project(":grammarBuilder") + compile project(":xmlGrammar") + testCompile "com.android.databinding:libraryJar:$version@jar" +} + +task fatJar(type: Jar) { + baseName = project.name + '-all' + from { configurations.compile.collect { it.isDirectory() ? it : zipTree(it) } } + with jar +} + +uploadArchives { + repositories { + mavenDeployer { + pom.artifactId = 'compiler' + } + } +} + +project(':library').afterEvaluate { libProject -> + tasks['compileTestKotlin'].dependsOn libProject.tasks['uploadJarArchives'] +} diff --git a/tools/data-binding/compiler/gradle/wrapper/gradle-wrapper.jar b/tools/data-binding/compiler/gradle/wrapper/gradle-wrapper.jar Binary files differnew file mode 100644 index 0000000..3d0dee6 --- /dev/null +++ b/tools/data-binding/compiler/gradle/wrapper/gradle-wrapper.jar diff --git a/tools/data-binding/compiler/gradle/wrapper/gradle-wrapper.properties b/tools/data-binding/compiler/gradle/wrapper/gradle-wrapper.properties new file mode 100644 index 0000000..29fb85e --- /dev/null +++ b/tools/data-binding/compiler/gradle/wrapper/gradle-wrapper.properties @@ -0,0 +1,6 @@ +#Thu Dec 11 16:05:38 PST 2014 +distributionBase=GRADLE_USER_HOME +distributionPath=wrapper/dists +zipStoreBase=GRADLE_USER_HOME +zipStorePath=wrapper/dists +distributionUrl=https\://services.gradle.org/distributions/gradle-2.1-bin.zip diff --git a/tools/data-binding/compiler/gradlew b/tools/data-binding/compiler/gradlew new file mode 100755 index 0000000..91a7e26 --- /dev/null +++ b/tools/data-binding/compiler/gradlew @@ -0,0 +1,164 @@ +#!/usr/bin/env bash + +############################################################################## +## +## Gradle start up script for UN*X +## +############################################################################## + +# Add default JVM options here. You can also use JAVA_OPTS and GRADLE_OPTS to pass JVM options to this script. +DEFAULT_JVM_OPTS="" + +APP_NAME="Gradle" +APP_BASE_NAME=`basename "$0"` + +# Use the maximum available, or set MAX_FD != -1 to use that value. +MAX_FD="maximum" + +warn ( ) { + echo "$*" +} + +die ( ) { + echo + echo "$*" + echo + exit 1 +} + +# OS specific support (must be 'true' or 'false'). +cygwin=false +msys=false +darwin=false +case "`uname`" in + CYGWIN* ) + cygwin=true + ;; + Darwin* ) + darwin=true + ;; + MINGW* ) + msys=true + ;; +esac + +# For Cygwin, ensure paths are in UNIX format before anything is touched. +if $cygwin ; then + [ -n "$JAVA_HOME" ] && JAVA_HOME=`cygpath --unix "$JAVA_HOME"` +fi + +# Attempt to set APP_HOME +# Resolve links: $0 may be a link +PRG="$0" +# Need this for relative symlinks. +while [ -h "$PRG" ] ; do + ls=`ls -ld "$PRG"` + link=`expr "$ls" : '.*-> \(.*\)$'` + if expr "$link" : '/.*' > /dev/null; then + PRG="$link" + else + PRG=`dirname "$PRG"`"/$link" + fi +done +SAVED="`pwd`" +cd "`dirname \"$PRG\"`/" >&- +APP_HOME="`pwd -P`" +cd "$SAVED" >&- + +CLASSPATH=$APP_HOME/gradle/wrapper/gradle-wrapper.jar + +# Determine the Java command to use to start the JVM. +if [ -n "$JAVA_HOME" ] ; then + if [ -x "$JAVA_HOME/jre/sh/java" ] ; then + # IBM's JDK on AIX uses strange locations for the executables + JAVACMD="$JAVA_HOME/jre/sh/java" + else + JAVACMD="$JAVA_HOME/bin/java" + fi + if [ ! -x "$JAVACMD" ] ; then + die "ERROR: JAVA_HOME is set to an invalid directory: $JAVA_HOME + +Please set the JAVA_HOME variable in your environment to match the +location of your Java installation." + fi +else + JAVACMD="java" + which java >/dev/null 2>&1 || die "ERROR: JAVA_HOME is not set and no 'java' command could be found in your PATH. + +Please set the JAVA_HOME variable in your environment to match the +location of your Java installation." +fi + +# Increase the maximum file descriptors if we can. +if [ "$cygwin" = "false" -a "$darwin" = "false" ] ; then + MAX_FD_LIMIT=`ulimit -H -n` + if [ $? -eq 0 ] ; then + if [ "$MAX_FD" = "maximum" -o "$MAX_FD" = "max" ] ; then + MAX_FD="$MAX_FD_LIMIT" + fi + ulimit -n $MAX_FD + if [ $? -ne 0 ] ; then + warn "Could not set maximum file descriptor limit: $MAX_FD" + fi + else + warn "Could not query maximum file descriptor limit: $MAX_FD_LIMIT" + fi +fi + +# For Darwin, add options to specify how the application appears in the dock +if $darwin; then + GRADLE_OPTS="$GRADLE_OPTS \"-Xdock:name=$APP_NAME\" \"-Xdock:icon=$APP_HOME/media/gradle.icns\"" +fi + +# For Cygwin, switch paths to Windows format before running java +if $cygwin ; then + APP_HOME=`cygpath --path --mixed "$APP_HOME"` + CLASSPATH=`cygpath --path --mixed "$CLASSPATH"` + + # We build the pattern for arguments to be converted via cygpath + ROOTDIRSRAW=`find -L / -maxdepth 1 -mindepth 1 -type d 2>/dev/null` + SEP="" + for dir in $ROOTDIRSRAW ; do + ROOTDIRS="$ROOTDIRS$SEP$dir" + SEP="|" + done + OURCYGPATTERN="(^($ROOTDIRS))" + # Add a user-defined pattern to the cygpath arguments + if [ "$GRADLE_CYGPATTERN" != "" ] ; then + OURCYGPATTERN="$OURCYGPATTERN|($GRADLE_CYGPATTERN)" + fi + # Now convert the arguments - kludge to limit ourselves to /bin/sh + i=0 + for arg in "$@" ; do + CHECK=`echo "$arg"|egrep -c "$OURCYGPATTERN" -` + CHECK2=`echo "$arg"|egrep -c "^-"` ### Determine if an option + + if [ $CHECK -ne 0 ] && [ $CHECK2 -eq 0 ] ; then ### Added a condition + eval `echo args$i`=`cygpath --path --ignore --mixed "$arg"` + else + eval `echo args$i`="\"$arg\"" + fi + i=$((i+1)) + done + case $i in + (0) set -- ;; + (1) set -- "$args0" ;; + (2) set -- "$args0" "$args1" ;; + (3) set -- "$args0" "$args1" "$args2" ;; + (4) set -- "$args0" "$args1" "$args2" "$args3" ;; + (5) set -- "$args0" "$args1" "$args2" "$args3" "$args4" ;; + (6) set -- "$args0" "$args1" "$args2" "$args3" "$args4" "$args5" ;; + (7) set -- "$args0" "$args1" "$args2" "$args3" "$args4" "$args5" "$args6" ;; + (8) set -- "$args0" "$args1" "$args2" "$args3" "$args4" "$args5" "$args6" "$args7" ;; + (9) set -- "$args0" "$args1" "$args2" "$args3" "$args4" "$args5" "$args6" "$args7" "$args8" ;; + esac +fi + +# Split up the JVM_OPTS And GRADLE_OPTS values into an array, following the shell quoting and substitution rules +function splitJvmOpts() { + JVM_OPTS=("$@") +} +eval splitJvmOpts $DEFAULT_JVM_OPTS $JAVA_OPTS $GRADLE_OPTS +JVM_OPTS[${#JVM_OPTS[*]}]="-Dorg.gradle.appname=$APP_BASE_NAME" + +exec "$JAVACMD" "${JVM_OPTS[@]}" -classpath "$CLASSPATH" org.gradle.wrapper.GradleWrapperMain "$@" diff --git a/tools/data-binding/compiler/gradlew.bat b/tools/data-binding/compiler/gradlew.bat new file mode 100644 index 0000000..aec9973 --- /dev/null +++ b/tools/data-binding/compiler/gradlew.bat @@ -0,0 +1,90 @@ +@if "%DEBUG%" == "" @echo off
+@rem ##########################################################################
+@rem
+@rem Gradle startup script for Windows
+@rem
+@rem ##########################################################################
+
+@rem Set local scope for the variables with windows NT shell
+if "%OS%"=="Windows_NT" setlocal
+
+@rem Add default JVM options here. You can also use JAVA_OPTS and GRADLE_OPTS to pass JVM options to this script.
+set DEFAULT_JVM_OPTS=
+
+set DIRNAME=%~dp0
+if "%DIRNAME%" == "" set DIRNAME=.
+set APP_BASE_NAME=%~n0
+set APP_HOME=%DIRNAME%
+
+@rem Find java.exe
+if defined JAVA_HOME goto findJavaFromJavaHome
+
+set JAVA_EXE=java.exe
+%JAVA_EXE% -version >NUL 2>&1
+if "%ERRORLEVEL%" == "0" goto init
+
+echo.
+echo ERROR: JAVA_HOME is not set and no 'java' command could be found in your PATH.
+echo.
+echo Please set the JAVA_HOME variable in your environment to match the
+echo location of your Java installation.
+
+goto fail
+
+:findJavaFromJavaHome
+set JAVA_HOME=%JAVA_HOME:"=%
+set JAVA_EXE=%JAVA_HOME%/bin/java.exe
+
+if exist "%JAVA_EXE%" goto init
+
+echo.
+echo ERROR: JAVA_HOME is set to an invalid directory: %JAVA_HOME%
+echo.
+echo Please set the JAVA_HOME variable in your environment to match the
+echo location of your Java installation.
+
+goto fail
+
+:init
+@rem Get command-line arguments, handling Windowz variants
+
+if not "%OS%" == "Windows_NT" goto win9xME_args
+if "%@eval[2+2]" == "4" goto 4NT_args
+
+:win9xME_args
+@rem Slurp the command line arguments.
+set CMD_LINE_ARGS=
+set _SKIP=2
+
+:win9xME_args_slurp
+if "x%~1" == "x" goto execute
+
+set CMD_LINE_ARGS=%*
+goto execute
+
+:4NT_args
+@rem Get arguments from the 4NT Shell from JP Software
+set CMD_LINE_ARGS=%$
+
+:execute
+@rem Setup the command line
+
+set CLASSPATH=%APP_HOME%\gradle\wrapper\gradle-wrapper.jar
+
+@rem Execute Gradle
+"%JAVA_EXE%" %DEFAULT_JVM_OPTS% %JAVA_OPTS% %GRADLE_OPTS% "-Dorg.gradle.appname=%APP_BASE_NAME%" -classpath "%CLASSPATH%" org.gradle.wrapper.GradleWrapperMain %CMD_LINE_ARGS%
+
+:end
+@rem End local scope for the variables with windows NT shell
+if "%ERRORLEVEL%"=="0" goto mainEnd
+
+:fail
+rem Set variable GRADLE_EXIT_CONSOLE if you need the _script_ return code instead of
+rem the _cmd.exe /c_ return code!
+if not "" == "%GRADLE_EXIT_CONSOLE%" exit 1
+exit /b 1
+
+:mainEnd
+if "%OS%"=="Windows_NT" endlocal
+
+:omega
diff --git a/tools/data-binding/compiler/src/main/java/android/databinding/tool/Binding.java b/tools/data-binding/compiler/src/main/java/android/databinding/tool/Binding.java new file mode 100644 index 0000000..f2bc96f --- /dev/null +++ b/tools/data-binding/compiler/src/main/java/android/databinding/tool/Binding.java @@ -0,0 +1,140 @@ +/* + * 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 android.databinding.tool; + +import android.databinding.tool.expr.Expr; +import android.databinding.tool.reflection.ModelAnalyzer; +import android.databinding.tool.reflection.ModelClass; +import android.databinding.tool.store.SetterStore; +import android.databinding.tool.store.SetterStore.SetterCall; + +public class Binding { + + private final String mName; + private final Expr mExpr; + private final BindingTarget mTarget; + private SetterStore.SetterCall mSetterCall; + + public Binding(BindingTarget target, String name, Expr expr) { + mTarget = target; + mName = name; + mExpr = expr; + } + + private SetterStore.SetterCall getSetterCall() { + if (mSetterCall == null) { + ModelClass viewType = mTarget.getResolvedType(); + if (viewType != null && viewType.extendsViewStub()) { + if (isViewStubAttribute()) { + mSetterCall = new ViewStubDirectCall(mName, viewType, mExpr); + } else { + mSetterCall = new ViewStubSetterCall(mName); + } + } else { + mSetterCall = SetterStore.get(ModelAnalyzer.getInstance()).getSetterCall(mName, + viewType, mExpr.getResolvedType(), mExpr.getModel().getImports()); + } + } + return mSetterCall; + } + + public BindingTarget getTarget() { + return mTarget; + } + + public String toJavaCode(String targetViewName, String expressionCode) { + return getSetterCall().toJava(targetViewName, expressionCode); + } + + /** + * The min api level in which this binding should be executed. + * <p> + * This should be the minimum value among the dependencies of this binding. For now, we only + * check the setter. + */ + public int getMinApi() { + return getSetterCall().getMinApi(); + } + +// private String resolveJavaCode(ModelAnalyzer modelAnalyzer) { +// +// } +//// return modelAnalyzer.findMethod(mTarget.getResolvedType(), mName, +//// Arrays.asList(mExpr.getResolvedType())); +// //} +// + + + public String getName() { + return mName; + } + + public Expr getExpr() { + return mExpr; + } + + private boolean isViewStubAttribute() { + if ("android:inflatedId".equals(mName)) { + return true; + } else if ("android:layout".equals(mName)) { + return true; + } else if ("android:visibility".equals(mName)) { + return true; + } else { + return false; + } + } + + private static class ViewStubSetterCall extends SetterCall { + private final String mName; + + public ViewStubSetterCall(String name) { + mName = name.substring(name.lastIndexOf(':') + 1); + } + + @Override + protected String toJavaInternal(String viewExpression, String converted) { + return "if (" + viewExpression + ".isInflated()) " + viewExpression + + ".getBinding().setVariable(BR." + mName + ", " + converted + ")"; + } + + @Override + public int getMinApi() { + return 0; + } + } + + private static class ViewStubDirectCall extends SetterCall { + private final SetterCall mWrappedCall; + + public ViewStubDirectCall(String name, ModelClass viewType, Expr expr) { + mWrappedCall = SetterStore.get(ModelAnalyzer.getInstance()).getSetterCall(name, + viewType, expr.getResolvedType(), expr.getModel().getImports()); + } + + @Override + protected String toJavaInternal(String viewExpression, String converted) { + return "if (!" + viewExpression + ".isInflated()) " + + mWrappedCall.toJava(viewExpression + ".getViewStub()", converted); + } + + @Override + public int getMinApi() { + return 0; + } + } +} diff --git a/tools/data-binding/compiler/src/main/java/android/databinding/tool/BindingTarget.java b/tools/data-binding/compiler/src/main/java/android/databinding/tool/BindingTarget.java new file mode 100644 index 0000000..79156c0 --- /dev/null +++ b/tools/data-binding/compiler/src/main/java/android/databinding/tool/BindingTarget.java @@ -0,0 +1,111 @@ +/* + * 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 android.databinding.tool; + +import android.databinding.tool.expr.Expr; +import android.databinding.tool.expr.ExprModel; +import android.databinding.tool.reflection.ModelAnalyzer; +import android.databinding.tool.reflection.ModelClass; +import android.databinding.tool.store.ResourceBundle; +import android.databinding.tool.store.SetterStore; + +import java.util.ArrayList; +import java.util.List; + +public class BindingTarget { + List<Binding> mBindings = new ArrayList<Binding>(); + ExprModel mModel; + ModelClass mResolvedClass; + String mFieldName; + + // 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; + + public BindingTarget(ResourceBundle.BindingTargetBundle bundle) { + mBundle = bundle; + } + + public boolean isUsed() { + return mBundle.isUsed(); + } + + public void addBinding(String name, Expr expr) { + mBindings.add(new Binding(this, name, expr)); + } + + public String getInterfaceType() { + return mBundle.getInterfaceType() == null ? mBundle.getFullClassName() : mBundle.getInterfaceType(); + } + + public String getId() { + return mBundle.getId(); + } + + public String getTag() { + return mBundle.getTag(); + } + + public String getOriginalTag() { + return mBundle.getOriginalTag(); + } + + public String getViewClass() { + return mBundle.getFullClassName(); + } + + public ModelClass getResolvedType() { + if (mResolvedClass == null) { + mResolvedClass = ModelAnalyzer.getInstance().findClass(mBundle.getFullClassName(), + mModel.getImports()); + } + return mResolvedClass; + } + + public String getIncludedLayout() { + return mBundle.getIncludedLayout(); + } + + public boolean isBinder() { + return getIncludedLayout() != null; + } + + public boolean supportsTag() { + return !SetterStore.get(ModelAnalyzer.getInstance()) + .isUntaggable(mBundle.getFullClassName()); + } + + public List<Binding> getBindings() { + return mBindings; + } + + public ExprModel getModel() { + return mModel; + } + + public void setModel(ExprModel model) { + mModel = model; + } + + public void setFieldName(String fieldName) { + mFieldName = fieldName; + } + + public String getFieldName() { + return mFieldName; + } +} diff --git a/tools/data-binding/compiler/src/main/java/android/databinding/tool/CompilerChef.java b/tools/data-binding/compiler/src/main/java/android/databinding/tool/CompilerChef.java new file mode 100644 index 0000000..90829c5 --- /dev/null +++ b/tools/data-binding/compiler/src/main/java/android/databinding/tool/CompilerChef.java @@ -0,0 +1,95 @@ +/* + * 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 android.databinding.tool; + +import android.databinding.tool.store.ResourceBundle; +import android.databinding.tool.util.L; +import android.databinding.tool.writer.DataBinderWriter; +import android.databinding.tool.writer.JavaFileWriter; + +/** + * Chef class for compiler. + * + * Different build systems can initiate a version of this to handle their work + */ +public class CompilerChef { + private JavaFileWriter mFileWriter; + private ResourceBundle mResourceBundle; + private DataBinder mDataBinder; + + private CompilerChef() { + } + + public static CompilerChef createChef(ResourceBundle bundle, JavaFileWriter fileWriter) { + CompilerChef chef = new CompilerChef(); + + chef.mResourceBundle = bundle; + chef.mFileWriter = fileWriter; + chef.mResourceBundle.validateMultiResLayouts(); + return chef; + } + + public ResourceBundle getResourceBundle() { + return mResourceBundle; + } + + public void ensureDataBinder() { + if (mDataBinder == null) { + mDataBinder = new DataBinder(mResourceBundle); + mDataBinder.setFileWriter(mFileWriter); + } + } + + public boolean hasAnythingToGenerate() { + L.d("checking if we have anything to generate. bundle size: %s", + mResourceBundle == null ? -1 : mResourceBundle.getLayoutBundles().size()); + return mResourceBundle != null && mResourceBundle.getLayoutBundles().size() > 0; + } + + public void writeDbrFile(int minSdk) { + ensureDataBinder(); + final String pkg = "android.databinding"; + DataBinderWriter dbr = new DataBinderWriter(pkg, mResourceBundle.getAppPackage(), + "DataBinderMapper", mDataBinder.getLayoutBinders(), minSdk); + if (dbr.getLayoutBinders().size() > 0) { + mFileWriter.writeToFile(pkg + "." + dbr.getClassName(), dbr.write()); + } + } + + /** + * Adds variables to list of Bindables. + */ + public void addBRVariables(BindableHolder bindables) { + ensureDataBinder(); + for (LayoutBinder layoutBinder : mDataBinder.mLayoutBinders) { + for (String variableName : layoutBinder.getUserDefinedVariables().keySet()) { + bindables.addVariable(variableName, layoutBinder.getClassName()); + } + } + } + + public void writeViewBinderInterfaces(boolean isLibrary) { + ensureDataBinder(); + mDataBinder.writerBaseClasses(isLibrary); + } + + public void writeViewBinders() { + ensureDataBinder(); + mDataBinder.writeBinders(); + } + + public interface BindableHolder { + void addVariable(String variableName, String containingClassName); + } +} diff --git a/tools/data-binding/compiler/src/main/java/android/databinding/tool/DataBinder.java b/tools/data-binding/compiler/src/main/java/android/databinding/tool/DataBinder.java new file mode 100644 index 0000000..70f8bcb --- /dev/null +++ b/tools/data-binding/compiler/src/main/java/android/databinding/tool/DataBinder.java @@ -0,0 +1,81 @@ +/* + * 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 android.databinding.tool; + +import android.databinding.tool.store.ResourceBundle; +import android.databinding.tool.util.L; +import android.databinding.tool.writer.JavaFileWriter; + +import java.util.ArrayList; +import java.util.HashSet; +import java.util.List; +import java.util.Map; +import java.util.Set; + +/** + * The main class that handles parsing files and generating classes. + */ +public class DataBinder { + List<LayoutBinder> mLayoutBinders = new ArrayList<LayoutBinder>(); + + private JavaFileWriter mFileWriter; + + public DataBinder(ResourceBundle resourceBundle) { + L.d("reading resource bundle into data binder"); + for (Map.Entry<String, List<ResourceBundle.LayoutFileBundle>> entry : + resourceBundle.getLayoutBundles().entrySet()) { + for (ResourceBundle.LayoutFileBundle bundle : entry.getValue()) { + mLayoutBinders.add(new LayoutBinder(resourceBundle, bundle)); + } + } + } + public List<LayoutBinder> getLayoutBinders() { + return mLayoutBinders; + } + + public void writerBaseClasses(boolean isLibrary) { + Set<String> writtenFiles = new HashSet<String>(); + for (LayoutBinder layoutBinder : mLayoutBinders) { + if (isLibrary || layoutBinder.hasVariations()) { + String className = layoutBinder.getClassName(); + if (writtenFiles.contains(className)) { + continue; + } + mFileWriter.writeToFile(layoutBinder.getPackage() + "." + className, + layoutBinder.writeViewBinderBaseClass()); + writtenFiles.add(className); + } + } + } + + public void writeBinders() { + for (LayoutBinder layoutBinder : mLayoutBinders) { + String className = layoutBinder.getImplementationName(); + L.d("writing data binder %s", className); + mFileWriter.writeToFile(layoutBinder.getPackage() + "." + className, + layoutBinder.writeViewBinder()); + } + } + + public void setFileWriter(JavaFileWriter fileWriter) { + mFileWriter = fileWriter; + } + + public JavaFileWriter getFileWriter() { + return mFileWriter; + } +} diff --git a/tools/data-binding/compiler/src/main/java/android/databinding/tool/ExpressionParser.java b/tools/data-binding/compiler/src/main/java/android/databinding/tool/ExpressionParser.java new file mode 100644 index 0000000..03687ff --- /dev/null +++ b/tools/data-binding/compiler/src/main/java/android/databinding/tool/ExpressionParser.java @@ -0,0 +1,50 @@ +/* + * 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 android.databinding.tool; + +import org.antlr.v4.runtime.ANTLRInputStream; +import org.antlr.v4.runtime.CommonTokenStream; + +import android.databinding.parser.BindingExpressionLexer; +import android.databinding.parser.BindingExpressionParser; +import android.databinding.tool.expr.Expr; +import android.databinding.tool.expr.ExprModel; +import android.databinding.tool.util.L; + +public class ExpressionParser { + final ExprModel mModel; + final ExpressionVisitor visitor; + + public ExpressionParser(ExprModel model) { + mModel = model; + visitor = new ExpressionVisitor(mModel); + } + + public Expr parse(String input) { + ANTLRInputStream inputStream = new ANTLRInputStream(input); + BindingExpressionLexer lexer = new BindingExpressionLexer(inputStream); + CommonTokenStream tokenStream = new CommonTokenStream(lexer); + BindingExpressionParser parser = new BindingExpressionParser(tokenStream); + BindingExpressionParser.BindingSyntaxContext root = parser.bindingSyntax(); + L.d("exp tree: %s", root.toStringTree(parser)); + return root.accept(visitor); + } + + public ExprModel getModel() { + return mModel; + } +} diff --git a/tools/data-binding/compiler/src/main/java/android/databinding/tool/ExpressionVisitor.java b/tools/data-binding/compiler/src/main/java/android/databinding/tool/ExpressionVisitor.java new file mode 100644 index 0000000..a30f59b --- /dev/null +++ b/tools/data-binding/compiler/src/main/java/android/databinding/tool/ExpressionVisitor.java @@ -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 android.databinding.tool; + +import com.google.common.base.Preconditions; + +import org.antlr.v4.runtime.misc.NotNull; +import org.antlr.v4.runtime.tree.ParseTree; +import org.antlr.v4.runtime.tree.TerminalNode; +import org.apache.commons.lang3.ObjectUtils; + +import android.databinding.parser.BindingExpressionBaseVisitor; +import android.databinding.parser.BindingExpressionParser; +import android.databinding.tool.expr.Expr; +import android.databinding.tool.expr.ExprModel; +import android.databinding.tool.expr.StaticIdentifierExpr; +import android.databinding.tool.reflection.ModelAnalyzer; +import android.databinding.tool.reflection.ModelClass; + +import java.util.ArrayList; +import java.util.List; + +public class ExpressionVisitor extends BindingExpressionBaseVisitor<Expr> { + private final ExprModel mModel; + public ExpressionVisitor(ExprModel model) { + mModel = model; + } + + @Override + public Expr visitStringLiteral(@NotNull BindingExpressionParser.StringLiteralContext ctx) { + final String javaString; + if (ctx.SingleQuoteString() != null) { + String str = ctx.SingleQuoteString().getText(); + String contents = str.substring(1, str.length() - 1); + contents = contents.replace("\"", "\\\"").replace("\\`", "`"); + javaString = '"' + contents + '"'; + } else { + javaString = ctx.DoubleQuoteString().getText(); + } + + return mModel.symbol(javaString, String.class); + } + + @Override + public Expr visitGrouping(@NotNull BindingExpressionParser.GroupingContext ctx) { + Preconditions.checkArgument(ctx.children.size() == 3, "Grouping expression should have" + + " 3 children. # of children: %d", ctx.children.size()); + return mModel.group(ctx.children.get(1).accept(this)); + } + + @Override + public Expr visitBindingSyntax(@NotNull BindingExpressionParser.BindingSyntaxContext ctx) { + try { + // TODO handle defaults + return mModel.bindingExpr(ctx.expression().accept(this)); + } catch (Exception e) { + System.out.println("Error while parsing! " + ctx.getText()); + e.printStackTrace(); + throw new RuntimeException(e); + } + } + + @Override + public Expr visitDotOp(@NotNull BindingExpressionParser.DotOpContext ctx) { + ModelAnalyzer analyzer = ModelAnalyzer.getInstance(); + ModelClass modelClass = analyzer.findClass(ctx.getText(), mModel.getImports()); + if (modelClass == null) { + return mModel.field(ctx.expression().accept(this), + ctx.Identifier().getSymbol().getText()); + } else { + String name = modelClass.toJavaCode(); + StaticIdentifierExpr expr = mModel.staticIdentifier(name); + expr.setUserDefinedType(name); + return expr; + } + } + + @Override + public Expr visitQuestionQuestionOp(@NotNull BindingExpressionParser.QuestionQuestionOpContext ctx) { + final Expr left = ctx.left.accept(this); + return mModel.ternary(mModel.comparison("==", left, mModel.symbol("null", Object.class)), + ctx.right.accept(this), left); + } + + @Override + public Expr visitTerminal(@NotNull TerminalNode node) { + final int type = node.getSymbol().getType(); + Class classType; + switch (type) { + case BindingExpressionParser.IntegerLiteral: + classType = int.class; + break; + case BindingExpressionParser.FloatingPointLiteral: + classType = float.class; + break; + case BindingExpressionParser.BooleanLiteral: + classType = boolean.class; + break; + case BindingExpressionParser.CharacterLiteral: + classType = char.class; + break; + case BindingExpressionParser.SingleQuoteString: + case BindingExpressionParser.DoubleQuoteString: + classType = String.class; + break; + case BindingExpressionParser.NullLiteral: + classType = Object.class; + break; + default: + throw new RuntimeException("cannot create expression from terminal node " + node.toString()); + } + return mModel.symbol(node.getText(), classType); + } + + @Override + public Expr visitComparisonOp(@NotNull BindingExpressionParser.ComparisonOpContext ctx) { + return mModel.comparison(ctx.op.getText(), ctx.left.accept(this), ctx.right.accept(this)); + } + + @Override + public Expr visitIdentifier(@NotNull BindingExpressionParser.IdentifierContext ctx) { + return mModel.identifier(ctx.getText()); + } + + @Override + public Expr visitTernaryOp(@NotNull BindingExpressionParser.TernaryOpContext ctx) { + return mModel.ternary(ctx.left.accept(this), ctx.iftrue.accept(this), + ctx.iffalse.accept(this)); + } + + @Override + public Expr visitMethodInvocation( + @NotNull BindingExpressionParser.MethodInvocationContext ctx) { + List<Expr> args = new ArrayList<Expr>(); + if (ctx.args != null) { + for (ParseTree item : ctx.args.children) { + if (ObjectUtils.equals(item.getText(), ",")) { + continue; + } + args.add(item.accept(this)); + } + } + return mModel.methodCall(ctx.target.accept(this), + ctx.Identifier().getText(), args); + } + + @Override + public Expr visitMathOp(@NotNull BindingExpressionParser.MathOpContext ctx) { + return mModel.math(ctx.left.accept(this), ctx.op.getText(), ctx.right.accept(this)); + } + + @Override + public Expr visitResources(@NotNull BindingExpressionParser.ResourcesContext ctx) { + final List<Expr> args = new ArrayList<Expr>(); + if (ctx.resourceParameters() != null) { + for (ParseTree item : ctx.resourceParameters().expressionList().children) { + if (ObjectUtils.equals(item.getText(), ",")) { + continue; + } + args.add(item.accept(this)); + } + } + final String resourceReference = ctx.ResourceReference().getText(); + final int colonIndex = resourceReference.indexOf(':'); + final int slashIndex = resourceReference.indexOf('/'); + final String packageName = colonIndex < 0 ? null : + resourceReference.substring(1, colonIndex).trim(); + final int startIndex = Math.max(1, colonIndex + 1); + final String resourceType = resourceReference.substring(startIndex, slashIndex).trim(); + final String resourceName = resourceReference.substring(slashIndex + 1).trim(); + return mModel.resourceExpr(packageName, resourceType, resourceName, args); + } + + @Override + public Expr visitBracketOp(@NotNull BindingExpressionParser.BracketOpContext ctx) { + return mModel.bracketExpr(visit(ctx.expression(0)), visit(ctx.expression(1))); + } + + @Override + public Expr visitCastOp(@NotNull BindingExpressionParser.CastOpContext ctx) { + return mModel.castExpr(ctx.type().getText(), visit(ctx.expression())); + } + + // @Override +// public Expr visitIdentifier(@NotNull BindingExpressionParser.IdentifierContext ctx) { +// final String identifier = ctx.Identifier().getText(); +// final VariableRef variableRef = mModel.getOrCreateVariable(identifier, null); +// mAccessedVariables.add(variableRef); +// +// return new FieldExpr(variableRef, new ArrayList<VariableRef>(0)); +// } +// +// @Override +// public Expr visit(@NotNull ParseTree tree) { +// if (tree == null) { +// return null; +// } +// return super.visit(tree); +// } +// +// @Override +// public Expr visitTernaryOp(@NotNull BindingExpressionParser.TernaryOpContext ctx) { +// return new TernaryExpr(ctx.left.accept(this), ctx.iftrue.accept(this), ctx.iffalse.accept(this)); +// } +// +// @Override +// public Expr visitTerminal(@NotNull TerminalNode node) { +// +// final int type = node.getSymbol().getType(); +// switch (type) { +// case IntegerLiteral: +// return new SymbolExpr(node.getText(), Integer.class); +// case FloatingPointLiteral: +// return new SymbolExpr(node.getText(), Float.class); +// case BooleanLiteral: +// return new SymbolExpr(node.getText(), Boolean.class); +// case CharacterLiteral: +// return new SymbolExpr(node.getText(), Character.class); +// case SingleQuoteString: +// return new SymbolExpr(node.getText(), String.class); +// case DoubleQuoteString: +// return new SymbolExpr(node.getText(), String.class); +// case NullLiteral: +// return new SymbolExpr(node.getText(), Object.class); +// default: +// throw new RuntimeException("cannot create expression from terminal node " + node.toString()); +// } +// } +// +// @Override +// public Expr visitMathOp(@NotNull BindingExpressionParser.MathOpContext ctx) { +// // TODO must support upper cast +// return new OpExpr(ctx.left.accept(this), ctx.op.getText(), ctx.right.accept(this)); +// } +// +// @Override +// public Expr visitBitShiftOp(@NotNull BindingExpressionParser.BitShiftOpContext ctx) { +// return new BinaryOpExpr(ctx.left.accept(this), ctx.op.getText(), ctx.right.accept(this)); +// } +// +// @Override +// public Expr visitComparisonOp(@NotNull BindingExpressionParser.ComparisonOpContext ctx) { +// return new ComparisonOpExpr(ctx.left.accept(this), ctx.op.getText(), ctx.right.accept(this)); +// } +// +// @Override +// public Expr visitBinaryOp(@NotNull BindingExpressionParser.BinaryOpContext ctx) { +// return new BinaryOpExpr(ctx.left.accept(this), ctx.op.getText(), ctx.right.accept(this)); +// } +// +// @Override +// public Expr visitAndOrOp(@NotNull BindingExpressionParser.AndOrOpContext ctx) { +// return new AndOrOpExpr(ctx.left.accept(this), ctx.op.getText(), ctx.right.accept(this)); +// } +// +// @Override +// protected Expr aggregateResult(final Expr aggregate, final Expr nextResult) { +// if (aggregate == null) { +// return nextResult; +// } else { +// return new Expr() { +// @org.jetbrains.annotations.NotNull +// @Override +// public Class<? extends Object> resolveValueType( +// @org.jetbrains.annotations.NotNull ModelAnalyzer modelAnalyzer) { +// return modelAnalyzer.commonParentOf(aggregate.getResolvedClass(), nextResult.getResolvedClass()); +// } +// +// @org.jetbrains.annotations.NotNull +// @Override +// public String toReadableString() { +// return aggregate.toReadableString() + ' ' + nextResult.toReadableString(); +// } +// +// @org.jetbrains.annotations.NotNull +// @Override +// public String toJava() { +// return aggregate.toJava() + ' ' + nextResult.toJava(); +// } +// }; +// } +// } +// +// @Override +// public Expr visitDefaults(@NotNull BindingExpressionParser.DefaultsContext ctx) { +// return visit(ctx.constantValue()); +// } +// +// @Override +// public Expr visitMethodInvocation( +// @NotNull BindingExpressionParser.MethodInvocationContext ctx) { +// final Expr expression = visit(ctx.expression()); +// final String methodName = ctx.Identifier().getText(); +// final ArrayList<Expr> parameters = new ArrayList<>(); +// if (ctx.expressionList() != null) { +// for (BindingExpressionParser.ExpressionContext parameter : ctx.expressionList() +// .expression()) { +// parameters.add(visit(parameter)); +// } +// } +// return new MethodCallExpr(expression, methodName, parameters); +// } +} diff --git a/tools/data-binding/compiler/src/main/java/android/databinding/tool/LayoutBinder.java b/tools/data-binding/compiler/src/main/java/android/databinding/tool/LayoutBinder.java new file mode 100644 index 0000000..e0b18d9 --- /dev/null +++ b/tools/data-binding/compiler/src/main/java/android/databinding/tool/LayoutBinder.java @@ -0,0 +1,212 @@ +/* + * 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 android.databinding.tool; + +import com.google.common.base.Preconditions; + +import android.databinding.tool.expr.Dependency; +import android.databinding.tool.expr.Expr; +import android.databinding.tool.expr.ExprModel; +import android.databinding.tool.expr.IdentifierExpr; +import android.databinding.tool.store.ResourceBundle; +import android.databinding.tool.store.ResourceBundle.BindingTargetBundle; +import android.databinding.tool.util.ParserHelper; +import android.databinding.tool.writer.LayoutBinderWriter; +import android.databinding.tool.writer.WriterPackage; + +import java.util.ArrayList; +import java.util.Collections; +import java.util.Comparator; +import java.util.HashMap; +import java.util.List; +import java.util.Map; + +/** + * Keeps all information about the bindings per layout file + */ +public class LayoutBinder { + private static final Comparator<BindingTarget> COMPARE_FIELD_NAME = new Comparator<BindingTarget>() { + @Override + public int compare(BindingTarget first, BindingTarget second) { + final String fieldName1 = WriterPackage.getFieldName(first); + final String fieldName2 = WriterPackage.getFieldName(second); + return fieldName1.compareTo(fieldName2); + } + }; + + /* + * val pkg: String, val projectPackage: String, val baseClassName: String, + val layoutName:String, val lb: LayoutExprBinding*/ + private final ExprModel mExprModel; + private final ExpressionParser mExpressionParser; + private final List<BindingTarget> mBindingTargets; + private String mPackage; + private String mModulePackage; + private String mProjectPackage; + private String mBaseClassName; + private final HashMap<String, String> mUserDefinedVariables = new HashMap<String, String>(); + + private LayoutBinderWriter mWriter; + private ResourceBundle.LayoutFileBundle mBundle; + + public LayoutBinder(ResourceBundle resourceBundle, + ResourceBundle.LayoutFileBundle layoutBundle) { + mExprModel = new ExprModel(); + mExpressionParser = new ExpressionParser(mExprModel); + mBindingTargets = new ArrayList<BindingTarget>(); + mBundle = layoutBundle; + mProjectPackage = resourceBundle.getAppPackage(); + mModulePackage = layoutBundle.getModulePackage(); + mPackage = layoutBundle.getModulePackage() + ".databinding"; + mBaseClassName = ParserHelper.INSTANCE$.toClassName(layoutBundle.getFileName()) + "Binding"; + // copy over data. + for (Map.Entry<String, String> variable : mBundle.getVariables().entrySet()) { + addVariable(variable.getKey(), variable.getValue()); + } + + for (Map.Entry<String, String> userImport : mBundle.getImports().entrySet()) { + mExprModel.addImport(userImport.getKey(), userImport.getValue()); + } + for (BindingTargetBundle targetBundle : mBundle.getBindingTargetBundles()) { + final BindingTarget bindingTarget = createBindingTarget(targetBundle); + for (ResourceBundle.BindingTargetBundle.BindingBundle bindingBundle : targetBundle + .getBindingBundleList()) { + bindingTarget.addBinding(bindingBundle.getName(), parse(bindingBundle.getExpr())); + } + } + Collections.sort(mBindingTargets, COMPARE_FIELD_NAME); + } + + public void resolveWhichExpressionsAreUsed() { + List<Expr> used = new ArrayList<Expr>(); + for (BindingTarget target : mBindingTargets) { + for (Binding binding : target.getBindings()) { + binding.getExpr().setIsUsed(true); + used.add(binding.getExpr()); + } + } + while (!used.isEmpty()) { + Expr e = used.remove(used.size() - 1); + for (Dependency dep : e.getDependencies()) { + if (!dep.getOther().isUsed()) { + used.add(dep.getOther()); + dep.getOther().setIsUsed(true); + } + } + } + } + + public IdentifierExpr addVariable(String name, String type) { + Preconditions.checkState(!mUserDefinedVariables.containsKey(name), + "%s has already been defined as %s", name, type); + final IdentifierExpr id = mExprModel.identifier(name); + id.setUserDefinedType(type); + id.enableDirectInvalidation(); + mUserDefinedVariables.put(name, type); + return id; + } + + public HashMap<String, String> getUserDefinedVariables() { + return mUserDefinedVariables; + } + + public BindingTarget createBindingTarget(ResourceBundle.BindingTargetBundle targetBundle) { + final BindingTarget target = new BindingTarget(targetBundle); + mBindingTargets.add(target); + target.setModel(mExprModel); + return target; + } + + public Expr parse(String input) { + final Expr parsed = mExpressionParser.parse(input); + parsed.setBindingExpression(true); + return parsed; + } + + public List<BindingTarget> getBindingTargets() { + return mBindingTargets; + } + + public boolean isEmpty() { + return mExprModel.size() == 0; + } + + public ExprModel getModel() { + return mExprModel; + } + + private void ensureWriter() { + if (mWriter == null) { + mWriter = new LayoutBinderWriter(this); + } + } + + public String writeViewBinderBaseClass() { + ensureWriter(); + return mWriter.writeBaseClass(); + } + + + public String writeViewBinder() { + mExprModel.seal(); + ensureWriter(); + Preconditions.checkNotNull(mPackage, "package cannot be null"); + Preconditions.checkNotNull(mProjectPackage, "project package cannot be null"); + Preconditions.checkNotNull(mBaseClassName, "base class name cannot be null"); + return mWriter.write(); + } + + public String getPackage() { + return mPackage; + } + + public String getModulePackage() { + return mModulePackage; + } + + public void setPackage(String aPackage) { + mPackage = aPackage; + } + + public String getProjectPackage() { + return mProjectPackage; + } + + public String getLayoutname() { + return mBundle.getFileName(); + } + + public String getImplementationName() { + if (hasVariations()) { + return mBaseClassName + mBundle.getConfigName() + "Impl"; + } else { + return mBaseClassName; + } + } + + public String getClassName() { + return mBaseClassName; + } + + public String getTag() { + return mBundle.getDirectory() + "/" + mBundle.getFileName(); + } + + public boolean hasVariations() { + return mBundle.hasVariations(); + } +} diff --git a/tools/data-binding/compiler/src/main/java/android/databinding/tool/LayoutXmlProcessor.java b/tools/data-binding/compiler/src/main/java/android/databinding/tool/LayoutXmlProcessor.java new file mode 100644 index 0000000..8cc01d2 --- /dev/null +++ b/tools/data-binding/compiler/src/main/java/android/databinding/tool/LayoutXmlProcessor.java @@ -0,0 +1,200 @@ +/* + * 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 android.databinding.tool; + +import com.google.common.base.Predicate; +import com.google.common.collect.Iterables; + +import org.apache.commons.lang3.StringEscapeUtils; +import org.xml.sax.SAXException; + +import android.databinding.BindingBuildInfo; +import android.databinding.tool.store.LayoutFileParser; +import android.databinding.tool.store.ResourceBundle; +import android.databinding.tool.writer.JavaFileWriter; + +import java.io.File; +import java.io.FilenameFilter; +import java.io.IOException; +import java.io.StringWriter; +import java.util.ArrayList; +import java.util.List; +import java.util.UUID; + +import javax.xml.bind.JAXBContext; +import javax.xml.bind.JAXBException; +import javax.xml.bind.Marshaller; +import javax.xml.parsers.ParserConfigurationException; +import javax.xml.xpath.XPathExpressionException; + +/** + * Processes the layout XML, stripping the binding attributes and elements + * and writes the information into an annotated class file for the annotation + * processor to work with. + */ +public class LayoutXmlProcessor { + // hardcoded in baseAdapters + public static final String RESOURCE_BUNDLE_PACKAGE = "android.databinding.layouts"; + public static final String CLASS_NAME = "DataBindingInfo"; + private final JavaFileWriter mFileWriter; + private final ResourceBundle mResourceBundle; + private final int mMinSdk; + + private boolean mProcessingComplete; + private boolean mWritten; + private final boolean mIsLibrary; + private final String mBuildId = UUID.randomUUID().toString(); + // can be a list of xml files or folders that contain XML files + private final List<File> mResources; + + public LayoutXmlProcessor(String applicationPackage, List<File> resources, + JavaFileWriter fileWriter, int minSdk, boolean isLibrary) { + mFileWriter = fileWriter; + mResourceBundle = new ResourceBundle(applicationPackage); + mResources = resources; + mMinSdk = minSdk; + mIsLibrary = isLibrary; + } + + public static List<File> getLayoutFiles(List<File> resources) { + List<File> result = new ArrayList<File>(); + for (File resource : Iterables.filter(resources, fileExists)) { + if (resource.isDirectory()) { + for (File layoutFolder : resource.listFiles(layoutFolderFilter)) { + for (File xmlFile : layoutFolder.listFiles(xmlFileFilter)) { + result.add(xmlFile); + } + + } + } else if (xmlFileFilter.accept(resource.getParentFile(), resource.getName())) { + result.add(resource); + } + } + return result; + } + + /** + * used by the studio plugin + */ + public ResourceBundle getResourceBundle() { + return mResourceBundle; + } + + public boolean processResources() + throws ParserConfigurationException, SAXException, XPathExpressionException, + IOException { + if (mProcessingComplete) { + return false; + } + LayoutFileParser layoutFileParser = new LayoutFileParser(); + for (File xmlFile : getLayoutFiles(mResources)) { + final ResourceBundle.LayoutFileBundle bindingLayout = layoutFileParser + .parseXml(xmlFile, mResourceBundle.getAppPackage()); + if (bindingLayout != null && !bindingLayout.isEmpty()) { + mResourceBundle.addLayoutBundle(bindingLayout); + } + } + mProcessingComplete = true; + return true; + } + + public void writeIntermediateFile(File sdkDir, File xmlOutDir) throws JAXBException { + if (mWritten) { + return; + } + JAXBContext context = JAXBContext.newInstance(ResourceBundle.LayoutFileBundle.class); + Marshaller marshaller = context.createMarshaller(); + writeInfoClass(sdkDir, xmlOutDir); + for (List<ResourceBundle.LayoutFileBundle> layouts : mResourceBundle.getLayoutBundles() + .values()) { + for (ResourceBundle.LayoutFileBundle layout : layouts) { + writeXmlFile(xmlOutDir, layout, marshaller); + } + } + mWritten = true; + } + + private void writeXmlFile(File xmlOutDir, ResourceBundle.LayoutFileBundle layout, + Marshaller marshaller) throws JAXBException { + String filename = generateExportFileName(layout) + ".xml"; + String xml = toXML(layout, marshaller); + mFileWriter.writeToFile(new File(xmlOutDir, filename), xml); + } + + public String getInfoClassFullName() { + return RESOURCE_BUNDLE_PACKAGE + "." + CLASS_NAME; + } + + private String toXML(ResourceBundle.LayoutFileBundle layout, Marshaller marshaller) + throws JAXBException { + StringWriter writer = new StringWriter(); + marshaller.marshal(layout, writer); + return writer.getBuffer().toString(); + } + + /** + * Generates a string identifier that can uniquely identify the given layout bundle. + * This identifier can be used when we need to export data about this layout bundle. + */ + private String generateExportFileName(ResourceBundle.LayoutFileBundle layout) { + StringBuilder name = new StringBuilder(layout.getFileName()); + name.append('-').append(layout.getDirectory()); + for (int i = name.length() - 1; i >= 0; i--) { + char c = name.charAt(i); + if (c == '-') { + name.deleteCharAt(i); + c = Character.toUpperCase(name.charAt(i)); + name.setCharAt(i, c); + } + } + return name.toString(); + } + + private void writeInfoClass(File sdkDir, File xmlOutDir) { + final String sdkPath = StringEscapeUtils.escapeJava(sdkDir.getAbsolutePath()); + final Class annotation = BindingBuildInfo.class; + final String layoutInfoPath = StringEscapeUtils.escapeJava(xmlOutDir.getAbsolutePath()); + String classString = "package " + RESOURCE_BUNDLE_PACKAGE + ";\n\n" + + "import " + annotation.getCanonicalName() + ";\n\n" + + "@" + annotation.getSimpleName() + "(buildId=\"" + mBuildId + "\", " + + "modulePackage=\"" + mResourceBundle.getAppPackage() + "\", " + + "sdkRoot=\"" + sdkPath + "\", " + + "layoutInfoDir=\"" + layoutInfoPath + "\"," + + "isLibrary=" + mIsLibrary + "," + + "minSdk=" + mMinSdk + ")\n" + + "public class " + CLASS_NAME + " {}\n"; + mFileWriter.writeToFile(RESOURCE_BUNDLE_PACKAGE + "." + CLASS_NAME, classString); + } + + private static final Predicate<File> fileExists = new Predicate<File>() { + @Override + public boolean apply(File input) { + return input.exists() && input.canRead(); + } + }; + + private static final FilenameFilter layoutFolderFilter = new FilenameFilter() { + @Override + public boolean accept(File dir, String name) { + return name.startsWith("layout"); + } + }; + + private static final FilenameFilter xmlFileFilter = new FilenameFilter() { + @Override + public boolean accept(File dir, String name) { + return name.toLowerCase().endsWith(".xml"); + } + }; +} diff --git a/tools/data-binding/compiler/src/main/java/android/databinding/tool/MakeCopy.java b/tools/data-binding/compiler/src/main/java/android/databinding/tool/MakeCopy.java new file mode 100644 index 0000000..ac585d1 --- /dev/null +++ b/tools/data-binding/compiler/src/main/java/android/databinding/tool/MakeCopy.java @@ -0,0 +1,245 @@ +/* + * 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 android.databinding.tool; + +import org.apache.commons.io.FileUtils; +import org.apache.commons.io.IOUtils; +import org.w3c.dom.Document; + +import android.databinding.tool.writer.JavaFileWriter; + +import java.io.File; +import java.io.FileWriter; +import java.io.FilenameFilter; +import java.io.IOException; +import java.util.ArrayList; + +import javax.xml.parsers.DocumentBuilder; +import javax.xml.parsers.DocumentBuilderFactory; +import javax.xml.xpath.XPath; +import javax.xml.xpath.XPathConstants; +import javax.xml.xpath.XPathExpressionException; +import javax.xml.xpath.XPathFactory; + +/** + * This class is used by make to copy resources to an intermediate directory and start processing + * them. When aapt takes over, this can be easily extracted to a short script. + */ +public class MakeCopy { + private static final int MANIFEST_INDEX = 0; + private static final int ADK_INDEX = 1; + private static final int SRC_INDEX = 2; + private static final int XML_INDEX = 3; + private static final int RES_OUT_INDEX = 4; + private static final int RES_IN_INDEX = 5; + + private static final String APP_SUBPATH = LayoutXmlProcessor.RESOURCE_BUNDLE_PACKAGE + .replace('.', File.separatorChar); + private static final FilenameFilter LAYOUT_DIR_FILTER = new FilenameFilter() { + @Override + public boolean accept(File dir, String name) { + return name.toLowerCase().startsWith("layout"); + } + }; + + private static final FilenameFilter XML_FILENAME_FILTER = new FilenameFilter() { + @Override + public boolean accept(File dir, String name) { + return name.toLowerCase().endsWith(".xml"); + } + }; + + public static void main(String[] args) { + if (args.length < 6) { + System.out.println("required parameters: manifest adk-dir src-out-dir xml-out-dir " + + "res-out-dir res-in-dir..."); + System.out.println("Creates an android data binding class and copies resources from"); + System.out.println("res-source to res-target and modifies binding layout files"); + System.out.println("in res-target. Binding data is extracted into XML files"); + System.out.println("and placed in xml-out-dir."); + System.out.println(" manifest path to AndroidManifest.xml file"); + System.out.println(" adk-dir path to Android SDK home"); + System.out.println(" src-out-dir path to where generated source goes"); + System.out.println(" xml-out-dir path to where generated binding XML goes"); + System.out.println(" res-out-dir path to the where modified resources should go"); + System.out.println(" res-in-dir path to source resources \"res\" directory. One" + + " or more are allowed."); + System.exit(1); + } + final boolean isLibrary; + final String applicationPackage; + final int minSdk; + final Document androidManifest = readAndroidManifest(new File(args[MANIFEST_INDEX])); + try { + final XPathFactory xPathFactory = XPathFactory.newInstance(); + final XPath xPath = xPathFactory.newXPath(); + isLibrary = (Boolean) xPath.evaluate("boolean(/manifest/application)", androidManifest, + XPathConstants.BOOLEAN); + applicationPackage = xPath.evaluate("string(/manifest/@package)", androidManifest); + final Double minSdkNumber = (Double) xPath.evaluate( + "number(/manifest/uses-sdk/@android:minSdkVersion)", androidManifest, + XPathConstants.NUMBER); + minSdk = minSdkNumber == null ? 1 : minSdkNumber.intValue(); + } catch (XPathExpressionException e) { + e.printStackTrace(); + System.exit(6); + return; + } + final File srcDir = new File(args[SRC_INDEX], APP_SUBPATH); + if (!makeTargetDir(srcDir)) { + System.err.println("Could not create source directory " + srcDir); + System.exit(2); + } + final File resTarget = new File(args[RES_OUT_INDEX]); + if (!makeTargetDir(resTarget)) { + System.err.println("Could not create resource directory: " + resTarget); + System.exit(4); + } + final File xmlDir = new File(args[XML_INDEX]); + if (!makeTargetDir(xmlDir)) { + System.err.println("Could not create xml output directory: " + xmlDir); + System.exit(5); + } + final File adkDir = new File(args[ADK_INDEX]); + if (!adkDir.exists()) { + System.err.println("Could not find android SDK directory: " + adkDir); + System.exit(6); + } + System.out.println("Application Package: " + applicationPackage); + System.out.println("Minimum SDK: " + minSdk); + System.out.println("Target Resources: " + resTarget.getAbsolutePath()); + System.out.println("Target Source Dir: " + srcDir.getAbsolutePath()); + System.out.println("Target XML Dir: " + xmlDir.getAbsolutePath()); + + boolean foundSomeResources = false; + for (int i = RES_IN_INDEX; i < args.length; i++) { + final File resDir = new File(args[i]); + if (!resDir.exists()) { + System.err.println("Could not find resource directory: " + resDir); + } else { + System.out.println("Source Resources: " + resDir.getAbsolutePath()); + try { + FileUtils.copyDirectory(resDir, resTarget); + addFromFile(resDir, resTarget); + foundSomeResources = true; + } catch (IOException e) { + System.err.println("Could not copy resources from " + resDir + " to " + resTarget + + ": " + e.getLocalizedMessage()); + System.exit(3); + } + } + } + + if (!foundSomeResources) { + System.err.println("No resource directories were found."); + System.exit(7); + } + processLayoutFiles(applicationPackage, resTarget, srcDir, xmlDir, adkDir, minSdk, + isLibrary); + } + + private static Document readAndroidManifest(File manifest) { + try { + DocumentBuilderFactory dbf = DocumentBuilderFactory.newInstance(); + DocumentBuilder documentBuilder = dbf.newDocumentBuilder(); + return documentBuilder.parse(manifest); + } catch (Exception e) { + System.err.println("Could not load Android Manifest from " + + manifest.getAbsolutePath() + ": " + e.getLocalizedMessage()); + System.exit(8); + return null; + } + } + + private static void processLayoutFiles(String applicationPackage, File resTarget, File srcDir, + File xmlDir, File adkDir, int minSdk, boolean isLibrary) { + ArrayList<File> resourceFolders = new ArrayList<File>(); + resourceFolders.add(resTarget); + MakeFileWriter makeFileWriter = new MakeFileWriter(srcDir); + LayoutXmlProcessor xmlProcessor = new LayoutXmlProcessor(applicationPackage, + resourceFolders, makeFileWriter, minSdk, isLibrary); + try { + xmlProcessor.processResources(); + xmlProcessor.writeIntermediateFile(adkDir, xmlDir); + if (makeFileWriter.getErrorCount() > 0) { + System.exit(9); + } + } catch (Exception e) { + System.err.println("Error processing layout files: " + e.getLocalizedMessage()); + System.exit(10); + } + } + + private static void addFromFile(File resDir, File resTarget) { + for (File layoutDir : resDir.listFiles(LAYOUT_DIR_FILTER)) { + if (layoutDir.isDirectory()) { + File targetDir = new File(resTarget, layoutDir.getName()); + for (File layoutFile : layoutDir.listFiles(XML_FILENAME_FILTER)) { + File targetFile = new File(targetDir, layoutFile.getName()); + FileWriter appender = null; + try { + appender = new FileWriter(targetFile, true); + appender.write("<!-- From: " + layoutFile.toURI().toString() + " -->\n"); + } catch (IOException e) { + System.err.println("Could not update " + layoutFile + ": " + + e.getLocalizedMessage()); + } finally { + IOUtils.closeQuietly(appender); + } + } + } + } + } + + private static boolean makeTargetDir(File dir) { + if (dir.exists()) { + return dir.isDirectory(); + } + + return dir.mkdirs(); + } + + private static class MakeFileWriter extends JavaFileWriter { + private final File mSourceRoot; + private int mErrorCount; + + public MakeFileWriter(File sourceRoot) { + mSourceRoot = sourceRoot; + } + + @Override + public void writeToFile(String canonicalName, String contents) { + String fileName = canonicalName.replace('.', File.separatorChar) + ".java"; + File sourceFile = new File(mSourceRoot, fileName); + FileWriter writer = null; + try { + sourceFile.getParentFile().mkdirs(); + writer = new FileWriter(sourceFile); + writer.write(contents); + } catch (IOException e) { + System.err.println("Could not write to " + sourceFile + ": " + + e.getLocalizedMessage()); + mErrorCount++; + } finally { + IOUtils.closeQuietly(writer); + } + } + + public int getErrorCount() { + return mErrorCount; + } + } +} diff --git a/tools/data-binding/compiler/src/main/java/android/databinding/tool/expr/BracketExpr.java b/tools/data-binding/compiler/src/main/java/android/databinding/tool/expr/BracketExpr.java new file mode 100644 index 0000000..b5cce7d --- /dev/null +++ b/tools/data-binding/compiler/src/main/java/android/databinding/tool/expr/BracketExpr.java @@ -0,0 +1,79 @@ +/* + * 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 android.databinding.tool.expr; + +import android.databinding.tool.reflection.ModelAnalyzer; +import android.databinding.tool.reflection.ModelClass; + +import java.util.List; + +public class BracketExpr extends Expr { + + public static enum BracketAccessor { + ARRAY, + LIST, + MAP, + } + + private BracketAccessor mAccessor; + + BracketExpr(Expr target, Expr arg) { + super(target, arg); + } + + @Override + protected ModelClass resolveType(ModelAnalyzer modelAnalyzer) { + ModelClass targetType = getTarget().resolveType(modelAnalyzer); + if (targetType.isArray()) { + mAccessor = BracketAccessor.ARRAY; + } else if (targetType.isList()) { + mAccessor = BracketAccessor.LIST; + } else if (targetType.isMap()) { + mAccessor = BracketAccessor.MAP; + } else { + throw new IllegalArgumentException("Cannot determine variable type used in [] " + + "expression. Cast the value to List, Map, " + + "or array. Type detected: " + targetType.toJavaCode()); + } + return targetType.getComponentType(); + } + + @Override + protected List<Dependency> constructDependencies() { + return constructDynamicChildrenDependencies(); + } + + protected String computeUniqueKey() { + return sUniqueKeyJoiner.join(getTarget().computeUniqueKey(), "$", getArg().computeUniqueKey(), "$"); + } + + public Expr getTarget() { + return getChildren().get(0); + } + + public Expr getArg() { + return getChildren().get(1); + } + + public BracketAccessor getAccessor() { + return mAccessor; + } + + public boolean argCastsInteger() { + return Object.class.equals(getArg().getResolvedType()); + } +} diff --git a/tools/data-binding/compiler/src/main/java/android/databinding/tool/expr/CastExpr.java b/tools/data-binding/compiler/src/main/java/android/databinding/tool/expr/CastExpr.java new file mode 100644 index 0000000..b4e41da --- /dev/null +++ b/tools/data-binding/compiler/src/main/java/android/databinding/tool/expr/CastExpr.java @@ -0,0 +1,58 @@ +/* + * Copyright (C) 2015 The Android Open Source Project + * + * Licensed under the Apache License, Version 2.0 (the "License"); + * you may not use this file except in compliance with the License. + * You may obtain a copy of the License at + * + * http://www.apache.org/licenses/LICENSE-2.0 + * + * Unless required by applicable law or agreed to in writing, software + * distributed under the License is distributed on an "AS IS" BASIS, + * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. + * See the License for the specific language governing permissions and + * limitations under the License. + */ + +package android.databinding.tool.expr; + +import android.databinding.tool.reflection.ModelAnalyzer; +import android.databinding.tool.reflection.ModelClass; + +import java.util.List; + +public class CastExpr extends Expr { + + final String mType; + + CastExpr(String type, Expr expr) { + super(expr); + mType = type; + } + + @Override + protected ModelClass resolveType(ModelAnalyzer modelAnalyzer) { + return modelAnalyzer.findClass(mType, getModel().getImports()); + } + + @Override + protected List<Dependency> constructDependencies() { + final List<Dependency> dependencies = constructDynamicChildrenDependencies(); + for (Dependency dependency : dependencies) { + dependency.setMandatory(true); + } + return dependencies; + } + + protected String computeUniqueKey() { + return sUniqueKeyJoiner.join(mType, getCastExpr().computeUniqueKey()); + } + + public Expr getCastExpr() { + return getChildren().get(0); + } + + public String getCastType() { + return getResolvedType().toJavaCode(); + } +} diff --git a/tools/data-binding/compiler/src/main/java/android/databinding/tool/expr/ComparisonExpr.java b/tools/data-binding/compiler/src/main/java/android/databinding/tool/expr/ComparisonExpr.java new file mode 100644 index 0000000..0c237be --- /dev/null +++ b/tools/data-binding/compiler/src/main/java/android/databinding/tool/expr/ComparisonExpr.java @@ -0,0 +1,62 @@ +/* + * 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 android.databinding.tool.expr; + +import android.databinding.tool.reflection.ModelAnalyzer; +import android.databinding.tool.reflection.ModelClass; + +import java.util.List; + +public class ComparisonExpr extends Expr { + final String mOp; + ComparisonExpr(String op, Expr left, Expr right) { + super(left, right); + mOp = op; + } + + @Override + protected String computeUniqueKey() { + return sUniqueKeyJoiner.join(mOp, super.computeUniqueKey()); + } + + @Override + protected ModelClass resolveType(ModelAnalyzer modelAnalyzer) { + return modelAnalyzer.loadPrimitive("boolean"); + } + + @Override + protected List<Dependency> constructDependencies() { + return constructDynamicChildrenDependencies(); + } + + public String getOp() { + return mOp; + } + + public Expr getLeft() { + return getChildren().get(0); + } + + public Expr getRight() { + return getChildren().get(1); + } + + @Override + public boolean isEqualityCheck() { + return "==".equals(mOp.trim()); + } +} diff --git a/tools/data-binding/compiler/src/main/java/android/databinding/tool/expr/Dependency.java b/tools/data-binding/compiler/src/main/java/android/databinding/tool/expr/Dependency.java new file mode 100644 index 0000000..1678af7 --- /dev/null +++ b/tools/data-binding/compiler/src/main/java/android/databinding/tool/expr/Dependency.java @@ -0,0 +1,80 @@ +/* + * 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 android.databinding.tool.expr; + +public class Dependency { + final Expr mDependant; + final Expr mOther; + final Expr mCondition; + final boolean mExpectedOutput;// ! + // set only if this is conditional. Means it has been resolved so that it can be used in + // should get calculations + boolean mElevated; + + // this means that trying to calculate the dependant expression w/o + // will crash the app unless "Other" has a non-null value + boolean mMandatory = false; + + public Dependency(Expr dependant, Expr other) { + mDependant = dependant; + mOther = other; + mCondition = null; + mOther.addDependant(this); + mExpectedOutput = false; + } + + public Dependency(Expr dependant, Expr other, Expr condition, boolean expectedOutput) { + mDependant = dependant; + mOther = other; + mCondition = condition; + mOther.addDependant(this); + mExpectedOutput = expectedOutput; + } + + public void setMandatory(boolean mandatory) { + mMandatory = mandatory; + } + + public boolean isMandatory() { + return mMandatory; + } + + public boolean isConditional() { + return mCondition != null && !mElevated; + } + + public Expr getOther() { + return mOther; + } + + public Expr getDependant() { + return mDependant; + } + + public boolean getExpectedOutput() { + return mExpectedOutput; + } + + public Expr getCondition() { + return mCondition; + } + + public void elevate() { + mElevated = true; + } + + public boolean isElevated() { + return mElevated; + } +} diff --git a/tools/data-binding/compiler/src/main/java/android/databinding/tool/expr/Expr.java b/tools/data-binding/compiler/src/main/java/android/databinding/tool/expr/Expr.java new file mode 100644 index 0000000..09b96d8 --- /dev/null +++ b/tools/data-binding/compiler/src/main/java/android/databinding/tool/expr/Expr.java @@ -0,0 +1,612 @@ +/* + * 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 android.databinding.tool.expr; + +import com.google.common.base.Function; +import com.google.common.base.Joiner; +import com.google.common.base.Preconditions; +import com.google.common.base.Predicate; +import com.google.common.collect.Iterables; +import com.google.common.collect.Lists; + +import android.databinding.tool.reflection.ModelAnalyzer; +import android.databinding.tool.reflection.ModelClass; + +import java.util.ArrayList; +import java.util.BitSet; +import java.util.Collections; +import java.util.List; + +abstract public class Expr { + + public static final int NO_ID = -1; + protected List<Expr> mChildren = new ArrayList<Expr>(); + + // any expression that refers to this. Useful if this expr is duplicate and being replaced + private List<Expr> mParents = new ArrayList<Expr>(); + + private Boolean mIsDynamic; + + private ModelClass mResolvedType; + + private String mUniqueKey; + + private List<Dependency> mDependencies; + + private List<Dependency> mDependants = Lists.newArrayList(); + + private int mId = NO_ID; + + private int mRequirementId = NO_ID; + + // means this expression can directly be invalidated by the user + private boolean mCanBeInvalidated = false; + + /** + * This set denotes the times when this expression is invalid. + * If it is an Identifier expression, it is its index + * If it is a composite expression, it is the union of invalid flags of its descendants + */ + private BitSet mInvalidFlags; + + /** + * Set when this expression is registered to a model + */ + private ExprModel mModel; + + /** + * This set denotes the times when this expression must be read. + * + * It is the union of invalidation flags of all of its non-conditional dependants. + */ + BitSet mShouldReadFlags; + + BitSet mReadSoFar = new BitSet();// i've read this variable for these flags + + /** + * calculated on initialization, assuming all conditionals are true + */ + BitSet mShouldReadWithConditionals; + + private boolean mIsBindingExpression; + + /** + * Used by generators when this expression is resolved. + */ + private boolean mRead; + private boolean mIsUsed = false; + + Expr(Iterable<Expr> children) { + for (Expr expr : children) { + mChildren.add(expr); + } + addParents(); + } + + Expr(Expr... children) { + Collections.addAll(mChildren, children); + addParents(); + } + + public int getId() { + Preconditions.checkState(mId != NO_ID, "if getId is called on an expression, it should have" + + " and id"); + return mId; + } + + public void setId(int id) { + Preconditions.checkState(mId == NO_ID, "ID is already set on " + this); + mId = id; + } + + public ExprModel getModel() { + return mModel; + } + + public BitSet getInvalidFlags() { + if (mInvalidFlags == null) { + mInvalidFlags = resolveInvalidFlags(); + } + return mInvalidFlags; + } + + private BitSet resolveInvalidFlags() { + BitSet bitSet = new BitSet(); + if (mCanBeInvalidated) { + bitSet.set(getId(), true); + } + for (Dependency dependency : getDependencies()) { + // TODO optional optimization: do not invalidate for conditional flags + bitSet.or(dependency.getOther().getInvalidFlags()); + } + return bitSet; + } + + public void setBindingExpression(boolean isBindingExpression) { + mIsBindingExpression = isBindingExpression; + } + + public boolean isBindingExpression() { + return mIsBindingExpression; + } + + public boolean isObservable() { + return getResolvedType().isObservable(); + } + + public BitSet getShouldReadFlags() { + if (mShouldReadFlags == null) { + getShouldReadFlagsWithConditionals(); + mShouldReadFlags = resolveShouldReadFlags(); + } + return mShouldReadFlags; + } + + public BitSet getShouldReadFlagsWithConditionals() { + if (mShouldReadWithConditionals == null) { + mShouldReadWithConditionals = resolveShouldReadWithConditionals(); + } + return mShouldReadWithConditionals; + } + + public void setModel(ExprModel model) { + mModel = model; + } + + private BitSet resolveShouldReadWithConditionals() { + // ensure we have invalid flags + BitSet bitSet = new BitSet(); + // if i'm invalid, that DOES NOT mean i should be read :/. + if (mIsBindingExpression) { + bitSet.or(getInvalidFlags()); + } + + for (Dependency dependency : getDependants()) { + // first traverse non-conditionals because we'll avoid adding conditionals if we are get because of these anyways + if (dependency.getCondition() == null) { + bitSet.or(dependency.getDependant().getShouldReadFlagsWithConditionals()); + } else { + bitSet.set(dependency.getDependant() + .getRequirementFlagIndex(dependency.getExpectedOutput())); + } + } + return bitSet; + } + + private BitSet resolveShouldReadFlags() { + // ensure we have invalid flags + BitSet bitSet = new BitSet(); + if (isRead()) { + return bitSet; + } + if (mIsBindingExpression) { + bitSet.or(getInvalidFlags()); + } + for (Dependency dependency : getDependants()) { + final boolean isElevated = unreadElevatedCheck.apply(dependency); + if (dependency.isConditional()) { + continue; // TODO + } + if (isElevated) { + // if i already have all flags that will require my dependant's predicate to + // be read, that means i'm already read thus can avoid adding its conditional + // dependency + if (!dependency.getDependant().getAllCalculationPaths().areAllPathsSatisfied( + mReadSoFar)) { + bitSet.set(dependency.getDependant() + .getRequirementFlagIndex(dependency.getExpectedOutput())); + } + } else { + bitSet.or(dependency.getDependant().getShouldReadFlags()); + } + } + bitSet.andNot(mReadSoFar); + // should read w/ conditionals does eleminate for unnecessary re-reads + bitSet.and(mShouldReadWithConditionals); + return bitSet; + } + + Predicate<Dependency> unreadElevatedCheck = new Predicate<Dependency>() { + @Override + public boolean apply(Dependency input) { + return input.isElevated() && !input.getDependant().isRead(); + } + }; + + private void addParents() { + for (Expr expr : mChildren) { + expr.mParents.add(this); + } + } + + public void onSwappedWith(Expr existing) { + for (Expr child : mChildren) { + child.onParentSwapped(this, existing); + } + } + + private void onParentSwapped(Expr oldParent, Expr newParent) { + Preconditions.checkState(mParents.remove(oldParent)); + mParents.add(newParent); + } + + public List<Expr> getChildren() { + return mChildren; + } + + public List<Expr> getParents() { + return mParents; + } + + /** + * Whether the result of this expression can change or not. + * + * For example, 3 + 5 can not change vs 3 + x may change. + * + * Default implementations checks children and returns true if any of them returns true + * + * @return True if the result of this expression may change due to variables + */ + public boolean isDynamic() { + if (mIsDynamic == null) { + mIsDynamic = isAnyChildDynamic(); + } + return mIsDynamic; + } + + private boolean isAnyChildDynamic() { + return Iterables.any(mChildren, new Predicate<Expr>() { + @Override + public boolean apply(Expr input) { + return input.isDynamic(); + } + }); + + } + + public ModelClass getResolvedType() { + if (mResolvedType == null) { + // TODO not get instance + mResolvedType = resolveType(ModelAnalyzer.getInstance()); + } + return mResolvedType; + } + + abstract protected ModelClass resolveType(ModelAnalyzer modelAnalyzer); + + abstract protected List<Dependency> constructDependencies(); + + /** + * Creates a dependency for each dynamic child. Should work for any expression besides + * conditionals. + */ + protected List<Dependency> constructDynamicChildrenDependencies() { + List<Dependency> dependencies = new ArrayList<Dependency>(); + for (Expr node : mChildren) { + if (!node.isDynamic()) { + continue; + } + dependencies.add(new Dependency(this, node)); + } + return dependencies; + } + + public final List<Dependency> getDependencies() { + if (mDependencies == null) { + mDependencies = constructDependencies(); + } + return mDependencies; + } + + void addDependant(Dependency dependency) { + mDependants.add(dependency); + } + + public List<Dependency> getDependants() { + return mDependants; + } + + protected static final String KEY_JOIN = "~"; + protected static final Joiner sUniqueKeyJoiner = Joiner.on(KEY_JOIN); + + /** + * Returns a unique string key that can identify this expression. + * + * It must take into account any dependencies + * + * @return A unique identifier for this expression + */ + public final String getUniqueKey() { + if (mUniqueKey == null) { + mUniqueKey = computeUniqueKey(); + Preconditions.checkNotNull(mUniqueKey, + "if there are no children, you must override computeUniqueKey"); + Preconditions.checkState(!mUniqueKey.trim().equals(""), + "if there are no children, you must override computeUniqueKey"); + } + return mUniqueKey; + } + + protected String computeUniqueKey() { + return computeChildrenKey(); + } + + protected final String computeChildrenKey() { + return sUniqueKeyJoiner.join(Iterables.transform(mChildren, new Function<Expr, String>() { + @Override + public String apply(Expr input) { + return input.getUniqueKey(); + } + })); + } + + public void enableDirectInvalidation() { + mCanBeInvalidated = true; + } + + public boolean canBeInvalidated() { + return mCanBeInvalidated; + } + + public void trimShouldReadFlags(BitSet bitSet) { + mShouldReadFlags.andNot(bitSet); + } + + public boolean isConditional() { + return false; + } + + public int getRequirementId() { + return mRequirementId; + } + + public void setRequirementId(int requirementId) { + mRequirementId = requirementId; + } + + /** + * This is called w/ a dependency of mine. + * Base method should thr + */ + public int getRequirementFlagIndex(boolean expectedOutput) { + Preconditions.checkState(mRequirementId != NO_ID, "If this is an expression w/ conditional" + + " dependencies, it must be assigned a requirement ID"); + return expectedOutput ? mRequirementId + 1 : mRequirementId; + } + + public boolean hasId() { + return mId != NO_ID; + } + + public void markFlagsAsRead(BitSet flags) { + mReadSoFar.or(flags); + } + + public boolean isRead() { + return mRead; + } + + public boolean considerElevatingConditionals(Expr justRead) { + boolean elevated = false; + for (Dependency dependency : mDependencies) { + if (dependency.isConditional() && dependency.getCondition() == justRead) { + dependency.elevate(); + elevated = true; + } + } + return elevated; + } + + public void invalidateReadFlags() { + mShouldReadFlags = null; + } + + public boolean hasNestedCannotRead() { + if (isRead()) { + return false; + } + if (getShouldReadFlags().isEmpty()) { + return true; + } + return Iterables.any(getDependencies(), hasNestedCannotRead); + } + + Predicate<Dependency> hasNestedCannotRead = new Predicate<Dependency>() { + @Override + public boolean apply(Dependency input) { + return input.isConditional() || input.getOther().hasNestedCannotRead(); + } + }; + + public boolean markAsReadIfDone() { + if (mRead) { + return false; + } + // TODO avoid clone, we can calculate this iteratively + BitSet clone = (BitSet) mShouldReadWithConditionals.clone(); + + clone.andNot(mReadSoFar); + mRead = clone.isEmpty(); + if (!mRead && !mReadSoFar.isEmpty()) { + // check if remaining dependencies can be satisfied w/ existing values + // for predicate flags, this expr may already be calculated to get the predicate + // to detect them, traverse them later on, see which flags should be calculated to calculate + // them. If any of them is completely covered w/ our non-conditional flags, no reason + // to add them to the list since we'll already be calculated due to our non-conditional + // flags + + for (int i = clone.nextSetBit(0); i != -1; i = clone.nextSetBit(i + 1)) { + final Expr expr = mModel.findFlagExpression(i); + if (!expr.isConditional()) { + continue; + } + final BitSet readForConditional = expr.findConditionalFlags(); + // to calculate that conditional, i should've read /readForConditional/ flags + // if my read-so-far bits has any common w/ that; that means i would've already + // read myself + clone.andNot(readForConditional); + final BitSet invalidFlags = (BitSet) getInvalidFlags().clone(); + invalidFlags.andNot(readForConditional); + mRead = invalidFlags.isEmpty() || clone.isEmpty(); + } + + } + if (mRead) { + mShouldReadFlags = null; // if we've been marked as read, clear should read flags + } + return mRead; + } + + BitSet mConditionalFlags; + + private BitSet findConditionalFlags() { + Preconditions.checkState(isConditional(), "should not call this on a non-conditional expr"); + if (mConditionalFlags == null) { + mConditionalFlags = new BitSet(); + resolveConditionalFlags(mConditionalFlags); + } + return mConditionalFlags; + } + + private void resolveConditionalFlags(BitSet flags) { + flags.or(getPredicateInvalidFlags()); + // if i have only 1 dependency which is conditional, traverse it as well + if (getDependants().size() == 1) { + final Dependency dependency = getDependants().get(0); + if (dependency.getCondition() != null) { + flags.or(dependency.getDependant().findConditionalFlags()); + flags.set(dependency.getDependant() + .getRequirementFlagIndex(dependency.getExpectedOutput())); + } + } + } + + + @Override + public String toString() { + return getUniqueKey(); + } + + public BitSet getReadSoFar() { + return mReadSoFar; + } + + private Node mCalculationPaths = null; + + protected Node getAllCalculationPaths() { + if (mCalculationPaths == null) { + Node node = new Node(); + // TODO distant parent w/ conditionals are still not traversed :/ + if (isConditional()) { + node.mBitSet.or(getPredicateInvalidFlags()); + } else { + node.mBitSet.or(getInvalidFlags()); + } + for (Dependency dependency : getDependants()) { + final Expr dependant = dependency.getDependant(); + if (dependency.getCondition() != null) { + Node cond = new Node(); + cond.setConditionFlag( + dependant.getRequirementFlagIndex(dependency.getExpectedOutput())); + cond.mParents.add(dependant.getAllCalculationPaths()); + } else { + node.mParents.add(dependant.getAllCalculationPaths()); + } + } + mCalculationPaths = node; + } + return mCalculationPaths; + } + + public String getDefaultValue() { + return ModelAnalyzer.getInstance().getDefaultValue(getResolvedType().toJavaCode()); + } + + protected BitSet getPredicateInvalidFlags() { + throw new IllegalStateException( + "must override getPredicateInvalidFlags in " + getClass().getSimpleName()); + } + + /** + * Used by code generation + */ + public boolean shouldReadNow(final Iterable<Expr> justRead) { + return !getShouldReadFlags().isEmpty() && + !Iterables.any(getDependencies(), new Predicate<Dependency>() { + @Override + public boolean apply(Dependency input) { + return !(input.getOther().isRead() || (justRead != null && Iterables + .contains(justRead, input.getOther()))); + } + }); + } + + public boolean isEqualityCheck() { + return false; + } + + public void setIsUsed(boolean isUsed) { + mIsUsed = isUsed; + } + + public boolean isUsed() { + return mIsUsed; + } + + public void updateExpr(ModelAnalyzer modelAnalyzer) { + for (Expr child : mChildren) { + child.updateExpr(modelAnalyzer); + } + } + + protected String asPackage() { + return null; + } + + static class Node { + + BitSet mBitSet = new BitSet(); + List<Node> mParents = new ArrayList<Node>(); + int mConditionFlag = -1; + + public boolean areAllPathsSatisfied(BitSet readSoFar) { + if (mConditionFlag != -1) { + return readSoFar.get(mConditionFlag) || mParents.get(0) + .areAllPathsSatisfied(readSoFar); + } else { + final BitSet clone = (BitSet) readSoFar.clone(); + readSoFar.and(mBitSet); + if (!readSoFar.isEmpty()) { + return true; + } + if (mParents.isEmpty()) { + return false; + } + for (Node parent : mParents) { + if (!parent.areAllPathsSatisfied(readSoFar)) { + return false; + } + } + return true; + } + } + + public void setConditionFlag(int requirementFlagIndex) { + mConditionFlag = requirementFlagIndex; + } + } +} diff --git a/tools/data-binding/compiler/src/main/java/android/databinding/tool/expr/ExprModel.java b/tools/data-binding/compiler/src/main/java/android/databinding/tool/expr/ExprModel.java new file mode 100644 index 0000000..0f8a935 --- /dev/null +++ b/tools/data-binding/compiler/src/main/java/android/databinding/tool/expr/ExprModel.java @@ -0,0 +1,495 @@ +/* + * 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 android.databinding.tool.expr; + +import com.google.common.base.Preconditions; +import com.google.common.base.Predicate; +import com.google.common.collect.Iterables; +import com.google.common.collect.Lists; + +import android.databinding.tool.reflection.ModelAnalyzer; +import android.databinding.tool.util.L; +import android.databinding.tool.writer.FlagSet; + +import java.util.ArrayList; +import java.util.Arrays; +import java.util.BitSet; +import java.util.HashMap; +import java.util.List; +import java.util.Map; + +public class ExprModel { + + Map<String, Expr> mExprMap = new HashMap<String, Expr>(); + + List<Expr> mBindingExpressions = new ArrayList<Expr>(); + + private int mInvalidateableFieldLimit = 0; + + private int mRequirementIdCount = 0; + + private static final String TRUE_KEY_SUFFIX = "== true"; + private static final String FALSE_KEY_SUFFIX = "== false"; + + /** + * Used by code generation. Keeps the list of expressions that are waiting to be evaluated. + */ + private List<Expr> mPendingExpressions; + + /** + * Used for converting flags into identifiers while debugging. + */ + private String[] mFlagMapping; + + private BitSet mInvalidateableFlags; + private BitSet mConditionalFlags; + + private int mFlagBucketCount;// how many buckets we use to identify flags + + private List<Expr> mObservables; + + private Map<String, String> mImports = new HashMap<String, String>(); + + /** + * Adds the expression to the list of expressions and returns it. + * If it already exists, returns existing one. + * + * @param expr The new parsed expression + * @return The expression itself or another one if the same thing was parsed before + */ + public <T extends Expr> T register(T expr) { + T existing = (T) mExprMap.get(expr.getUniqueKey()); + if (existing != null) { + Preconditions.checkState(expr.getParents().isEmpty(), + "If an expression already exists, it should've never been added to a parent," + + "if thats the case, somewhere we are creating an expression w/o" + + "calling expression model"); + // tell the expr that it is being swapped so that if it was added to some other expr + // as a parent, those can swap their references + expr.onSwappedWith(existing); + return existing; + } + mExprMap.put(expr.getUniqueKey(), expr); + expr.setModel(this); + return expr; + } + + public void unregister(Expr expr) { + mExprMap.remove(expr.getUniqueKey()); + } + + public Map<String, Expr> getExprMap() { + return mExprMap; + } + + public int size() { + return mExprMap.size(); + } + + public ComparisonExpr comparison(String op, Expr left, Expr right) { + return register(new ComparisonExpr(op, left, right)); + } + + public FieldAccessExpr field(Expr parent, String name) { + return register(new FieldAccessExpr(parent, name)); + } + + public FieldAccessExpr observableField(Expr parent, String name) { + return register(new FieldAccessExpr(parent, name, true)); + } + + public SymbolExpr symbol(String text, Class type) { + return register(new SymbolExpr(text, type)); + } + + public TernaryExpr ternary(Expr pred, Expr ifTrue, Expr ifFalse) { + return register(new TernaryExpr(pred, ifTrue, ifFalse)); + } + + public IdentifierExpr identifier(String name) { + return register(new IdentifierExpr(name)); + } + + public StaticIdentifierExpr staticIdentifier(String name) { + return register(new StaticIdentifierExpr(name)); + } + + public MethodCallExpr methodCall(Expr target, String name, List<Expr> args) { + return register(new MethodCallExpr(target, name, args)); + } + + public MathExpr math(Expr left, String op, Expr right) { + return register(new MathExpr(left, op, right)); + } + + public Expr group(Expr grouped) { + return register(new GroupExpr(grouped)); + } + + public Expr resourceExpr(String packageName, String resourceType, String resourceName, + List<Expr> args) { + return register(new ResourceExpr(packageName, resourceType, resourceName, args)); + } + + public Expr bracketExpr(Expr variableExpr, Expr argExpr) { + return register(new BracketExpr(variableExpr, argExpr)); + } + + public Expr castExpr(String type, Expr expr) { + return register(new CastExpr(type, expr)); + } + + public List<Expr> getBindingExpressions() { + return mBindingExpressions; + } + + public void addImport(String alias, String type) { + Preconditions.checkState(!mImports.containsKey(alias), + "%s has already been defined as %s", alias, type); + final StaticIdentifierExpr id = staticIdentifier(alias); + L.d("adding import %s as %s klass: %s", type, alias, id.getClass().getSimpleName()); + id.setUserDefinedType(type); + mImports.put(alias, type); + } + + public Map<String, String> getImports() { + return mImports; + } + + /** + * The actual thingy that is set on the binding target. + * + * Input must be already registered + */ + public Expr bindingExpr(Expr bindingExpr) { + Preconditions.checkArgument(mExprMap.containsKey(bindingExpr.getUniqueKey()), + "Main expression should already be registered"); + if (!mBindingExpressions.contains(bindingExpr)) { + mBindingExpressions.add(bindingExpr); + } + return bindingExpr; + } + + /** + * Nodes to which no one depends + */ + public Iterable<Expr> findRootNodes() { + return Iterables.filter(mExprMap.values(), new Predicate<Expr>() { + @Override + public boolean apply(Expr input) { + return input.getParents().isEmpty(); + } + }); + } + + /** + * Nodes, which do not depend on any other node + */ + public Iterable<Expr> findLeafNodes() { + return Iterables.filter(mExprMap.values(), new Predicate<Expr>() { + @Override + public boolean apply(Expr input) { + return input.getChildren().isEmpty(); + } + }); + } + + public List<Expr> getObservables() { + return mObservables; + } + + /** + * Give id to each expression. Will be useful if we serialize. + */ + public void seal() { + List<Expr> notifiableExpressions = new ArrayList<Expr>(); + //ensure class analyzer. We need to know observables at this point + final ModelAnalyzer modelAnalyzer = ModelAnalyzer.getInstance(); + + ArrayList<Expr> exprs = new ArrayList<Expr>(mBindingExpressions); + for (Expr expr: exprs) { + expr.updateExpr(modelAnalyzer); + } + + int counter = 0; + final Iterable<Expr> observables = filterObservables(modelAnalyzer); + List<String> flagMapping = Lists.newArrayList(); + mObservables = Lists.newArrayList(); + for (Expr expr : observables) { + // observables gets initial ids + flagMapping.add(expr.getUniqueKey()); + expr.setId(counter++); + mObservables.add(expr); + notifiableExpressions.add(expr); + L.d("observable %s", expr.getUniqueKey()); + } + + // non-observable identifiers gets next ids + final Iterable<Expr> nonObservableIds = filterNonObservableIds(modelAnalyzer); + for (Expr expr : nonObservableIds) { + flagMapping.add(expr.getUniqueKey()); + expr.setId(counter++); + notifiableExpressions.add(expr); + L.d("non-observable %s", expr.getUniqueKey()); + } + + // descendents of observables gets following ids + for (Expr expr : observables) { + for (Expr parent : expr.getParents()) { + if (parent.hasId()) { + continue;// already has some id, means observable + } + // only fields earn an id + if (parent instanceof FieldAccessExpr) { + FieldAccessExpr fae = (FieldAccessExpr) parent; + L.d("checking field access expr %s. getter: %s", fae,fae.getGetter()); + if (fae.isDynamic() && fae.getGetter().canBeInvalidated) { + flagMapping.add(parent.getUniqueKey()); + parent.setId(counter++); + notifiableExpressions.add(parent); + L.d("notifiable field %s : %s for %s : %s", parent.getUniqueKey(), + Integer.toHexString(System.identityHashCode(parent)), + expr.getUniqueKey(), + Integer.toHexString(System.identityHashCode(expr))); + } + } + } + } + + // non-dynamic binding expressions receive some ids so that they can be invalidated + for (int i = 0; i < mBindingExpressions.size(); i++) { + L.d("[" + i + "] " + mBindingExpressions.get(i)); + } + for (Expr expr : mBindingExpressions) { + if (!(expr.isDynamic() || !expr.hasId())) { + L.d("Expr " + expr + " is dynamic? " + expr.isDynamic() + ", has ID? " + expr.hasId()); + } + Preconditions.checkState(expr.isDynamic() || !expr.hasId()); + if (!expr.isDynamic()) { + // give it an id for invalidateAll + expr.setId(counter ++); + notifiableExpressions.add(expr); + } + } + + for (Expr expr : notifiableExpressions) { + expr.enableDirectInvalidation(); + } + + // make sure all dependencies are resolved to avoid future race conditions + for (Expr expr : mExprMap.values()) { + expr.getDependencies(); + } + + mInvalidateableFieldLimit = counter; + mInvalidateableFlags = new BitSet(); + for (int i = 0; i < mInvalidateableFieldLimit; i++) { + mInvalidateableFlags.set(i, true); + } + + // make sure all dependencies are resolved to avoid future race conditions + for (Expr expr : mExprMap.values()) { + if (expr.isConditional()) { + expr.setRequirementId(counter); + flagMapping.add(expr.getUniqueKey() + FALSE_KEY_SUFFIX); + flagMapping.add(expr.getUniqueKey() + TRUE_KEY_SUFFIX); + counter += 2; + } + } + mConditionalFlags = new BitSet(); + for (int i = mInvalidateableFieldLimit; i < counter; i++) { + mConditionalFlags.set(i, true); + } + + mRequirementIdCount = (counter - mInvalidateableFieldLimit) / 2; + + // everybody gets an id + for (Map.Entry<String, Expr> entry : mExprMap.entrySet()) { + final Expr value = entry.getValue(); + if (!value.hasId()) { + value.setId(counter++); + } + } + mFlagMapping = new String[flagMapping.size()]; + flagMapping.toArray(mFlagMapping); + + for (Expr expr : mExprMap.values()) { + expr.getShouldReadFlagsWithConditionals(); + } + + for (Expr expr : mExprMap.values()) { + // ensure all types are calculated + expr.getResolvedType(); + } + + mFlagBucketCount = 1 + (getTotalFlagCount() / FlagSet.sBucketSize); + } + + public int getFlagBucketCount() { + return mFlagBucketCount; + } + + public int getTotalFlagCount() { + return mRequirementIdCount * 2 + mInvalidateableFieldLimit; + } + + public int getInvalidateableFieldLimit() { + return mInvalidateableFieldLimit; + } + + public String[] getFlagMapping() { + return mFlagMapping; + } + + public String getFlag(int id) { + return mFlagMapping[id]; + } + + 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() + && !input.isObservable() + && input.isDynamic(); + } + }); + } + + private Iterable<Expr> filterObservables(final ModelAnalyzer modelAnalyzer) { + return Iterables.filter(mExprMap.values(), new Predicate<Expr>() { + @Override + public boolean apply(Expr input) { + return input.isObservable(); + } + }); + } + + public List<Expr> getPendingExpressions() { + if (mPendingExpressions == null) { + mPendingExpressions = Lists.newArrayList(); + for (Expr expr : mExprMap.values()) { + if (!expr.isRead() && expr.isDynamic()) { + mPendingExpressions.add(expr); + } + } + } + return mPendingExpressions; + } + + public boolean markBitsRead() { + L.d("marking bits as done"); + // each has should read flags, we set them back on them + for (Expr expr : filterShouldRead(getPendingExpressions())) { + expr.markFlagsAsRead(expr.getShouldReadFlags()); + } + return pruneDone(); + } + + private boolean pruneDone() { + boolean marked = true; + List<Expr> markedAsReadList = Lists.newArrayList(); + while (marked) { + marked = false; + for (Expr expr : mExprMap.values()) { + if (expr.isRead()) { + continue; + } + if (expr.markAsReadIfDone()) { + L.d("marked %s as read ", expr.getUniqueKey()); + marked = true; + markedAsReadList.add(expr); + } + + } + } + boolean elevated = false; + for (Expr markedAsRead : markedAsReadList) { + for (Dependency dependency : markedAsRead.getDependants()) { + if (dependency.getDependant().considerElevatingConditionals(markedAsRead)) { + elevated = true; + } + } + } + if (elevated) { + // some conditionals are elevated. We should re-calculate flags + for (Expr expr : getPendingExpressions()) { + if (!expr.isRead()) { + expr.invalidateReadFlags(); + } + } + mPendingExpressions = null; + } + return elevated; + } + + public static Iterable<Expr> filterShouldRead(Iterable<Expr> exprs) { + return toCollection(Iterables.filter(exprs, sShouldReadPred)); + } + + public static List<Expr> toCollection(Iterable<Expr> iterable) { + return Arrays.asList(Iterables.toArray(iterable, Expr.class)); + } + + private static final Predicate<Expr> sShouldReadPred = new Predicate<Expr>() { + @Override + public boolean apply(final Expr expr) { + return !expr.getShouldReadFlags().isEmpty() && !Iterables.any( + expr.getDependencies(), new Predicate<Dependency>() { + @Override + public boolean apply(Dependency dependency) { + final boolean result = dependency.isConditional() || + dependency.getOther().hasNestedCannotRead(); + return result; + } + }); + } + }; + + private static final Predicate<Expr> sReadNowPred = new Predicate<Expr>() { + @Override + public boolean apply(Expr input) { + return !input.getShouldReadFlags().isEmpty() && + !Iterables.any(input.getDependencies(), new Predicate<Dependency>() { + @Override + public boolean apply(Dependency input) { + return !input.getOther().isRead(); + } + }); + } + }; + + public Expr findFlagExpression(int flag) { + final String key = mFlagMapping[flag]; + if (mExprMap.containsKey(key)) { + return mExprMap.get(key); + } + int falseIndex = key.indexOf(FALSE_KEY_SUFFIX); + if (falseIndex > -1) { + final String trimmed = key.substring(0, falseIndex); + return mExprMap.get(trimmed); + } + int trueIndex = key.indexOf(TRUE_KEY_SUFFIX); + if (trueIndex > -1) { + final String trimmed = key.substring(0, trueIndex); + return mExprMap.get(trimmed); + } + Preconditions.checkArgument(false, "cannot find expression for flag %d", flag); + return null; + } +} diff --git a/tools/data-binding/compiler/src/main/java/android/databinding/tool/expr/FieldAccessExpr.java b/tools/data-binding/compiler/src/main/java/android/databinding/tool/expr/FieldAccessExpr.java new file mode 100644 index 0000000..754cca8 --- /dev/null +++ b/tools/data-binding/compiler/src/main/java/android/databinding/tool/expr/FieldAccessExpr.java @@ -0,0 +1,127 @@ +/* + * 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 android.databinding.tool.expr; + +import android.databinding.tool.reflection.Callable; +import android.databinding.tool.reflection.ModelAnalyzer; +import android.databinding.tool.reflection.ModelClass; +import android.databinding.tool.util.L; + +import java.util.List; + +public class FieldAccessExpr extends Expr { + String mName; + Callable mGetter; + final boolean mIsObservableField; + + FieldAccessExpr(Expr parent, String name) { + super(parent); + mName = name; + mIsObservableField = false; + } + + FieldAccessExpr(Expr parent, String name, boolean isObservableField) { + super(parent); + mName = name; + mIsObservableField = isObservableField; + } + + public Expr getChild() { + return getChildren().get(0); + } + + public Callable getGetter() { + if (mGetter == null) { + getResolvedType(); + } + return mGetter; + } + + @Override + public boolean isDynamic() { + if (!getChild().isDynamic()) { + return false; + } + if (mGetter == null) { + getResolvedType(); + } + // maybe this is just a final field in which case cannot be notified as changed + return mGetter.type != Callable.Type.FIELD || mGetter.isDynamic; + } + + @Override + protected List<Dependency> constructDependencies() { + final List<Dependency> dependencies = constructDynamicChildrenDependencies(); + for (Dependency dependency : dependencies) { + if (dependency.getOther() == getChild()) { + dependency.setMandatory(true); + } + } + return dependencies; + } + + @Override + protected String computeUniqueKey() { + if (mIsObservableField) { + return sUniqueKeyJoiner.join(mName, "..", super.computeUniqueKey()); + } + return sUniqueKeyJoiner.join(mName, ".", super.computeUniqueKey()); + } + + public String getName() { + return mName; + } + + @Override + public void updateExpr(ModelAnalyzer modelAnalyzer) { + resolveType(modelAnalyzer); + super.updateExpr(modelAnalyzer); + } + + @Override + protected ModelClass resolveType(ModelAnalyzer modelAnalyzer) { + if (mGetter == null) { + Expr child = getChild(); + child.resolveType(modelAnalyzer); + boolean isStatic = child instanceof StaticIdentifierExpr; + ModelClass resolvedType = child.getResolvedType(); + L.d("resolving %s. Resolved type: %s", this, resolvedType); + + mGetter = resolvedType.findGetterOrField(mName, isStatic); + if (mGetter.resolvedType.isObservableField()) { + // Make this the ".get()" and add an extra field access for the observable field + child.getParents().remove(this); + getChildren().remove(child); + + FieldAccessExpr observableField = getModel().observableField(child, mName); + observableField.mGetter = mGetter; + + getChildren().add(observableField); + observableField.getParents().add(this); + mGetter = mGetter.resolvedType.findGetterOrField("get", false); + mName = ""; + } + } + return mGetter.resolvedType; + } + + @Override + protected String asPackage() { + String parentPackage = getChild().asPackage(); + return parentPackage == null ? null : parentPackage + "." + mName; + } +} diff --git a/tools/data-binding/compiler/src/main/java/android/databinding/tool/expr/GroupExpr.java b/tools/data-binding/compiler/src/main/java/android/databinding/tool/expr/GroupExpr.java new file mode 100644 index 0000000..8af1d68 --- /dev/null +++ b/tools/data-binding/compiler/src/main/java/android/databinding/tool/expr/GroupExpr.java @@ -0,0 +1,42 @@ +/* + * 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 android.databinding.tool.expr; + +import android.databinding.tool.reflection.ModelAnalyzer; +import android.databinding.tool.reflection.ModelClass; + +import java.util.List; + +public class GroupExpr extends Expr { + public GroupExpr(Expr wrapped) { + super(wrapped); + } + + @Override + protected ModelClass resolveType(ModelAnalyzer modelAnalyzer) { + return getWrapped().resolveType(modelAnalyzer); + } + + @Override + protected List<Dependency> constructDependencies() { + return getWrapped().constructDependencies(); + } + + public Expr getWrapped() { + return getChildren().get(0); + } +} diff --git a/tools/data-binding/compiler/src/main/java/android/databinding/tool/expr/IdentifierExpr.java b/tools/data-binding/compiler/src/main/java/android/databinding/tool/expr/IdentifierExpr.java new file mode 100644 index 0000000..5207485 --- /dev/null +++ b/tools/data-binding/compiler/src/main/java/android/databinding/tool/expr/IdentifierExpr.java @@ -0,0 +1,87 @@ +/* + * 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 android.databinding.tool.expr; + +import com.google.common.base.Preconditions; +import com.google.common.collect.Lists; + +import android.databinding.tool.reflection.ModelAnalyzer; +import android.databinding.tool.reflection.ModelClass; +import android.databinding.tool.util.L; + +import java.util.List; + +public class IdentifierExpr extends Expr { + String mName; + String mUserDefinedType; + IdentifierExpr(String name) { + mName = name; + } + + public String getName() { + return mName; + } + + /** + * If this is root, its type should be set while parsing the XML document + * @param userDefinedType The type of this identifier + */ + public void setUserDefinedType(String userDefinedType) { + mUserDefinedType = userDefinedType; + } + + @Override + protected String computeUniqueKey() { + return sUniqueKeyJoiner.join(mName, super.computeUniqueKey()); + } + + public String getUserDefinedType() { + return mUserDefinedType; + } + + public String getExpandedUserDefinedType(ModelAnalyzer modelAnalyzer) { + Preconditions.checkNotNull(mUserDefinedType, + "Identifiers must have user defined types from the XML file. %s is missing it", + mName); + final String expanded = modelAnalyzer + .applyImports(mUserDefinedType, getModel().getImports()); + L.d("expanded version of %s is %s", mUserDefinedType, expanded); + return expanded; + } + + @Override + public boolean isDynamic() { + return true; + } + + @Override + 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 modelAnalyzer.findClass(mUserDefinedType, getModel().getImports()); + } + + @Override + protected List<Dependency> constructDependencies() { + return Lists.newArrayList(); + } + + @Override + protected String asPackage() { + return mUserDefinedType == null ? mName : null; + } +} diff --git a/tools/data-binding/compiler/src/main/java/android/databinding/tool/expr/MathExpr.java b/tools/data-binding/compiler/src/main/java/android/databinding/tool/expr/MathExpr.java new file mode 100644 index 0000000..fbd0b3d --- /dev/null +++ b/tools/data-binding/compiler/src/main/java/android/databinding/tool/expr/MathExpr.java @@ -0,0 +1,65 @@ +/* + * 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 android.databinding.tool.expr; + +import android.databinding.tool.reflection.ModelAnalyzer; +import android.databinding.tool.reflection.ModelClass; + +import java.util.List; + +public class MathExpr extends Expr { + final String mOp; + MathExpr(Expr left, String op, Expr right) { + super(left, right); + mOp = op; + } + + @Override + protected String computeUniqueKey() { + return sUniqueKeyJoiner.join(getLeft().getUniqueKey(), mOp, getRight().getUniqueKey()); + } + + @Override + protected ModelClass resolveType(ModelAnalyzer modelAnalyzer) { + if ("+".equals(mOp)) { + // TODO we need upper casting etc. + if (getLeft().getResolvedType().isString() + || getRight().getResolvedType().isString()) { + return modelAnalyzer.findClass(String.class); + } + } + return modelAnalyzer.findCommonParentOf(getLeft().getResolvedType(), + getRight().getResolvedType()); + } + + @Override + protected List<Dependency> constructDependencies() { + return constructDynamicChildrenDependencies(); + } + + public String getOp() { + return mOp; + } + + public Expr getLeft() { + return getChildren().get(0); + } + + public Expr getRight() { + return getChildren().get(1); + } +} diff --git a/tools/data-binding/compiler/src/main/java/android/databinding/tool/expr/MethodCallExpr.java b/tools/data-binding/compiler/src/main/java/android/databinding/tool/expr/MethodCallExpr.java new file mode 100644 index 0000000..491adc8 --- /dev/null +++ b/tools/data-binding/compiler/src/main/java/android/databinding/tool/expr/MethodCallExpr.java @@ -0,0 +1,105 @@ +/* + * 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 android.databinding.tool.expr; + +import com.google.common.collect.Iterables; + +import android.databinding.tool.reflection.Callable; +import android.databinding.tool.reflection.Callable.Type; +import android.databinding.tool.reflection.ModelAnalyzer; +import android.databinding.tool.reflection.ModelClass; +import android.databinding.tool.reflection.ModelMethod; +import android.databinding.tool.util.L; + +import java.util.ArrayList; +import java.util.Arrays; +import java.util.List; + +public class MethodCallExpr extends Expr { + final String mName; + + Callable mGetter; + + MethodCallExpr(Expr target, String name, List<Expr> args) { + super(Iterables.concat(Arrays.asList(target), args)); + mName = name; + } + + @Override + public void updateExpr(ModelAnalyzer modelAnalyzer) { + resolveType(modelAnalyzer); + super.updateExpr(modelAnalyzer); + } + + @Override + protected ModelClass resolveType(ModelAnalyzer modelAnalyzer) { + if (mGetter == null) { + List<ModelClass> args = new ArrayList<ModelClass>(); + for (Expr expr : getArgs()) { + args.add(expr.getResolvedType()); + } + + Expr target = getTarget(); + boolean isStatic = target instanceof StaticIdentifierExpr; + ModelMethod method = target.getResolvedType().getMethod(mName, args, isStatic); + if (method == null) { + String message = "cannot find method '" + mName + "' in class " + + target.getResolvedType().toJavaCode(); + IllegalArgumentException e = new IllegalArgumentException(message); + L.e(e, "cannot find method %s in class %s", mName, + target.getResolvedType().toJavaCode()); + throw e; + } + mGetter = new Callable(Type.METHOD, method.getName(), method.getReturnType(args), true, + false); + } + return mGetter.resolvedType; + } + + @Override + protected List<Dependency> constructDependencies() { + final List<Dependency> dependencies = constructDynamicChildrenDependencies(); + for (Dependency dependency : dependencies) { + if (dependency.getOther() == getTarget()) { + dependency.setMandatory(true); + } + } + return dependencies; + } + + @Override + protected String computeUniqueKey() { + return sUniqueKeyJoiner.join(getTarget().computeUniqueKey(), mName, + super.computeUniqueKey()); + } + + public Expr getTarget() { + return getChildren().get(0); + } + + public String getName() { + return mName; + } + + public List<Expr> getArgs() { + return getChildren().subList(1, getChildren().size()); + } + + public Callable getGetter() { + return mGetter; + } +} diff --git a/tools/data-binding/compiler/src/main/java/android/databinding/tool/expr/ResourceExpr.java b/tools/data-binding/compiler/src/main/java/android/databinding/tool/expr/ResourceExpr.java new file mode 100644 index 0000000..6639103 --- /dev/null +++ b/tools/data-binding/compiler/src/main/java/android/databinding/tool/expr/ResourceExpr.java @@ -0,0 +1,205 @@ +/* + * 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 android.databinding.tool.expr; + +import com.google.common.collect.ImmutableMap; + +import android.databinding.tool.reflection.ModelAnalyzer; +import android.databinding.tool.reflection.ModelClass; +import android.databinding.tool.writer.WriterPackage; + +import java.util.List; +import java.util.Map; + +public class ResourceExpr extends Expr { + + private final static Map<String, String> RESOURCE_TYPE_TO_R_OBJECT = + ImmutableMap.<String, String>builder() + .put("colorStateList", "color ") + .put("dimenOffset", "dimen ") + .put("dimenSize", "dimen ") + .put("intArray", "array ") + .put("stateListAnimator", "animator ") + .put("stringArray", "array ") + .put("typedArray", "array") + .build(); + + // lazily initialized + private Map<String, ModelClass> mResourceToTypeMapping; + + protected final String mPackage; + + protected final String mResourceType; + + protected final String mResourceId; + + public ResourceExpr(String packageName, String resourceType, String resourceName, + List<Expr> args) { + super(args); + if ("android".equals(packageName)) { + mPackage = "android."; + } else { + mPackage = ""; + } + mResourceType = resourceType; + mResourceId = resourceName; + } + + private Map<String, ModelClass> getResourceToTypeMapping(ModelAnalyzer modelAnalyzer) { + if (mResourceToTypeMapping == null) { + final Map<String, String> imports = getModel().getImports(); + mResourceToTypeMapping = ImmutableMap.<String, ModelClass>builder() + .put("anim", modelAnalyzer.findClass("android.view.animation.Animation", + imports)) + .put("animator", modelAnalyzer.findClass("android.animation.Animator", + imports)) + .put("colorStateList", + modelAnalyzer.findClass("android.content.res.ColorStateList", + imports)) + .put("drawable", modelAnalyzer.findClass("android.graphics.drawable.Drawable", + imports)) + .put("stateListAnimator", + modelAnalyzer.findClass("android.animation.StateListAnimator", + imports)) + .put("transition", modelAnalyzer.findClass("android.transition.Transition", + imports)) + .put("typedArray", modelAnalyzer.findClass("android.content.res.TypedArray", + imports)) + .put("interpolator", + modelAnalyzer.findClass("android.view.animation.Interpolator", imports)) + .put("bool", modelAnalyzer.findClass(boolean.class)) + .put("color", modelAnalyzer.findClass(int.class)) + .put("dimenOffset", modelAnalyzer.findClass(int.class)) + .put("dimenSize", modelAnalyzer.findClass(int.class)) + .put("id", modelAnalyzer.findClass(int.class)) + .put("integer", modelAnalyzer.findClass(int.class)) + .put("layout", modelAnalyzer.findClass(int.class)) + .put("dimen", modelAnalyzer.findClass(float.class)) + .put("fraction", modelAnalyzer.findClass(float.class)) + .put("intArray", modelAnalyzer.findClass(int[].class)) + .put("string", modelAnalyzer.findClass(String.class)) + .put("stringArray", modelAnalyzer.findClass(String[].class)) + .build(); + } + return mResourceToTypeMapping; + } + + @Override + protected ModelClass resolveType(ModelAnalyzer modelAnalyzer) { + final Map<String, ModelClass> mapping = getResourceToTypeMapping( + modelAnalyzer); + final ModelClass modelClass = mapping.get(mResourceType); + if (modelClass != null) { + return modelClass; + } + if ("plurals".equals(mResourceType)) { + if (getChildren().isEmpty()) { + return modelAnalyzer.findClass(int.class); + } else { + return modelAnalyzer.findClass(String.class); + } + } + return modelAnalyzer.findClass(mResourceType, getModel().getImports()); + } + + @Override + protected List<Dependency> constructDependencies() { + return constructDynamicChildrenDependencies(); + } + + @Override + protected String computeUniqueKey() { + String base; + if (mPackage == null) { + base = "@" + mResourceType + "/" + mResourceId; + } else { + base = "@" + "android:" + mResourceType + "/" + mResourceId; + } + return sUniqueKeyJoiner.join(base, computeChildrenKey()); + } + + public String getResourceId() { + return mPackage + "R." + getResourceObject() + "." + mResourceId; + } + + public String toJava() { + final String context = "getRoot().getContext()"; + final String resources = "getRoot().getResources()"; + final String resourceName = mPackage + "R." + getResourceObject() + "." + mResourceId; + if ("anim".equals(mResourceType)) return "android.view.animation.AnimationUtils.loadAnimation(" + context + ", " + resourceName + ")"; + if ("animator".equals(mResourceType)) return "android.animation.AnimatorInflater.loadAnimator(" + context + ", " + resourceName + ")"; + if ("bool".equals(mResourceType)) return resources + ".getBoolean(" + resourceName + ")"; + if ("color".equals(mResourceType)) return resources + ".getColor(" + resourceName + ")"; + if ("colorStateList".equals(mResourceType)) return resources + ".getColorStateList(" + resourceName + ")"; + if ("dimen".equals(mResourceType)) return resources + ".getDimension(" + resourceName + ")"; + if ("dimenOffset".equals(mResourceType)) return resources + ".getDimensionPixelOffset(" + resourceName + ")"; + if ("dimenSize".equals(mResourceType)) return resources + ".getDimensionPixelSize(" + resourceName + ")"; + if ("drawable".equals(mResourceType)) return resources + ".getDrawable(" + resourceName + ")"; + if ("fraction".equals(mResourceType)) { + String base = getChildCode(0, "1"); + String pbase = getChildCode(1, "1"); + return resources + ".getFraction(" + resourceName + ", " + base + ", " + pbase + + ")"; + } + if ("id".equals(mResourceType)) return resourceName; + if ("intArray".equals(mResourceType)) return resources + ".getIntArray(" + resourceName + ")"; + if ("integer".equals(mResourceType)) return resources + ".getInteger(" + resourceName + ")"; + if ("interpolator".equals(mResourceType)) return "android.view.animation.AnimationUtils.loadInterpolator(" + context + ", " + resourceName + ")"; + if ("layout".equals(mResourceType)) return resourceName; + if ("plurals".equals(mResourceType)) { + if (getChildren().isEmpty()) { + return resourceName; + } else { + return makeParameterCall(resourceName, "getQuantityString"); + } + } + if ("stateListAnimator".equals(mResourceType)) return "android.animation.AnimatorInflater.loadStateListAnimator(" + context + ", " + resourceName + ")"; + if ("string".equals(mResourceType)) return makeParameterCall(resourceName, "getString"); + if ("stringArray".equals(mResourceType)) return resources + ".getStringArray(" + resourceName + ")"; + if ("transition".equals(mResourceType)) return "android.transition.TransitionInflater.from(" + context + ").inflateTransition(" + resourceName + ")"; + if ("typedArray".equals(mResourceType)) return resources + ".obtainTypedArray(" + resourceName + ")"; + final String property = Character.toUpperCase(mResourceType.charAt(0)) + + mResourceType.substring(1); + return resources + ".get" + property + "(" + resourceName + ")"; + + } + + private String getChildCode(int childIndex, String defaultValue) { + if (getChildren().size() <= childIndex) { + return defaultValue; + } else { + return WriterPackage.toCode(getChildren().get(childIndex), false).generate(); + } + } + + private String makeParameterCall(String resourceName, String methodCall) { + StringBuilder sb = new StringBuilder("getRoot().getResources()."); + sb.append(methodCall).append("(").append(resourceName); + for (Expr expr : getChildren()) { + sb.append(", ").append(WriterPackage.toCode(expr, false).generate()); + } + sb.append(")"); + return sb.toString(); + } + + private String getResourceObject() { + String rFileObject = RESOURCE_TYPE_TO_R_OBJECT.get(mResourceType); + if (rFileObject == null) { + rFileObject = mResourceType; + } + return rFileObject; + } +} diff --git a/tools/data-binding/compiler/src/main/java/android/databinding/tool/expr/StaticIdentifierExpr.java b/tools/data-binding/compiler/src/main/java/android/databinding/tool/expr/StaticIdentifierExpr.java new file mode 100644 index 0000000..8ca5128 --- /dev/null +++ b/tools/data-binding/compiler/src/main/java/android/databinding/tool/expr/StaticIdentifierExpr.java @@ -0,0 +1,34 @@ +/* + * 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 android.databinding.tool.expr; + +public class StaticIdentifierExpr extends IdentifierExpr { + + StaticIdentifierExpr(String name) { + super(name); + } + + @Override + public boolean isObservable() { + return false; + } + + @Override + public boolean isDynamic() { + return false; + } +} diff --git a/tools/data-binding/compiler/src/main/java/android/databinding/tool/expr/SymbolExpr.java b/tools/data-binding/compiler/src/main/java/android/databinding/tool/expr/SymbolExpr.java new file mode 100644 index 0000000..9e03d2f --- /dev/null +++ b/tools/data-binding/compiler/src/main/java/android/databinding/tool/expr/SymbolExpr.java @@ -0,0 +1,54 @@ +/* + * 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 android.databinding.tool.expr; + +import com.google.common.collect.Lists; + +import android.databinding.tool.reflection.ModelAnalyzer; +import android.databinding.tool.reflection.ModelClass; + +import java.util.List; + +public class SymbolExpr extends Expr { + String mText; + Class mType; + + SymbolExpr(String text, Class type) { + super(); + mText = text; + mType = type; + } + + public String getText() { + return mText; + } + + @Override + protected ModelClass resolveType(ModelAnalyzer modelAnalyzer) { + return modelAnalyzer.findClass(mType); + } + + @Override + protected String computeUniqueKey() { + return mText; + } + + @Override + protected List<Dependency> constructDependencies() { + return Lists.newArrayList(); + } +} diff --git a/tools/data-binding/compiler/src/main/java/android/databinding/tool/expr/TernaryExpr.java b/tools/data-binding/compiler/src/main/java/android/databinding/tool/expr/TernaryExpr.java new file mode 100644 index 0000000..02e5cd7 --- /dev/null +++ b/tools/data-binding/compiler/src/main/java/android/databinding/tool/expr/TernaryExpr.java @@ -0,0 +1,84 @@ +/* + * 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 android.databinding.tool.expr; + +import com.google.common.collect.Lists; + +import android.databinding.tool.reflection.ModelAnalyzer; +import android.databinding.tool.reflection.ModelClass; + +import java.util.BitSet; +import java.util.List; + +public class TernaryExpr extends Expr { + TernaryExpr(Expr pred, Expr ifTrue, Expr ifFalse) { + super(pred, ifTrue, ifFalse); + } + + public Expr getPred() { + return getChildren().get(0); + } + + public Expr getIfTrue() { + return getChildren().get(1); + } + + public Expr getIfFalse() { + return getChildren().get(2); + } + + @Override + protected String computeUniqueKey() { + return "?:" + super.computeUniqueKey(); + } + + @Override + protected ModelClass resolveType(ModelAnalyzer modelAnalyzer) { + return modelAnalyzer.findCommonParentOf(getIfTrue().getResolvedType(), + getIfFalse().getResolvedType()); + } + + @Override + protected List<Dependency> constructDependencies() { + List<Dependency> deps = Lists.newArrayList(); + Expr predExpr = getPred(); + if (predExpr.isDynamic()) { + final Dependency pred = new Dependency(this, predExpr); + pred.setMandatory(true); + deps.add(pred); + } + Expr ifTrueExpr = getIfTrue(); + if (ifTrueExpr.isDynamic()) { + deps.add(new Dependency(this, ifTrueExpr, predExpr, true)); + } + Expr ifFalseExpr = getIfFalse(); + if (ifFalseExpr.isDynamic()) { + deps.add(new Dependency(this, ifFalseExpr, predExpr, false)); + } + return deps; + } + + @Override + protected BitSet getPredicateInvalidFlags() { + return getPred().getInvalidFlags(); + } + + @Override + public boolean isConditional() { + return true; + } +} diff --git a/tools/data-binding/compiler/src/main/java/android/databinding/tool/reflection/Callable.java b/tools/data-binding/compiler/src/main/java/android/databinding/tool/reflection/Callable.java new file mode 100644 index 0000000..8883124 --- /dev/null +++ b/tools/data-binding/compiler/src/main/java/android/databinding/tool/reflection/Callable.java @@ -0,0 +1,58 @@ +/* + * Copyright (C) 2015 The Android Open Source Project + * + * Licensed under the Apache License, Version 2.0 (the "License"); + * you may not use this file except in compliance with the License. + * You may obtain a copy of the License at + * + * http://www.apache.org/licenses/LICENSE-2.0 + * + * Unless required by applicable law or agreed to in writing, software + * distributed under the License is distributed on an "AS IS" BASIS, + * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. + * See the License for the specific language governing permissions and + * limitations under the License. + */ +package android.databinding.tool.reflection; + +public class Callable { + + public static enum Type { + METHOD, + FIELD + } + + public final Type type; + + public final String name; + + public final ModelClass resolvedType; + + public final boolean isDynamic; + + public final boolean canBeInvalidated; + + public Callable(Type type, String name, ModelClass resolvedType, boolean isDynamic, + boolean canBeInvalidated) { + this.type = type; + this.name = name; + this.resolvedType = resolvedType; + this.isDynamic = isDynamic; + this.canBeInvalidated = canBeInvalidated; + } + + public String getTypeCodeName() { + return resolvedType.toJavaCode(); + } + + @Override + public String toString() { + return "Callable{" + + "type=" + type + + ", name='" + name + '\'' + + ", resolvedType=" + resolvedType + + ", isDynamic=" + isDynamic + + ", canBeInvalidated=" + canBeInvalidated + + '}'; + } +} diff --git a/tools/data-binding/compiler/src/main/java/android/databinding/tool/reflection/ModelAnalyzer.java b/tools/data-binding/compiler/src/main/java/android/databinding/tool/reflection/ModelAnalyzer.java new file mode 100644 index 0000000..187b9dc --- /dev/null +++ b/tools/data-binding/compiler/src/main/java/android/databinding/tool/reflection/ModelAnalyzer.java @@ -0,0 +1,298 @@ +/* + * 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 android.databinding.tool.reflection; + +import com.google.common.base.Preconditions; + +import android.databinding.tool.reflection.annotation.AnnotationAnalyzer; +import android.databinding.tool.util.L; + +import java.util.Map; + +import javax.annotation.processing.ProcessingEnvironment; + +/** + * This is the base class for several implementations of something that + * acts like a ClassLoader. Different implementations work with the Annotation + * Processor, ClassLoader, and an Android Studio plugin. + */ +public abstract class ModelAnalyzer { + + public static final String[] LIST_CLASS_NAMES = { + "java.util.List", + "android.util.SparseArray", + "android.util.SparseBooleanArray", + "android.util.SparseIntArray", + "android.util.SparseLongArray", + "android.util.LongSparseArray", + "android.support.v4.util.LongSparseArray", + }; + + 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"; + + public static final String OBSERVABLE_CLASS_NAME = "android.databinding.Observable"; + + public static final String OBSERVABLE_LIST_CLASS_NAME = "android.databinding.ObservableList"; + + public static final String OBSERVABLE_MAP_CLASS_NAME = "android.databinding.ObservableMap"; + + public static final String[] OBSERVABLE_FIELDS = { + "android.databinding.ObservableBoolean", + "android.databinding.ObservableByte", + "android.databinding.ObservableChar", + "android.databinding.ObservableShort", + "android.databinding.ObservableInt", + "android.databinding.ObservableLong", + "android.databinding.ObservableFloat", + "android.databinding.ObservableDouble", + "android.databinding.ObservableField", + }; + + public static final String VIEW_DATA_BINDING = + "android.databinding.ViewDataBinding"; + + public static final String VIEW_STUB_CLASS_NAME = "android.view.ViewStub"; + + private ModelClass[] mListTypes; + private ModelClass mMapType; + private ModelClass mStringType; + private ModelClass mObjectType; + private ModelClass mObservableType; + private ModelClass mObservableListType; + private ModelClass mObservableMapType; + private ModelClass[] mObservableFieldTypes; + private ModelClass mViewBindingType; + private ModelClass mViewStubType; + + private static ModelAnalyzer sAnalyzer; + + protected void setInstance(ModelAnalyzer analyzer) { + sAnalyzer = analyzer; + } + + public ModelClass findCommonParentOf(ModelClass modelClass1, + ModelClass modelClass2) { + ModelClass curr = modelClass1; + while (curr != null && !curr.isAssignableFrom(modelClass2)) { + curr = curr.getSuperclass(); + } + if (curr == null) { + ModelClass primitive1 = modelClass1.unbox(); + ModelClass primitive2 = modelClass2.unbox(); + if (!modelClass1.equals(primitive1) || !modelClass2.equals(primitive2)) { + return findCommonParentOf(primitive1, primitive2); + } + } + Preconditions.checkNotNull(curr, + "must be able to find a common parent for " + modelClass1 + " and " + modelClass2); + return curr; + + } + + public abstract ModelClass loadPrimitive(String className); + + public static ModelAnalyzer getInstance() { + return sAnalyzer; + } + + public static void setProcessingEnvironment(ProcessingEnvironment processingEnvironment) { + if (sAnalyzer != null) { + throw new IllegalStateException("processing env is already created, you cannot " + + "change class loader after that"); + } + L.d("setting processing env to %s", processingEnvironment); + AnnotationAnalyzer annotationAnalyzer = new AnnotationAnalyzer(processingEnvironment); + sAnalyzer = annotationAnalyzer; + } + + /** + * Takes a raw className (potentially w/ generics and arrays) and expands definitions using + * the import statements. + * <p> + * For instance, this allows user to define variables + * <variable type="User" name="user"/> + * if they previously imported User. + * <import name="com.example.User"/> + */ + public String applyImports(String className, Map<String, String> imports) { + className = className.trim(); + int numDimensions = 0; + String generic = null; + // handle array + while (className.endsWith("[]")) { + numDimensions++; + className = className.substring(0, className.length() - 2); + } + // handle generics + final int lastCharIndex = className.length() - 1; + if ('>' == className.charAt(lastCharIndex)) { + // has generic. + int open = className.indexOf('<'); + if (open == -1) { + L.e("un-matching generic syntax for %s", className); + return className; + } + generic = applyImports(className.substring(open + 1, lastCharIndex), imports); + className = className.substring(0, open); + } + int dotIndex = className.indexOf('.'); + final String qualifier; + final String rest; + if (dotIndex == -1) { + qualifier = className; + rest = null; + } else { + qualifier = className.substring(0, dotIndex); + rest = className.substring(dotIndex); // includes dot + } + final String expandedQualifier = imports.get(qualifier); + String result; + if (expandedQualifier != null) { + result = rest == null ? expandedQualifier : expandedQualifier + rest; + } else { + result = className; // no change + } + // now append back dimension and generics + if (generic != null) { + result = result + "<" + applyImports(generic, imports) + ">"; + } + while (numDimensions-- > 0) { + result = result + "[]"; + } + return result; + } + + 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 ModelClass findClass(String className, Map<String, String> imports); + + public abstract ModelClass findClass(Class classType); + + public abstract TypeUtil createTypeUtil(); + + ModelClass[] getListTypes() { + if (mListTypes == null) { + mListTypes = new ModelClass[LIST_CLASS_NAMES.length]; + for (int i = 0; i < mListTypes.length; i++) { + final ModelClass modelClass = findClass(LIST_CLASS_NAMES[i], null); + if (modelClass != null) { + mListTypes[i] = modelClass.erasure(); + } + } + } + return mListTypes; + } + + public ModelClass getMapType() { + if (mMapType == null) { + mMapType = loadClassErasure(MAP_CLASS_NAME); + } + return mMapType; + } + + ModelClass getStringType() { + if (mStringType == null) { + mStringType = findClass(STRING_CLASS_NAME, null); + } + return mStringType; + } + + ModelClass getObjectType() { + if (mObjectType == null) { + mObjectType = findClass(OBJECT_CLASS_NAME, null); + } + return mObjectType; + } + + ModelClass getObservableType() { + if (mObservableType == null) { + mObservableType = findClass(OBSERVABLE_CLASS_NAME, null); + } + return mObservableType; + } + + ModelClass getObservableListType() { + if (mObservableListType == null) { + mObservableListType = loadClassErasure(OBSERVABLE_LIST_CLASS_NAME); + } + return mObservableListType; + } + + ModelClass getObservableMapType() { + if (mObservableMapType == null) { + mObservableMapType = loadClassErasure(OBSERVABLE_MAP_CLASS_NAME); + } + return mObservableMapType; + } + + ModelClass getViewDataBindingType() { + if (mViewBindingType == null) { + mViewBindingType = findClass(VIEW_DATA_BINDING, null); + } + return mViewBindingType; + } + + ModelClass[] getObservableFieldTypes() { + if (mObservableFieldTypes == null) { + mObservableFieldTypes = new ModelClass[OBSERVABLE_FIELDS.length]; + for (int i = 0; i < OBSERVABLE_FIELDS.length; i++) { + mObservableFieldTypes[i] = loadClassErasure(OBSERVABLE_FIELDS[i]); + } + } + return mObservableFieldTypes; + } + + ModelClass getViewStubType() { + if (mViewStubType == null) { + mViewStubType = findClass(VIEW_STUB_CLASS_NAME, null); + } + return mViewStubType; + } + + private ModelClass loadClassErasure(String className) { + return findClass(className, null).erasure(); + } +} diff --git a/tools/data-binding/compiler/src/main/java/android/databinding/tool/reflection/ModelClass.java b/tools/data-binding/compiler/src/main/java/android/databinding/tool/reflection/ModelClass.java new file mode 100644 index 0000000..c55a400 --- /dev/null +++ b/tools/data-binding/compiler/src/main/java/android/databinding/tool/reflection/ModelClass.java @@ -0,0 +1,388 @@ +/* + * 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 android.databinding.tool.reflection; + +import android.databinding.tool.util.L; + +import org.apache.commons.lang3.StringUtils; + +import android.databinding.tool.reflection.ModelAnalyzer; + +import java.util.ArrayList; +import java.util.List; + +public abstract class ModelClass { + + public abstract String toJavaCode(); + + /** + * @return whether this ModelClass represents an array. + */ + public abstract boolean isArray(); + + /** + * For arrays, lists, and maps, this returns the contained value. For other types, null + * is returned. + * + * @return The component type for arrays, the value type for maps, and the element type + * for lists. + */ + public abstract ModelClass getComponentType(); + + /** + * @return Whether or not this ModelClass can be treated as a List. This means + * it is a java.util.List, or one of the Sparse*Array classes. + */ + public boolean isList() { + for (ModelClass listType : ModelAnalyzer.getInstance().getListTypes()) { + if (listType != null) { + if (listType.isAssignableFrom(this)) { + return true; + } + } + } + return false; + } + + /** + * @return whether or not this ModelClass can be considered a Map or not. + */ + public boolean isMap() { + return ModelAnalyzer.getInstance().getMapType().isAssignableFrom(erasure()); + } + + /** + * @return whether or not this ModelClass is a java.lang.String. + */ + public boolean isString() { + return ModelAnalyzer.getInstance().getStringType().equals(this); + } + + /** + * @return whether or not this ModelClass represents a Reference type. + */ + public abstract boolean isNullable(); + + /** + * @return whether or not this ModelClass represents a primitive type. + */ + public abstract boolean isPrimitive(); + + /** + * @return whether or not this ModelClass represents a Java boolean + */ + public abstract boolean isBoolean(); + + /** + * @return whether or not this ModelClass represents a Java char + */ + public abstract boolean isChar(); + + /** + * @return whether or not this ModelClass represents a Java byte + */ + public abstract boolean isByte(); + + /** + * @return whether or not this ModelClass represents a Java short + */ + public abstract boolean isShort(); + + /** + * @return whether or not this ModelClass represents a Java int + */ + public abstract boolean isInt(); + + /** + * @return whether or not this ModelClass represents a Java long + */ + public abstract boolean isLong(); + + /** + * @return whether or not this ModelClass represents a Java float + */ + public abstract boolean isFloat(); + + /** + * @return whether or not this ModelClass represents a Java double + */ + public abstract boolean isDouble(); + + /** + * @return whether or not this ModelClass is java.lang.Object and not a primitive or subclass. + */ + public boolean isObject() { + return ModelAnalyzer.getInstance().getObjectType().equals(this); + } + + /** + * @return whether or not this ModelClass type extends ViewStub. + */ + public boolean extendsViewStub() { + return ModelAnalyzer.getInstance().getViewStubType().isAssignableFrom(this); + } + + /** + * @return whether or not this is an Observable type such as ObservableMap, ObservableList, + * or Observable. + */ + public boolean isObservable() { + ModelAnalyzer modelAnalyzer = ModelAnalyzer.getInstance(); + return modelAnalyzer.getObservableType().isAssignableFrom(this) || + modelAnalyzer.getObservableListType().isAssignableFrom(this) || + modelAnalyzer.getObservableMapType().isAssignableFrom(this); + + } + + /** + * @return whether or not this is an ObservableField, or any of the primitive versions + * such as ObservableBoolean and ObservableInt + */ + public boolean isObservableField() { + ModelClass erasure = erasure(); + for (ModelClass observableField : ModelAnalyzer.getInstance().getObservableFieldTypes()) { + if (observableField.isAssignableFrom(erasure)) { + return true; + } + } + return false; + } + + /** + * @return whether or not this ModelClass represents a void + */ + public abstract boolean isVoid(); + + /** + * When this is a boxed type, such as Integer, this will return the unboxed value, + * such as int. If this is not a boxed type, this is returned. + * + * @return The unboxed type of the class that this ModelClass represents or this if it isn't a + * boxed type. + */ + public abstract ModelClass unbox(); + + /** + * When this is a primitive type, such as boolean, this will return the boxed value, + * such as Boolean. If this is not a primitive type, this is returned. + * + * @return The boxed type of the class that this ModelClass represents or this if it isn't a + * primitive type. + */ + public abstract ModelClass box(); + + /** + * Returns whether or not the type associated with <code>that</code> can be assigned to + * the type associated with this ModelClass. If this and that only require boxing or unboxing + * then true is returned. + * + * @param that the ModelClass to compare. + * @return true if <code>that</code> requires only boxing or if <code>that</code> is an + * implementation of or subclass of <code>this</code>. + */ + public abstract boolean isAssignableFrom(ModelClass that); + + /** + * Returns an array containing all public methods on the type represented by this ModelClass + * with the name <code>name</code> and can take the passed-in types as arguments. This will + * also work if the arguments match VarArgs parameter. + * + * @param name The name of the method to find. + * @param args The types that the method should accept. + * @param isStatic Whether only static methods should be returned or instance methods. + * @return An array containing all public methods with the name <code>name</code> and taking + * <code>args</code> parameters. + */ + public ModelMethod[] getMethods(String name, List<ModelClass> args, boolean isStatic) { + ModelMethod[] methods = getDeclaredMethods(); + ArrayList<ModelMethod> matching = new ArrayList<ModelMethod>(); + for (ModelMethod method : methods) { + if (method.isPublic() && method.isStatic() == isStatic && + name.equals(method.getName()) && method.acceptsArguments(args)) { + matching.add(method); + } + } + return matching.toArray(new ModelMethod[matching.size()]); + } + + /** + * Returns all public instance methods with the given name and number of parameters. + * + * @param name The name of the method to find. + * @param numParameters The number of parameters that the method should take + * @return An array containing all public methods with the given name and number of parameters. + */ + public ModelMethod[] getMethods(String name, int numParameters) { + ModelMethod[] methods = getDeclaredMethods(); + ArrayList<ModelMethod> matching = new ArrayList<ModelMethod>(); + for (ModelMethod method : methods) { + if (method.isPublic() && !method.isStatic() && + name.equals(method.getName()) && + method.getParameterTypes().length == numParameters) { + matching.add(method); + } + } + return matching.toArray(new ModelMethod[matching.size()]); + } + + /** + * Returns the public method with the name <code>name</code> with the parameters that + * best match args. <code>staticAccess</code> governs whether a static or instance method + * will be returned. If no matching method was found, null is returned. + * + * @param name The method name to find + * @param args The arguments that the method should accept + * @param staticAccess true if the returned method should be static or false if it should + * be an instance method. + */ + public ModelMethod getMethod(String name, List<ModelClass> args, boolean staticAccess) { + ModelMethod[] methods = getMethods(name, args, staticAccess); + if (methods.length == 0) { + return null; + } + ModelMethod bestMethod = methods[0]; + for (int i = 1; i < methods.length; i++) { + if (methods[i].isBetterArgMatchThan(bestMethod, args)) { + bestMethod = methods[i]; + } + } + return bestMethod; + } + + /** + * If this represents a class, the super class that it extends is returned. If this + * represents an interface, the interface that this extends is returned. + * <code>null</code> is returned if this is not a class or interface, such as an int, or + * if it is java.lang.Object or an interface that does not extend any other type. + * + * @return The class or interface that this ModelClass extends or null. + */ + public abstract ModelClass getSuperclass(); + + /** + * @return A String representation of the class or interface that this represents, not + * including any type arguments. + */ + public String getCanonicalName() { + return erasure().toJavaCode(); + } + + /** + * Returns this class type without any generic type arguments. + * @return this class type without any generic type arguments. + */ + public abstract ModelClass erasure(); + + /** + * Since when this class is available. Important for Binding expressions so that we don't + * call non-existing APIs when setting UI. + * + * @return The SDK_INT where this method was added. If it is not a framework method, should + * return 1. + */ + public int getMinApi() { + return SdkUtil.getMinApi(this); + } + + /** + * Returns the JNI description of the method which can be used to lookup it in SDK. + * @see TypeUtil + */ + public abstract String getJniDescription(); + + /** + * Returns the getter method or field that the name refers to. + * @param name The name of the field or the body of the method name -- can be name(), + * getName(), or isName(). + * @param staticAccess Whether this should look for static methods and fields or instance + * versions + * @return the getter method or field that the name refers to. + * @throws IllegalArgumentException if there is no such method or field available. + */ + public Callable findGetterOrField(String name, boolean staticAccess) { + String capitalized = StringUtils.capitalize(name); + String[] methodNames = { + "get" + capitalized, + "is" + capitalized, + name + }; + final ModelField backingField = getField(name, true, staticAccess); + L.d("Finding getter or field for %s, field = %s", name, backingField == null ? null : backingField.getName()); + for (String methodName : methodNames) { + ModelMethod[] methods = getMethods(methodName, 0); + for (ModelMethod method : methods) { + if (method.isPublic() && method.isStatic() == staticAccess) { + final Callable result = new Callable(Callable.Type.METHOD, methodName, + method.getReturnType(null), true, method.isBindable() || + (backingField != null && backingField.isBindable())); + L.d("backing field for %s is %s", result, backingField); + return result; + } + } + } + + if (backingField != null && backingField.isPublic()) { + ModelClass fieldType = backingField.getFieldType(); + return new Callable(Callable.Type.FIELD, name, fieldType, + !backingField.isFinal() || fieldType.isObservable(), backingField.isBindable()); + } + throw new IllegalArgumentException( + "cannot find " + name + " in " + toJavaCode()); + + } + + public ModelField getField(String name, boolean allowPrivate, boolean staticAccess) { + ModelField[] fields = getDeclaredFields(); + for (ModelField field : fields) { + if (name.equals(stripFieldName(field.getName())) && field.isStatic() == staticAccess && + (allowPrivate || !field.isPublic())) { + return field; + } + } + return null; + } + + protected abstract ModelField[] getDeclaredFields(); + + protected abstract ModelMethod[] getDeclaredMethods(); + + private static String stripFieldName(String fieldName) { + // TODO: Make this configurable through IntelliJ + 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; + } +} diff --git a/tools/data-binding/compiler/src/main/java/android/databinding/tool/reflection/ModelField.java b/tools/data-binding/compiler/src/main/java/android/databinding/tool/reflection/ModelField.java new file mode 100644 index 0000000..0cde85b --- /dev/null +++ b/tools/data-binding/compiler/src/main/java/android/databinding/tool/reflection/ModelField.java @@ -0,0 +1,49 @@ +/* + * 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 android.databinding.tool.reflection; + +public abstract class ModelField { + + /** + * @return Whether this field has been annotated with Bindable. + */ + public abstract boolean isBindable(); + + /** + * @return The field name. + */ + public abstract String getName(); + + /** + * @return true if this field is marked public. + */ + public abstract boolean isPublic(); + + /** + * @return true if this is a static field. + */ + public abstract boolean isStatic(); + + /** + * @return true if the field was declared final. + */ + public abstract boolean isFinal(); + + /** + * @return The declared type of the field variable. + */ + public abstract ModelClass getFieldType(); +} diff --git a/tools/data-binding/compiler/src/main/java/android/databinding/tool/reflection/ModelMethod.java b/tools/data-binding/compiler/src/main/java/android/databinding/tool/reflection/ModelMethod.java new file mode 100644 index 0000000..b4ee671 --- /dev/null +++ b/tools/data-binding/compiler/src/main/java/android/databinding/tool/reflection/ModelMethod.java @@ -0,0 +1,182 @@ +/* + * 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 android.databinding.tool.reflection; + +import android.databinding.Bindable; + +import java.util.List; + +public abstract class ModelMethod { + public abstract ModelClass getDeclaringClass(); + + public abstract ModelClass[] getParameterTypes(); + + public abstract String getName(); + + public abstract ModelClass getReturnType(List<ModelClass> args); + + public abstract boolean isVoid(); + + public abstract boolean isPublic(); + + public abstract boolean isStatic(); + + /** + * @return whether or not this method has been given the {@link Bindable} annotation. + */ + public abstract boolean isBindable(); + + /** + * Since when this method is available. Important for Binding expressions so that we don't + * call non-existing APIs when setting UI. + * + * @return The SDK_INT where this method was added. If it is not a framework method, should + * return 1. + */ + public abstract int getMinApi(); + + /** + * Returns the JNI description of the method which can be used to lookup it in SDK. + * @see TypeUtil + */ + public abstract String getJniDescription(); + + /** + * @return true if the final parameter is a varargs parameter. + */ + public abstract boolean isVarArgs(); + + /** + * @param args The arguments to the method + * @return Whether the arguments would be accepted as parameters to this method. + */ + public boolean acceptsArguments(List<ModelClass> args) { + boolean isVarArgs = isVarArgs(); + ModelClass[] parameterTypes = getParameterTypes(); + if ((!isVarArgs && args.size() != parameterTypes.length) || + (isVarArgs && args.size() < parameterTypes.length - 1)) { + return false; // The wrong number of parameters + } + boolean parametersMatch = true; + for (int i = 0; i < args.size(); i++) { + ModelClass parameterType = getParameter(i, parameterTypes); + ModelClass arg = args.get(i); + if (!parameterType.isAssignableFrom(arg) && !isImplicitConversion(arg, parameterType)) { + parametersMatch = false; + break; + } + } + return parametersMatch; + } + + public boolean isBetterArgMatchThan(ModelMethod other, List<ModelClass> args) { + final ModelClass[] parameterTypes = getParameterTypes(); + final ModelClass[] otherParameterTypes = other.getParameterTypes(); + for (int i = 0; i < args.size(); i++) { + final ModelClass arg = args.get(i); + final ModelClass thisParameter = getParameter(i, parameterTypes); + final ModelClass thatParameter = other.getParameter(i, otherParameterTypes); + final int diff = compareParameter(arg, thisParameter, thatParameter); + if (diff != 0) { + return diff < 0; + } + } + return false; + } + + private ModelClass getParameter(int index, ModelClass[] parameterTypes) { + int normalParamCount = isVarArgs() ? parameterTypes.length - 1 : parameterTypes.length; + if (index < normalParamCount) { + return parameterTypes[index]; + } else { + return parameterTypes[parameterTypes.length - 1].getComponentType(); + } + } + + private static int compareParameter(ModelClass arg, ModelClass thisParameter, + ModelClass thatParameter) { + if (thatParameter.equals(arg)) { + return 1; + } else if (thisParameter.equals(arg)) { + return -1; + } else if (isBoxingConversion(thatParameter, arg)) { + return 1; + } else if (isBoxingConversion(thisParameter, arg)) { + // Boxing/unboxing is second best + return -1; + } else { + int argConversionLevel = getImplicitConversionLevel(arg); + if (argConversionLevel != -1) { + int oldConversionLevel = getImplicitConversionLevel(thatParameter); + int newConversionLevel = getImplicitConversionLevel(thisParameter); + if (newConversionLevel != -1 && + (oldConversionLevel == -1 || newConversionLevel < oldConversionLevel)) { + return -1; + } else if (oldConversionLevel != -1) { + return 1; + } + } + // Look for more exact match + if (thatParameter.isAssignableFrom(thisParameter)) { + return -1; + } + } + return 0; // no difference + } + + public static boolean isBoxingConversion(ModelClass class1, ModelClass class2) { + if (class1.isPrimitive() != class2.isPrimitive()) { + return (class1.box().equals(class2.box())); + } else { + return false; + } + } + + public static int getImplicitConversionLevel(ModelClass primitive) { + if (primitive == null) { + return -1; + } else if (primitive.isByte()) { + return 0; + } else if (primitive.isChar()) { + return 1; + } else if (primitive.isShort()) { + return 2; + } else if (primitive.isInt()) { + return 3; + } else if (primitive.isLong()) { + return 4; + } else if (primitive.isFloat()) { + return 5; + } else if (primitive.isDouble()) { + return 6; + } else { + return -1; + } + } + + public 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; + } + int fromConversionLevel = getImplicitConversionLevel(from); + int toConversionLevel = getImplicitConversionLevel(to); + return fromConversionLevel < toConversionLevel; + } else { + return false; + } + } +} diff --git a/tools/data-binding/compiler/src/main/java/android/databinding/tool/reflection/SdkUtil.java b/tools/data-binding/compiler/src/main/java/android/databinding/tool/reflection/SdkUtil.java new file mode 100644 index 0000000..177935a --- /dev/null +++ b/tools/data-binding/compiler/src/main/java/android/databinding/tool/reflection/SdkUtil.java @@ -0,0 +1,159 @@ +/* + * 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 android.databinding.tool.reflection; + +import com.google.common.base.Preconditions; + +import org.w3c.dom.Document; +import org.w3c.dom.Node; +import org.w3c.dom.NodeList; + +import android.databinding.tool.util.L; + +import java.io.File; +import java.util.HashMap; +import java.util.Map; + +import javax.xml.parsers.DocumentBuilder; +import javax.xml.parsers.DocumentBuilderFactory; +import javax.xml.xpath.XPath; +import javax.xml.xpath.XPathExpressionException; +import javax.xml.xpath.XPathFactory; + +/** + * Class that is used for SDK related stuff. + * <p> + * Must be initialized with the sdk location to work properly + */ +public class SdkUtil { + + static File mSdkPath; + + static ApiChecker mApiChecker; + + static int mMinSdk; + + public static void initialize(int minSdk, File sdkPath) { + mSdkPath = sdkPath; + mMinSdk = minSdk; + mApiChecker = new ApiChecker(new File(sdkPath.getAbsolutePath() + + "/platform-tools/api/api-versions.xml")); + L.d("SdkUtil init, minSdk: %s", minSdk); + } + + public static int getMinApi(ModelClass modelClass) { + return mApiChecker.getMinApi(modelClass.getJniDescription(), null); + } + + public static int getMinApi(ModelMethod modelMethod) { + ModelClass declaringClass = modelMethod.getDeclaringClass(); + Preconditions.checkNotNull(mApiChecker, "should've initialized api checker"); + while (declaringClass != null) { + String classDesc = declaringClass.getJniDescription(); + String methodDesc = modelMethod.getJniDescription(); + int result = mApiChecker.getMinApi(classDesc, methodDesc); + L.d("checking method api for %s, class:%s method:%s. result: %d", modelMethod.getName(), + classDesc, methodDesc, result); + if (result > 1) { + return result; + } + declaringClass = declaringClass.getSuperclass(); + } + return 1; + } + + private static class ApiChecker { + + private Map<String, Integer> mFullLookup = new HashMap<String, Integer>(); + + private Document mDoc; + + private XPath mXPath; + + public ApiChecker(File apiFile) { + try { + DocumentBuilderFactory factory = DocumentBuilderFactory.newInstance(); + DocumentBuilder builder = factory.newDocumentBuilder(); + mDoc = builder.parse(apiFile); + XPathFactory xPathFactory = XPathFactory.newInstance(); + mXPath = xPathFactory.newXPath(); + buildFullLookup(); + } catch (Throwable t) { + L.e(t, "cannot load api descriptions from %s", apiFile); + } + } + + private void buildFullLookup() throws XPathExpressionException { + NodeList allClasses = mDoc.getChildNodes().item(0).getChildNodes(); + for (int j = 0; j < allClasses.getLength(); j++) { + Node node = allClasses.item(j); + if (node.getNodeType() != Node.ELEMENT_NODE || !"class" + .equals(node.getNodeName())) { + continue; + } + //L.d("checking node %s", node.getAttributes().getNamedItem("name").getNodeValue()); + int classSince = getSince(node); + String classDesc = node.getAttributes().getNamedItem("name").getNodeValue(); + + final NodeList childNodes = node.getChildNodes(); + for (int i = 0; i < childNodes.getLength(); i++) { + Node child = childNodes.item(i); + if (child.getNodeType() != Node.ELEMENT_NODE || !"method" + .equals(child.getNodeName())) { + continue; + } + int methodSince = getSince(child); + int since = Math.max(classSince, methodSince); + if (since > SdkUtil.mMinSdk) { + String methodDesc = child.getAttributes().getNamedItem("name") + .getNodeValue(); + String key = cacheKey(classDesc, methodDesc); + mFullLookup.put(key, since); + } + } + } + } + + public int getMinApi(String classDesc, String methodOrFieldDesc) { + if (mDoc == null || mXPath == null) { + return 1; + } + if (classDesc == null || classDesc.isEmpty()) { + return 1; + } + final String key = cacheKey(classDesc, methodOrFieldDesc); + Integer since = mFullLookup.get(key); + return since == null ? 1 : since; + } + + private static String cacheKey(String classDesc, String methodOrFieldDesc) { + return classDesc + "~" + methodOrFieldDesc; + } + + private static int getSince(Node node) { + final Node since = node.getAttributes().getNamedItem("since"); + if (since != null) { + final String nodeValue = since.getNodeValue(); + if (nodeValue != null && !nodeValue.isEmpty()) { + try { + return Integer.parseInt(nodeValue); + } catch (Throwable t) { + } + } + } + + return 1; + } + } +} diff --git a/tools/data-binding/compiler/src/main/java/android/databinding/tool/reflection/TypeUtil.java b/tools/data-binding/compiler/src/main/java/android/databinding/tool/reflection/TypeUtil.java new file mode 100644 index 0000000..f396bd7 --- /dev/null +++ b/tools/data-binding/compiler/src/main/java/android/databinding/tool/reflection/TypeUtil.java @@ -0,0 +1,57 @@ +/* + * 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 android.databinding.tool.reflection; + +public abstract class TypeUtil { + + public static final String BYTE = "B"; + + public static final String CHAR = "C"; + + public static final String DOUBLE = "D"; + + public static final String FLOAT = "F"; + + public static final String INT = "I"; + + public static final String LONG = "J"; + + public static final String SHORT = "S"; + + public static final String VOID = "V"; + + public static final String BOOLEAN = "Z"; + + public static final String ARRAY = "["; + + public static final String CLASS_PREFIX = "L"; + + public static final String CLASS_SUFFIX = ";"; + + private static TypeUtil sInstance; + + abstract public String getDescription(ModelClass modelClass); + + abstract public String getDescription(ModelMethod modelMethod); + + public static TypeUtil getInstance() { + if (sInstance == null) { + sInstance = ModelAnalyzer.getInstance().createTypeUtil(); + } + return sInstance; + } +} diff --git a/tools/data-binding/compiler/src/main/java/android/databinding/tool/reflection/annotation/AnnotationAnalyzer.java b/tools/data-binding/compiler/src/main/java/android/databinding/tool/reflection/annotation/AnnotationAnalyzer.java new file mode 100644 index 0000000..d01f579 --- /dev/null +++ b/tools/data-binding/compiler/src/main/java/android/databinding/tool/reflection/annotation/AnnotationAnalyzer.java @@ -0,0 +1,200 @@ +/* + * 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 android.databinding.tool.reflection.annotation; + +import com.google.common.collect.ImmutableMap; + +import android.databinding.tool.reflection.ModelAnalyzer; +import android.databinding.tool.reflection.ModelClass; +import android.databinding.tool.reflection.TypeUtil; +import android.databinding.tool.util.L; + +import java.util.ArrayList; +import java.util.Map; + +import javax.annotation.processing.ProcessingEnvironment; +import javax.lang.model.element.TypeElement; +import javax.lang.model.type.DeclaredType; +import javax.lang.model.type.TypeKind; +import javax.lang.model.type.TypeMirror; +import javax.lang.model.util.Elements; +import javax.lang.model.util.Types; + +public class AnnotationAnalyzer extends ModelAnalyzer { + + public 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 mProcessingEnv; + + public AnnotationAnalyzer(ProcessingEnvironment processingEnvironment) { + mProcessingEnv = processingEnvironment; + setInstance(this); + } + + public static AnnotationAnalyzer get() { + return (AnnotationAnalyzer) getInstance(); + } + + @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)); + } + } + + @Override + public AnnotationClass findClass(String className, Map<String, String> imports) { + 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) { + TypeElement typeElement = getTypeElement(className, imports); + if (typeElement == null) { + return null; + } + declaredType = (DeclaredType) typeElement.asType(); + } else { + int templateCloseIndex = className.lastIndexOf('>'); + String paramStr = className.substring(templateOpenIndex + 1, templateCloseIndex); + + String baseClassName = className.substring(0, templateOpenIndex); + TypeElement typeElement = getTypeElement(baseClassName, imports); + if (typeElement == null) { + L.e("cannot find type element for %s", baseClassName); + return null; + } + + 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), imports).mTypeMirror; + if (typeArgs[i] == null) { + L.e("cannot find type argument for %s in %s", templateParameters.get(i), + baseClassName); + return null; + } + } + 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 TypeElement getTypeElement(String className, Map<String, String> imports) { + Elements elementUtils = getElementUtils(); + if (className.indexOf('.') < 0 && imports != null) { + // try the imports + String importedClass = imports.get(className); + if (importedClass != null) { + className = importedClass; + } + } + if (className.indexOf('.') < 0) { + // try java.lang. + String javaLangClass = "java.lang." + className; + try { + TypeElement javaLang = elementUtils.getTypeElement(javaLangClass); + if (javaLang != null) { + return javaLang; + } + } catch (Exception e) { + // try the normal way + } + } + try { + return elementUtils.getTypeElement(className); + } catch (Exception e) { + return null; + } + } + + private ArrayList<String> splitTemplateParameters(String templateParameters) { + ArrayList<String> list = new ArrayList<String>(); + 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--; + } + } + index++; + } + list.add(arg.toString()); + return list; + } + + @Override + public ModelClass findClass(Class classType) { + return findClass(classType.getCanonicalName(), null); + } + + public Types getTypeUtils() { + return mProcessingEnv.getTypeUtils(); + } + + public Elements getElementUtils() { + return mProcessingEnv.getElementUtils(); + } + + public ProcessingEnvironment getProcessingEnv() { + return mProcessingEnv; + } + + @Override + public TypeUtil createTypeUtil() { + return new AnnotationTypeUtil(this); + } +} diff --git a/tools/data-binding/compiler/src/main/java/android/databinding/tool/reflection/annotation/AnnotationClass.java b/tools/data-binding/compiler/src/main/java/android/databinding/tool/reflection/annotation/AnnotationClass.java new file mode 100644 index 0000000..a1bec3c --- /dev/null +++ b/tools/data-binding/compiler/src/main/java/android/databinding/tool/reflection/annotation/AnnotationClass.java @@ -0,0 +1,323 @@ +/* + * 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 android.databinding.tool.reflection.annotation; + +import android.databinding.tool.reflection.ModelAnalyzer; +import android.databinding.tool.reflection.ModelClass; +import android.databinding.tool.reflection.ModelField; +import android.databinding.tool.reflection.ModelMethod; +import android.databinding.tool.reflection.TypeUtil; +import android.databinding.tool.util.L; + +import java.util.ArrayList; +import java.util.List; + +import javax.lang.model.element.Element; +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; + +/** + * This is the implementation of ModelClass for the annotation + * processor. It relies on AnnotationAnalyzer. + */ +class AnnotationClass extends ModelClass { + + final TypeMirror mTypeMirror; + + public AnnotationClass(TypeMirror typeMirror) { + mTypeMirror = typeMirror; + } + + @Override + public String toJavaCode() { + return mTypeMirror.toString(); + } + + @Override + public boolean isArray() { + return mTypeMirror.getKind() == TypeKind.ARRAY; + } + + @Override + public AnnotationClass getComponentType() { + TypeMirror component = null; + if (isArray()) { + component = ((ArrayType) mTypeMirror).getComponentType(); + } else if (isList()) { + for (ModelMethod method : getMethods("get", 1)) { + ModelClass parameter = method.getParameterTypes()[0]; + if (parameter.isInt() || parameter.isLong()) { + ArrayList<ModelClass> parameters = new ArrayList<ModelClass>(1); + parameters.add(parameter); + return (AnnotationClass) method.getReturnType(parameters); + } + } + // no "get" call found! + return null; + } else { + AnnotationClass mapClass = (AnnotationClass) ModelAnalyzer.getInstance().getMapType(); + DeclaredType mapType = findInterface(mapClass.mTypeMirror); + 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 { + ArrayList<TypeMirror> toCheck = new ArrayList<TypeMirror>(); + toCheck.add(mTypeMirror); + while (!toCheck.isEmpty()) { + TypeMirror typeMirror = toCheck.remove(0); + if (typeUtil.isSameType(interfaceType, typeUtil.erasure(typeMirror))) { + foundInterface = typeMirror; + break; + } else { + toCheck.addAll(typeUtil.directSupertypes(typeMirror)); + } + } + if (foundInterface == null) { + L.e("Detected " + interfaceType + " type for " + mTypeMirror + + ", but not able to find the implemented interface."); + return null; + } + } + if (foundInterface.getKind() != TypeKind.DECLARED) { + L.e("Found " + interfaceType + " type for " + mTypeMirror + + ", but it isn't a declared type: " + foundInterface); + return null; + } + return (DeclaredType) foundInterface; + } + + @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 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 AnnotationClass box() { + if (!isPrimitive()) { + return this; + } + return new AnnotationClass(getTypeUtils().boxedClass((PrimitiveType) mTypeMirror).asType()); + } + + @Override + public boolean isAssignableFrom(ModelClass that) { + if (that == null) { + return false; + } + AnnotationClass thatAnnotationClass = (AnnotationClass) that; + return getTypeUtils().isAssignable(thatAnnotationClass.mTypeMirror, this.mTypeMirror); + } + + @Override + public ModelMethod[] getDeclaredMethods() { + final ModelMethod[] declaredMethods; + if (mTypeMirror.getKind() == TypeKind.DECLARED) { + DeclaredType declaredType = (DeclaredType) mTypeMirror; + Elements elementUtils = getElementUtils(); + TypeElement typeElement = (TypeElement) declaredType.asElement(); + List<? extends Element> members = elementUtils.getAllMembers(typeElement); + List<ExecutableElement> methods = ElementFilter.methodsIn(members); + declaredMethods = new ModelMethod[methods.size()]; + for (int i = 0; i < declaredMethods.length; i++) { + declaredMethods[i] = new AnnotationMethod(declaredType, methods.get(i)); + } + } else { + declaredMethods = new ModelMethod[0]; + } + return declaredMethods; + } + + @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 String getCanonicalName() { + return getTypeUtils().erasure(mTypeMirror).toString(); + } + + @Override + public ModelClass erasure() { + final TypeMirror erasure = getTypeUtils().erasure(mTypeMirror); + if (erasure == mTypeMirror) { + return this; + } else { + return new AnnotationClass(erasure); + } + } + + @Override + public String getJniDescription() { + return TypeUtil.getInstance().getDescription(this); + } + + @Override + protected ModelField[] getDeclaredFields() { + final ModelField[] declaredFields; + if (mTypeMirror.getKind() == TypeKind.DECLARED) { + DeclaredType declaredType = (DeclaredType) mTypeMirror; + Elements elementUtils = getElementUtils(); + TypeElement typeElement = (TypeElement) declaredType.asElement(); + List<? extends Element> members = elementUtils.getAllMembers(typeElement); + List<VariableElement> fields = ElementFilter.fieldsIn(members); + declaredFields = new ModelField[fields.size()]; + for (int i = 0; i < declaredFields.length; i++) { + declaredFields[i] = new AnnotationField(typeElement, fields.get(i)); + } + } else { + declaredFields = new ModelField[0]; + } + return declaredFields; + } + + @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.get().mProcessingEnv.getTypeUtils(); + } + + private static Elements getElementUtils() { + return AnnotationAnalyzer.get().mProcessingEnv.getElementUtils(); + } + + @Override + public String toString() { + return mTypeMirror.toString(); + } +} diff --git a/tools/data-binding/compiler/src/main/java/android/databinding/tool/reflection/annotation/AnnotationField.java b/tools/data-binding/compiler/src/main/java/android/databinding/tool/reflection/annotation/AnnotationField.java new file mode 100644 index 0000000..9373c6c --- /dev/null +++ b/tools/data-binding/compiler/src/main/java/android/databinding/tool/reflection/annotation/AnnotationField.java @@ -0,0 +1,82 @@ +/* + * 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 android.databinding.tool.reflection.annotation; + +import android.databinding.Bindable; +import android.databinding.tool.reflection.ModelClass; +import android.databinding.tool.reflection.ModelField; + +import javax.lang.model.element.Modifier; +import javax.lang.model.element.TypeElement; +import javax.lang.model.element.VariableElement; + +class AnnotationField extends 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 isBindable() { + return mField.getAnnotation(Bindable.class) != null; + } + + @Override + public String getName() { + return mField.getSimpleName().toString(); + } + + @Override + public boolean isPublic() { + return mField.getModifiers().contains(Modifier.PUBLIC); + } + + @Override + public boolean isStatic() { + return mField.getModifiers().contains(Modifier.STATIC); + } + + @Override + public boolean isFinal() { + return mField.getModifiers().contains(Modifier.FINAL); + } + + @Override + public ModelClass getFieldType() { + return new AnnotationClass(mField.asType()); + } + + @Override + public boolean equals(Object obj) { + if (obj instanceof AnnotationField) { + AnnotationField that = (AnnotationField) obj; + return mDeclaredClass.equals(that.mDeclaredClass) && AnnotationAnalyzer.get() + .getTypeUtils().isSameType(mField.asType(), that.mField.asType()); + } else { + return false; + } + } +} diff --git a/tools/data-binding/compiler/src/main/java/android/databinding/tool/reflection/annotation/AnnotationMethod.java b/tools/data-binding/compiler/src/main/java/android/databinding/tool/reflection/annotation/AnnotationMethod.java new file mode 100644 index 0000000..00e1a4e --- /dev/null +++ b/tools/data-binding/compiler/src/main/java/android/databinding/tool/reflection/annotation/AnnotationMethod.java @@ -0,0 +1,122 @@ +/* + * 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 android.databinding.tool.reflection.annotation; + +import android.databinding.Bindable; +import android.databinding.tool.reflection.ModelClass; +import android.databinding.tool.reflection.ModelMethod; +import android.databinding.tool.reflection.SdkUtil; +import android.databinding.tool.reflection.TypeUtil; + +import java.util.List; + +import javax.lang.model.element.ExecutableElement; +import javax.lang.model.element.Modifier; +import javax.lang.model.type.DeclaredType; +import javax.lang.model.type.ExecutableType; +import javax.lang.model.type.TypeKind; +import javax.lang.model.type.TypeMirror; +import javax.lang.model.util.Types; + +class AnnotationMethod extends ModelMethod { + final ExecutableType mMethod; + final DeclaredType mDeclaringType; + final ExecutableElement mExecutableElement; + int mApiLevel = -1; // calculated on demand + + public AnnotationMethod(DeclaredType declaringType, ExecutableElement executableElement) { + mDeclaringType = declaringType; + mExecutableElement = executableElement; + Types typeUtils = AnnotationAnalyzer.get().getTypeUtils(); + mMethod = (ExecutableType) typeUtils.asMemberOf(declaringType, executableElement); + } + + @Override + public ModelClass getDeclaringClass() { + return new AnnotationClass(mDeclaringType); + } + + @Override + public ModelClass[] getParameterTypes() { + List<? extends TypeMirror> parameters = mMethod.getParameterTypes(); + ModelClass[] parameterTypes = new ModelClass[parameters.size()]; + for (int i = 0; i < parameters.size(); i++) { + parameterTypes[i] = new AnnotationClass(parameters.get(i)); + } + return parameterTypes; + } + + @Override + public String getName() { + return mExecutableElement.getSimpleName().toString(); + } + + @Override + public ModelClass getReturnType(List<ModelClass> args) { + TypeMirror returnType = mMethod.getReturnType(); + // TODO: support argument-supplied types + // for example: public T[] toArray(T[] arr) + return new AnnotationClass(returnType); + } + + @Override + public boolean isVoid() { + return mMethod.getReturnType().getKind() == TypeKind.VOID; + } + + @Override + public boolean isPublic() { + return mExecutableElement.getModifiers().contains(Modifier.PUBLIC); + } + + @Override + public boolean isStatic() { + return mExecutableElement.getModifiers().contains(Modifier.STATIC); + } + + @Override + public boolean isBindable() { + return mExecutableElement.getAnnotation(Bindable.class) != null; + } + + @Override + public int getMinApi() { + if (mApiLevel == -1) { + mApiLevel = SdkUtil.getMinApi(this); + } + return mApiLevel; + } + + @Override + public String getJniDescription() { + return TypeUtil.getInstance().getDescription(this); + } + + @Override + public boolean isVarArgs() { + return mExecutableElement.isVarArgs(); + } + + @Override + public String toString() { + return "AnnotationMethod{" + + "mMethod=" + mMethod + + ", mDeclaringType=" + mDeclaringType + + ", mExecutableElement=" + mExecutableElement + + ", mApiLevel=" + mApiLevel + + '}'; + } +} diff --git a/tools/data-binding/compiler/src/main/java/android/databinding/tool/reflection/annotation/AnnotationTypeUtil.java b/tools/data-binding/compiler/src/main/java/android/databinding/tool/reflection/annotation/AnnotationTypeUtil.java new file mode 100644 index 0000000..ebc6a07 --- /dev/null +++ b/tools/data-binding/compiler/src/main/java/android/databinding/tool/reflection/annotation/AnnotationTypeUtil.java @@ -0,0 +1,105 @@ +/* + * 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 android.databinding.tool.reflection.annotation; + +import android.databinding.tool.reflection.ModelClass; +import android.databinding.tool.reflection.ModelMethod; +import android.databinding.tool.reflection.TypeUtil; + +import java.util.List; + +import javax.lang.model.type.ArrayType; +import javax.lang.model.type.ExecutableType; +import javax.lang.model.type.TypeMirror; +import javax.lang.model.type.TypeVariable; + +public class AnnotationTypeUtil extends TypeUtil { + javax.lang.model.util.Types mTypes; + + public AnnotationTypeUtil( + AnnotationAnalyzer annotationAnalyzer) { + mTypes = annotationAnalyzer.getTypeUtils(); + } + + @Override + public String getDescription(ModelClass modelClass) { + // TODO use interface + return modelClass.getCanonicalName().replace('.', '/'); + } + + @Override + public String getDescription(ModelMethod modelMethod) { + // TODO use interface + return modelMethod.getName() + getDescription( + ((AnnotationMethod) modelMethod).mExecutableElement.asType()); + } + + private String getDescription(TypeMirror typeMirror) { + if (typeMirror == null) { + throw new UnsupportedOperationException(); + } + switch (typeMirror.getKind()) { + case BOOLEAN: + return BOOLEAN; + case BYTE: + return BYTE; + case SHORT: + return SHORT; + case INT: + return INT; + case LONG: + return LONG; + case CHAR: + return CHAR; + case FLOAT: + return FLOAT; + case DOUBLE: + return DOUBLE; + case DECLARED: + return CLASS_PREFIX + mTypes.erasure(typeMirror).toString().replace('.', '/') + CLASS_SUFFIX; + case VOID: + return VOID; + case ARRAY: + final ArrayType arrayType = (ArrayType) typeMirror; + final String componentType = getDescription(arrayType.getComponentType()); + return ARRAY + componentType; + case TYPEVAR: + final TypeVariable typeVariable = (TypeVariable) typeMirror; + final String name = typeVariable.toString(); + return CLASS_PREFIX + name.replace('.', '/') + CLASS_SUFFIX; + case EXECUTABLE: + final ExecutableType executableType = (ExecutableType) typeMirror; + final int argStart = mTypes.erasure(executableType).toString().indexOf('('); + final String methodName = executableType.toString().substring(0, argStart); + final String args = joinArgs(executableType.getParameterTypes()); + // TODO detect constructor? + return methodName + "(" + args + ")" + getDescription( + executableType.getReturnType()); + default: + throw new UnsupportedOperationException("cannot understand type " + + typeMirror.toString() + ", kind:" + typeMirror.getKind().name()); + } + } + + private String joinArgs(List<? extends TypeMirror> mirrorList) { + StringBuilder result = new StringBuilder(); + for (TypeMirror mirror : mirrorList) { + result.append(getDescription(mirror)); + } + return result.toString(); + } +} diff --git a/tools/data-binding/compiler/src/main/java/android/databinding/tool/store/LayoutFileParser.java b/tools/data-binding/compiler/src/main/java/android/databinding/tool/store/LayoutFileParser.java new file mode 100644 index 0000000..5909922 --- /dev/null +++ b/tools/data-binding/compiler/src/main/java/android/databinding/tool/store/LayoutFileParser.java @@ -0,0 +1,276 @@ +/* + * 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 android.databinding.tool.store; + +import com.google.common.base.Preconditions; + +import org.apache.commons.io.FileUtils; +import org.apache.commons.lang3.ObjectUtils; +import org.apache.commons.lang3.StringUtils; +import org.w3c.dom.Document; +import org.w3c.dom.NamedNodeMap; +import org.w3c.dom.Node; +import org.w3c.dom.NodeList; +import org.xml.sax.SAXException; + +import android.databinding.tool.util.L; +import android.databinding.tool.util.ParserHelper; +import android.databinding.tool.util.XmlEditor; + +import java.io.File; +import java.io.IOException; +import java.util.ArrayList; +import java.util.List; + +import javax.xml.parsers.DocumentBuilder; +import javax.xml.parsers.DocumentBuilderFactory; +import javax.xml.parsers.ParserConfigurationException; +import javax.xml.xpath.XPath; +import javax.xml.xpath.XPathConstants; +import javax.xml.xpath.XPathExpression; +import javax.xml.xpath.XPathExpressionException; +import javax.xml.xpath.XPathFactory; + +/** + * Gets the list of XML files and creates a list of + * {@link android.databinding.tool.store.ResourceBundle} that can be persistent or converted to + * LayoutBinder. + */ +public class LayoutFileParser { + private static final String XPATH_VARIABLE_DEFINITIONS = "//variable"; + private static final String XPATH_BINDING_ELEMENTS = "//*[@*[starts-with(., '@{') and substring(., string-length(.)) = '}']]"; + private static final String XPATH_ID_ELEMENTS = "//*[@*[local-name()='id']]"; + private static final String XPATH_IMPORT_DEFINITIONS = "//import"; + private static final String XPATH_MERGE_TAG = "/merge"; + final String LAYOUT_PREFIX = "@layout/"; + + public ResourceBundle.LayoutFileBundle parseXml(File xml, String pkg) + throws ParserConfigurationException, IOException, SAXException, + XPathExpressionException { + final String xmlNoExtension = ParserHelper.INSTANCE$.stripExtension(xml.getName()); + final String newTag = xml.getParentFile().getName() + '/' + xmlNoExtension; + File original = stripFileAndGetOriginal(xml, newTag); + if (original == null) { + L.d("assuming the file is the original for %s", xml.getAbsoluteFile()); + original = xml; + } + L.d("parsing file %s", xml.getAbsolutePath()); + + ResourceBundle.LayoutFileBundle bundle = new ResourceBundle.LayoutFileBundle( + xmlNoExtension, xml.getParentFile().getName(), pkg); + + DocumentBuilderFactory factory = DocumentBuilderFactory.newInstance(); + final DocumentBuilder builder = factory.newDocumentBuilder(); + final Document doc = builder.parse(original); + + final XPathFactory xPathFactory = XPathFactory.newInstance(); + final XPath xPath = xPathFactory.newXPath(); + + List<Node> variableNodes = getVariableNodes(doc, xPath); + + L.d("number of variable nodes %d", variableNodes.size()); + for (Node item : variableNodes) { + L.d("reading variable node %s", item); + NamedNodeMap attributes = item.getAttributes(); + String variableName = attributes.getNamedItem("name").getNodeValue(); + String variableType = attributes.getNamedItem("type").getNodeValue(); + L.d("name: %s, type:%s", variableName, variableType); + bundle.addVariable(variableName, variableType); + } + + final List<Node> imports = getImportNodes(doc, xPath); + L.d("import node count %d", imports.size()); + for (Node item : imports) { + NamedNodeMap attributes = item.getAttributes(); + String type = attributes.getNamedItem("type").getNodeValue(); + final Node aliasNode = attributes.getNamedItem("alias"); + final String alias; + if (aliasNode == null) { + final String[] split = StringUtils.split(type, '.'); + alias = split[split.length - 1]; + } else { + alias = aliasNode.getNodeValue(); + } + bundle.addImport(alias, type); + } + + final List<Node> bindingNodes = getBindingNodes(doc, xPath); + L.d("number of binding nodes %d", bindingNodes.size()); + int tagNumber = 0; + for (Node parent : bindingNodes) { + NamedNodeMap attributes = parent.getAttributes(); + String nodeName = parent.getNodeName(); + String className; + String includedLayoutName = null; + final Node id = attributes.getNamedItem("android:id"); + if ("include".equals(nodeName)) { + if (id == null) { + L.e("<include> must have android:id attribute with binding expressions."); + throw new RuntimeException("<include> must have android:id attribute " + + "with binding expressions."); + } + // get the layout attribute + final Node includedLayout = attributes.getNamedItem("layout"); + Preconditions.checkNotNull(includedLayout, "must include a layout"); + final String includeValue = includedLayout.getNodeValue(); + Preconditions.checkArgument(includeValue.startsWith(LAYOUT_PREFIX)); + // if user is binding something there, there MUST be a layout file to be + // generated. + String layoutName = includeValue.substring(LAYOUT_PREFIX.length()); + className = pkg + ".databinding." + + ParserHelper.INSTANCE$.toClassName(layoutName) + "Binding"; + includedLayoutName = layoutName; + } else { + className = getFullViewClassName(parent); + } + final Node originalTag = attributes.getNamedItem("android:tag"); + final String tag; + if (doc.getDocumentElement() == parent) { + tag = null; + } else { + tag = String.valueOf(tagNumber++); + } + final ResourceBundle.BindingTargetBundle bindingTargetBundle = + bundle.createBindingTarget(id == null ? null : id.getNodeValue(), + className, true, tag, originalTag == null ? null : originalTag.getNodeValue()); + bindingTargetBundle.setIncludedLayout(includedLayoutName); + + final int attrCount = attributes.getLength(); + for (int i = 0; i < attrCount; i ++) { + final Node attr = attributes.item(i); + String value = attr.getNodeValue(); + if (value.charAt(0) == '@' && value.charAt(1) == '{' && + value.charAt(value.length() - 1) == '}') { + final String strippedValue = value.substring(2, value.length() - 1); + bindingTargetBundle.addBinding(attr.getNodeName(), strippedValue); + } + } + } + + if (!bindingNodes.isEmpty() || !imports.isEmpty() || !variableNodes.isEmpty()) { + if (isMergeLayout(doc, xPath)) { + L.e("<merge> is not allowed with data binding."); + throw new RuntimeException("<merge> is not allowed with data binding."); + } + final List<Node> idNodes = getNakedIds(doc, xPath); + for (Node node : idNodes) { + if (!bindingNodes.contains(node) && !"include".equals(node.getNodeName())) { + final Node id = node.getAttributes().getNamedItem("android:id"); + final String className = getFullViewClassName(node); + bundle.createBindingTarget(id.getNodeValue(), className, true, null, null); + } + } + } + + return bundle; + } + + private boolean isMergeLayout(Document doc, XPath xPath) throws XPathExpressionException { + return !get(doc, xPath, XPATH_MERGE_TAG).isEmpty(); + } + + private List<Node> getBindingNodes(Document doc, XPath xPath) throws XPathExpressionException { + return get(doc, xPath, XPATH_BINDING_ELEMENTS); + } + + private List<Node> getVariableNodes(Document doc, XPath xPath) throws XPathExpressionException { + return get(doc, xPath, XPATH_VARIABLE_DEFINITIONS); + } + + private List<Node> getImportNodes(Document doc, XPath xPath) throws XPathExpressionException { + return get(doc, xPath, XPATH_IMPORT_DEFINITIONS); + } + + private List<Node> getNakedIds(Document doc, XPath xPath) throws XPathExpressionException { + return get(doc, xPath, XPATH_ID_ELEMENTS); + } + + private List<Node> get(Document doc, XPath xPath, String pattern) + throws XPathExpressionException { + final XPathExpression expr = xPath.compile(pattern); + return toList((NodeList) expr.evaluate(doc, XPathConstants.NODESET)); + } + + private List<Node> toList(NodeList nodeList) { + List<Node> result = new ArrayList<Node>(); + for (int i = 0; i < nodeList.getLength(); i ++) { + result.add(nodeList.item(i)); + } + return result; + } + + private String getFullViewClassName(Node viewNode) { + String viewName = viewNode.getNodeName(); + if ("view".equals(viewName)) { + Node classNode = viewNode.getAttributes().getNamedItem("class"); + if (classNode == null) { + L.e("No class attribute for 'view' node"); + } else { + viewName = classNode.getNodeValue(); + } + } + if (viewName.indexOf('.') == -1) { + if (ObjectUtils.equals(viewName, "View") || ObjectUtils.equals(viewName, "ViewGroup") || + ObjectUtils.equals(viewName, "ViewStub")) { + return "android.view." + viewName; + } + return "android.widget." + viewName; + } + return viewName; + } + + private void stripBindingTags(File xml, String newTag) throws IOException { + String res = XmlEditor.INSTANCE$.strip(xml, newTag); + if (res != null) { + L.d("file %s has changed, overwriting %s", xml.getName(), xml.getAbsolutePath()); + FileUtils.writeStringToFile(xml, res); + } + } + + private File stripFileAndGetOriginal(File xml, String binderId) + throws ParserConfigurationException, IOException, SAXException, + XPathExpressionException { + L.d("parsing resource file %s", xml.getAbsolutePath()); + DocumentBuilderFactory factory = DocumentBuilderFactory.newInstance(); + DocumentBuilder builder = factory.newDocumentBuilder(); + Document doc = builder.parse(xml); + XPathFactory xPathFactory = XPathFactory.newInstance(); + XPath xPath = xPathFactory.newXPath(); + final XPathExpression commentElementExpr = xPath + .compile("//comment()[starts-with(., \" From: file:\")][last()]"); + final NodeList commentElementNodes = (NodeList) commentElementExpr + .evaluate(doc, XPathConstants.NODESET); + L.d("comment element nodes count %s", commentElementNodes.getLength()); + if (commentElementNodes.getLength() == 0) { + L.d("cannot find comment element to find the actual file"); + return null; + } + final Node first = commentElementNodes.item(0); + String actualFilePath = first.getNodeValue().substring(" From: file:".length()).trim(); + L.d("actual file to parse: %s", actualFilePath); + File actualFile = new File(actualFilePath); + if (!actualFile.canRead()) { + L.d("cannot find original, skipping. %s", actualFile.getAbsolutePath()); + return null; + } + + // now if file has any binding expressions, find and delete them + // TODO we should rely on namespace to avoid parsing file twice + boolean changed = getVariableNodes(doc, xPath).size() > 0 || getImportNodes(doc, xPath).size() > 0; + if (changed) { + stripBindingTags(xml, binderId); + } + return actualFile; + } +} diff --git a/tools/data-binding/compiler/src/main/java/android/databinding/tool/store/ResourceBundle.java b/tools/data-binding/compiler/src/main/java/android/databinding/tool/store/ResourceBundle.java new file mode 100644 index 0000000..0cb29e7 --- /dev/null +++ b/tools/data-binding/compiler/src/main/java/android/databinding/tool/store/ResourceBundle.java @@ -0,0 +1,509 @@ +/* + * 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 android.databinding.tool.store; + +import com.google.common.base.Preconditions; +import com.google.common.base.Predicate; +import com.google.common.collect.Iterables; + +import android.databinding.tool.reflection.ModelAnalyzer; +import android.databinding.tool.reflection.ModelClass; +import android.databinding.tool.util.L; +import android.databinding.tool.util.ParserHelper; + +import java.io.Serializable; +import java.util.ArrayList; +import java.util.HashMap; +import java.util.HashSet; +import java.util.List; +import java.util.Map; +import java.util.Set; + +import javax.xml.bind.annotation.XmlAccessType; +import javax.xml.bind.annotation.XmlAccessorType; +import javax.xml.bind.annotation.XmlAttribute; +import javax.xml.bind.annotation.XmlElement; +import javax.xml.bind.annotation.XmlElementWrapper; +import javax.xml.bind.annotation.XmlRootElement; +import javax.xml.bind.annotation.adapters.XmlAdapter; +import javax.xml.bind.annotation.adapters.XmlJavaTypeAdapter; + +/** + * This is a serializable class that can keep the result of parsing layout files. + */ +public class ResourceBundle implements Serializable { + + private String mAppPackage; + + private HashMap<String, List<LayoutFileBundle>> mLayoutBundles + = new HashMap<String, List<LayoutFileBundle>>(); + + public ResourceBundle(String appPackage) { + mAppPackage = appPackage; + } + + public void addLayoutBundle(LayoutFileBundle bundle) { + Preconditions.checkArgument(bundle.mFileName != null, "File bundle must have a name"); + if (!mLayoutBundles.containsKey(bundle.mFileName)) { + mLayoutBundles.put(bundle.mFileName, new ArrayList<LayoutFileBundle>()); + } + final List<LayoutFileBundle> bundles = mLayoutBundles.get(bundle.mFileName); + for (LayoutFileBundle existing : bundles) { + if (existing.equals(bundle)) { + L.d("skipping layout bundle %s because it already exists.", bundle); + return; + } + } + L.d("adding bundle %s", bundle); + bundles.add(bundle); + } + + public HashMap<String, List<LayoutFileBundle>> getLayoutBundles() { + return mLayoutBundles; + } + + public String getAppPackage() { + return mAppPackage; + } + + public void validateMultiResLayouts() { + final Iterable<Map.Entry<String, List<LayoutFileBundle>>> multiResLayouts = Iterables + .filter(mLayoutBundles.entrySet(), + new Predicate<Map.Entry<String, List<LayoutFileBundle>>>() { + @Override + public boolean apply(Map.Entry<String, List<LayoutFileBundle>> input) { + return input.getValue().size() > 1; + } + }); + + for (Map.Entry<String, List<LayoutFileBundle>> bundles : multiResLayouts) { + // validate all ids are in correct view types + // and all variables have the same name + Map<String, String> variableTypes = new HashMap<String, String>(); + Map<String, String> importTypes = new HashMap<String, String>(); + + for (LayoutFileBundle bundle : bundles.getValue()) { + bundle.mHasVariations = true; + for (Map.Entry<String, String> variable : bundle.mVariables.entrySet()) { + String existing = variableTypes.get(variable.getKey()); + Preconditions + .checkState(existing == null || existing.equals(variable.getValue()), + "inconsistent variable types for %s for layout %s", + variable.getKey(), bundle.mFileName); + variableTypes.put(variable.getKey(), variable.getValue()); + } + for (Map.Entry<String, String> userImport : bundle.mImports.entrySet()) { + String existing = importTypes.get(userImport.getKey()); + Preconditions + .checkState(existing == null || existing.equals(userImport.getValue()), + "inconsistent variable types for %s for layout %s", + userImport.getKey(), bundle.mFileName); + importTypes.put(userImport.getKey(), userImport.getValue()); + } + } + + for (LayoutFileBundle bundle : bundles.getValue()) { + // now add missing ones to each to ensure they can be referenced + L.d("checking for missing variables in %s / %s", bundle.mFileName, + bundle.mConfigName); + for (Map.Entry<String, String> variable : variableTypes.entrySet()) { + if (!bundle.mVariables.containsKey(variable.getKey())) { + bundle.mVariables.put(variable.getKey(), variable.getValue()); + L.d("adding missing variable %s to %s / %s", variable.getKey(), + bundle.mFileName, bundle.mConfigName); + } + } + for (Map.Entry<String, String> userImport : importTypes.entrySet()) { + if (!bundle.mImports.containsKey(userImport.getKey())) { + bundle.mImports.put(userImport.getKey(), userImport.getValue()); + L.d("adding missing import %s to %s / %s", userImport.getKey(), + bundle.mFileName, bundle.mConfigName); + } + } + } + + Set<String> includeBindingIds = new HashSet<String>(); + Set<String> viewBindingIds = new HashSet<String>(); + Map<String, String> viewTypes = new HashMap<String, String>(); + Map<String, String> includes = new HashMap<String, String>(); + L.d("validating ids for %s", bundles.getKey()); + for (LayoutFileBundle bundle : bundles.getValue()) { + for (BindingTargetBundle target : bundle.mBindingTargetBundles) { + L.d("checking %s %s %s", target.getId(), target.mFullClassName, target.isBinder()); + if (target.isBinder()) { + Preconditions.checkState(!viewBindingIds.contains(target.mFullClassName), + "Cannot use the same id for a View and an include tag. Error in " + + "file %s / %s", bundle.mFileName, bundle.mConfigName); + includeBindingIds.add(target.mFullClassName); + } else { + Preconditions.checkState(!includeBindingIds.contains(target.mFullClassName), + "Cannot use the same id for a View and an include tag. Error in " + + "file %s / %s", bundle.mFileName, bundle.mConfigName); + viewBindingIds.add(target.mFullClassName); + } + String existingType = viewTypes.get(target.mId); + if (existingType == null) { + L.d("assigning %s as %s", target.getId(), target.mFullClassName); + viewTypes.put(target.mId, target.mFullClassName); + if (target.isBinder()) { + includes.put(target.mId, target.getIncludedLayout()); + } + } else if (!existingType.equals(target.mFullClassName)) { + if (target.isBinder()) { + L.d("overriding %s as base binder", target.getId()); + viewTypes.put(target.mId, + "android.databinding.ViewDataBinding"); + includes.put(target.mId, target.getIncludedLayout()); + } else { + L.d("overriding %s as base view", target.getId()); + viewTypes.put(target.mId, "android.view.View"); + } + } + } + } + + for (LayoutFileBundle bundle : bundles.getValue()) { + for (Map.Entry<String, String> viewType : viewTypes.entrySet()) { + BindingTargetBundle target = bundle.getBindingTargetById(viewType.getKey()); + if (target == null) { + bundle.createBindingTarget(viewType.getKey(), viewType.getValue(), false, + null, null).setIncludedLayout(includes.get(viewType.getKey())); + } else { + L.d("setting interface type on %s (%s) as %s", target.mId, target.mFullClassName, viewType.getValue()); + target.setInterfaceType(viewType.getValue()); + } + } + } + } + // assign class names to each + for (Map.Entry<String, List<LayoutFileBundle>> entry : mLayoutBundles.entrySet()) { + for (LayoutFileBundle bundle : entry.getValue()) { + final String configName; + if (bundle.hasVariations()) { + // append configuration specifiers. + final String parentFileName = bundle.mDirectory; + L.d("parent file for %s is %s", bundle.getFileName(), parentFileName); + if ("layout".equals(parentFileName)) { + configName = ""; + } else { + configName = ParserHelper.INSTANCE$.toClassName(parentFileName.substring("layout-".length())); + } + } else { + configName = ""; + } + bundle.mConfigName = configName; + } + } + } + + @XmlAccessorType(XmlAccessType.NONE) + @XmlRootElement(name="Layout") + public static class LayoutFileBundle implements Serializable { + @XmlAttribute(name="layout", required = true) + public String mFileName; + @XmlAttribute(name="modulePackage", required = true) + public String mModulePackage; + private String mConfigName; + + @XmlAttribute(name="directory", required = true) + public String mDirectory; + public boolean mHasVariations; + + @XmlElement(name="Variables") + @XmlJavaTypeAdapter(NameTypeAdapter.class) + public Map<String, String> mVariables = new HashMap<String, String>(); + + @XmlElement(name="Imports") + @XmlJavaTypeAdapter(NameTypeAdapter.class) + public Map<String, String> mImports = new HashMap<String, String>(); + + @XmlElementWrapper(name="Targets") + @XmlElement(name="Target") + public List<BindingTargetBundle> mBindingTargetBundles = new ArrayList<BindingTargetBundle>(); + + // for XML binding + public LayoutFileBundle() { + } + + public LayoutFileBundle(String fileName, String directory, String modulePackage) { + mFileName = fileName; + mDirectory = directory; + mModulePackage = modulePackage; + } + + public void addVariable(String name, String type) { + mVariables.put(name, type); + } + + public void addImport(String alias, String type) { + mImports.put(alias, type); + } + + public BindingTargetBundle createBindingTarget(String id, String fullClassName, + boolean used, String tag, String originalTag) { + BindingTargetBundle target = new BindingTargetBundle(id, fullClassName, used, tag, + originalTag); + mBindingTargetBundles.add(target); + return target; + } + + public boolean isEmpty() { + return mVariables.isEmpty() && mImports.isEmpty() && mBindingTargetBundles.isEmpty(); + } + + public BindingTargetBundle getBindingTargetById(String key) { + for (BindingTargetBundle target : mBindingTargetBundles) { + if (key.equals(target.mId)) { + return target; + } + } + return null; + } + + public String getFileName() { + return mFileName; + } + + public String getConfigName() { + return mConfigName; + } + + public String getDirectory() { + return mDirectory; + } + + public boolean hasVariations() { + return mHasVariations; + } + + public Map<String, String> getVariables() { + return mVariables; + } + + public Map<String, String> getImports() { + return mImports; + } + + public List<BindingTargetBundle> getBindingTargetBundles() { + return mBindingTargetBundles; + } + + @Override + public boolean equals(Object o) { + if (this == o) { + return true; + } + if (o == null || getClass() != o.getClass()) { + return false; + } + + LayoutFileBundle bundle = (LayoutFileBundle) o; + + if (mConfigName != null ? !mConfigName.equals(bundle.mConfigName) + : bundle.mConfigName != null) { + return false; + } + if (mDirectory != null ? !mDirectory.equals(bundle.mDirectory) + : bundle.mDirectory != null) { + return false; + } + if (mFileName != null ? !mFileName.equals(bundle.mFileName) + : bundle.mFileName != null) { + return false; + } + + return true; + } + + @Override + public int hashCode() { + int result = mFileName != null ? mFileName.hashCode() : 0; + result = 31 * result + (mConfigName != null ? mConfigName.hashCode() : 0); + result = 31 * result + (mDirectory != null ? mDirectory.hashCode() : 0); + return result; + } + + @Override + public String toString() { + return "LayoutFileBundle{" + + "mHasVariations=" + mHasVariations + + ", mDirectory='" + mDirectory + '\'' + + ", mConfigName='" + mConfigName + '\'' + + ", mModulePackage='" + mModulePackage + '\'' + + ", mFileName='" + mFileName + '\'' + + '}'; + } + + public String getModulePackage() { + return mModulePackage; + } + } + + @XmlAccessorType(XmlAccessType.NONE) + public static class MarshalledNameType { + @XmlAttribute(name="type", required = true) + public String type; + + @XmlAttribute(name="name", required = true) + public String name; + } + + public static class MarshalledMapType { + public List<MarshalledNameType> entries; + } + + @XmlAccessorType(XmlAccessType.NONE) + public static class BindingTargetBundle implements Serializable { + // public for XML serialization + + @XmlAttribute(name="id") + public String mId; + @XmlAttribute(name="tag", required = true) + public String mTag; + @XmlAttribute(name="originalTag") + public String mOriginalTag; + @XmlAttribute(name="boundClass", required = true) + public String mFullClassName; + public boolean mUsed = true; + @XmlElementWrapper(name="Expressions") + @XmlElement(name="Expression") + public List<BindingBundle> mBindingBundleList = new ArrayList<BindingBundle>(); + @XmlAttribute(name="include") + public String mIncludedLayout; + private String mInterfaceType; + + // For XML serialization + public BindingTargetBundle() {} + + public BindingTargetBundle(String id, String fullClassName, boolean used, + String tag, String originalTag) { + mId = id; + mFullClassName = fullClassName; + mUsed = used; + mTag = tag; + mOriginalTag = originalTag; + } + + public void addBinding(String name, String expr) { + mBindingBundleList.add(new BindingBundle(name, expr)); + } + + public void setIncludedLayout(String includedLayout) { + mIncludedLayout = includedLayout; + } + + public String getIncludedLayout() { + return mIncludedLayout; + } + + public boolean isBinder() { + return mIncludedLayout != null; + } + + public void setInterfaceType(String interfaceType) { + mInterfaceType = interfaceType; + } + + public String getId() { + return mId; + } + + public String getTag() { + return mTag; + } + + public String getOriginalTag() { + return mOriginalTag; + } + + public String getFullClassName() { + return mFullClassName; + } + + public boolean isUsed() { + return mUsed; + } + + public List<BindingBundle> getBindingBundleList() { + return mBindingBundleList; + } + + public String getInterfaceType() { + return mInterfaceType; + } + + @XmlAccessorType(XmlAccessType.NONE) + public static class BindingBundle implements Serializable { + + private String mName; + private String mExpr; + + public BindingBundle() {} + + public BindingBundle(String name, String expr) { + mName = name; + mExpr = expr; + } + + @XmlAttribute(name="attribute", required=true) + public String getName() { + return mName; + } + + @XmlAttribute(name="text", required=true) + public String getExpr() { + return mExpr; + } + + public void setName(String name) { + mName = name; + } + + public void setExpr(String expr) { + mExpr = expr; + } + } + } + + private final static class NameTypeAdapter + extends XmlAdapter<MarshalledMapType, Map<String, String>> { + + @Override + public HashMap<String, String> unmarshal(MarshalledMapType v) throws Exception { + HashMap<String, String> map = new HashMap<String, String>(); + if (v.entries != null) { + for (MarshalledNameType entry : v.entries) { + map.put(entry.name, entry.type); + } + } + return map; + } + + @Override + public MarshalledMapType marshal(Map<String, String> v) throws Exception { + if (v.isEmpty()) { + return null; + } + MarshalledMapType marshalled = new MarshalledMapType(); + marshalled.entries = new ArrayList<MarshalledNameType>(); + for (String name : v.keySet()) { + MarshalledNameType nameType = new MarshalledNameType(); + nameType.name = name; + nameType.type = v.get(name); + marshalled.entries.add(nameType); + } + return marshalled; + } + } +} diff --git a/tools/data-binding/compiler/src/main/java/android/databinding/tool/store/SetterStore.java b/tools/data-binding/compiler/src/main/java/android/databinding/tool/store/SetterStore.java new file mode 100644 index 0000000..3a53c2c --- /dev/null +++ b/tools/data-binding/compiler/src/main/java/android/databinding/tool/store/SetterStore.java @@ -0,0 +1,612 @@ +/* + * 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 android.databinding.tool.store; + +import org.apache.commons.lang3.StringUtils; + +import android.databinding.tool.reflection.ModelAnalyzer; +import android.databinding.tool.reflection.ModelClass; +import android.databinding.tool.reflection.ModelMethod; +import android.databinding.tool.util.GenerationalClassUtil; +import android.databinding.tool.util.L; + +import java.io.IOException; +import java.io.Serializable; +import java.util.ArrayList; +import java.util.Arrays; +import java.util.HashMap; +import java.util.List; +import java.util.Map; +import java.util.Objects; +import java.util.Set; + +import javax.annotation.processing.ProcessingEnvironment; +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.TypeMirror; + +public class SetterStore { + + public static final String SETTER_STORE_FILE_EXT = "-setter_store.bin"; + + private static SetterStore sStore; + + private final IntermediateV1 mStore; + private final ModelAnalyzer mClassAnalyzer; + + private SetterStore(ModelAnalyzer modelAnalyzer, IntermediateV1 store) { + mClassAnalyzer = modelAnalyzer; + mStore = store; + } + + public static SetterStore get(ModelAnalyzer modelAnalyzer) { + if (sStore == null) { + sStore = load(modelAnalyzer, SetterStore.class.getClassLoader()); + } + return sStore; + } + + private static SetterStore load(ModelAnalyzer modelAnalyzer, ClassLoader classLoader) { + IntermediateV1 store = new IntermediateV1(); + List<Intermediate> previousStores = GenerationalClassUtil + .loadObjects(classLoader, + new GenerationalClassUtil.ExtensionFilter(SETTER_STORE_FILE_EXT)); + for (Intermediate intermediate : previousStores) { + merge(store, intermediate); + } + return new SetterStore(modelAnalyzer, store); + } + + public void addRenamedMethod(String attribute, String declaringClass, String method, + TypeElement declaredOn) { + HashMap<String, MethodDescription> renamed = mStore.renamedMethods.get(attribute); + if (renamed == null) { + renamed = new HashMap<String, MethodDescription>(); + mStore.renamedMethods.put(attribute, renamed); + } + MethodDescription methodDescription = + new MethodDescription(declaredOn.getQualifiedName().toString(), method); + L.d("STORE addmethod desc %s", methodDescription); + renamed.put(declaringClass, methodDescription); + } + + public void addBindingAdapter(String attribute, ExecutableElement bindingMethod) { + L.d("STORE addBindingAdapter %s %s", attribute, bindingMethod); + HashMap<AccessorKey, MethodDescription> adapters = mStore.adapterMethods.get(attribute); + + if (adapters == null) { + adapters = new HashMap<AccessorKey, MethodDescription>(); + mStore.adapterMethods.put(attribute, adapters); + } + List<? extends VariableElement> parameters = bindingMethod.getParameters(); + String view = getQualifiedName(parameters.get(0).asType()); + String value = getQualifiedName(parameters.get(1).asType()); + + AccessorKey key = new AccessorKey(view, value); + if (adapters.containsKey(key)) { + throw new IllegalArgumentException("Already exists!"); + } + + adapters.put(key, new MethodDescription(bindingMethod)); + } + + public void addUntaggableTypes(String[] typeNames, TypeElement declaredOn) { + L.d("STORE addUntaggableTypes %s %s", Arrays.toString(typeNames), declaredOn); + String declaredType = declaredOn.getQualifiedName().toString(); + for (String type : typeNames) { + mStore.untaggableTypes.put(type, declaredType); + } + } + + private static String getQualifiedName(TypeMirror type) { + switch (type.getKind()) { + case BOOLEAN: + case BYTE: + case SHORT: + case INT: + case LONG: + case CHAR: + case FLOAT: + case DOUBLE: + case VOID: + return type.toString(); + case ARRAY: + return getQualifiedName(((ArrayType) type).getComponentType()) + "[]"; + case DECLARED: + return ((TypeElement) ((DeclaredType) type).asElement()).getQualifiedName() + .toString(); + default: + return "-- no type --"; + } + } + + public void addConversionMethod(ExecutableElement conversionMethod) { + L.d("STORE addConversionMethod %s", conversionMethod); + List<? extends VariableElement> parameters = conversionMethod.getParameters(); + String fromType = getQualifiedName(parameters.get(0).asType()); + String toType = getQualifiedName(conversionMethod.getReturnType()); + MethodDescription methodDescription = new MethodDescription(conversionMethod); + HashMap<String, MethodDescription> convertTo = mStore.conversionMethods.get(fromType); + if (convertTo == null) { + convertTo = new HashMap<String, MethodDescription>(); + mStore.conversionMethods.put(fromType, convertTo); + } + convertTo.put(toType, methodDescription); + } + + public void clear(Set<String> classes) { + ArrayList<AccessorKey> removedAccessorKeys = new ArrayList<AccessorKey>(); + for (HashMap<AccessorKey, MethodDescription> adapters : mStore.adapterMethods.values()) { + for (AccessorKey key : adapters.keySet()) { + MethodDescription description = adapters.get(key); + if (classes.contains(description.type)) { + removedAccessorKeys.add(key); + } + } + removeFromMap(adapters, removedAccessorKeys); + } + + ArrayList<String> removedRenamed = new ArrayList<String>(); + for (HashMap<String, MethodDescription> renamed : mStore.renamedMethods.values()) { + for (String key : renamed.keySet()) { + if (classes.contains(renamed.get(key).type)) { + removedRenamed.add(key); + } + } + removeFromMap(renamed, removedRenamed); + } + + ArrayList<String> removedConversions = new ArrayList<String>(); + for (HashMap<String, MethodDescription> convertTos : mStore.conversionMethods.values()) { + for (String toType : convertTos.keySet()) { + MethodDescription methodDescription = convertTos.get(toType); + if (classes.contains(methodDescription.type)) { + removedConversions.add(toType); + } + } + removeFromMap(convertTos, removedConversions); + } + + ArrayList<String> removedUntaggable = new ArrayList<String>(); + for (String typeName : mStore.untaggableTypes.keySet()) { + if (classes.contains(mStore.untaggableTypes.get(typeName))) { + removedUntaggable.add(typeName); + } + } + removeFromMap(mStore.untaggableTypes, removedUntaggable); + } + + private static <K, V> void removeFromMap(Map<K, V> map, List<K> keys) { + for (K key : keys) { + map.remove(key); + } + keys.clear(); + } + + public void write(String projectPackage, ProcessingEnvironment processingEnvironment) + throws IOException { + GenerationalClassUtil.writeIntermediateFile(processingEnvironment, + projectPackage, projectPackage + SETTER_STORE_FILE_EXT, mStore); + } + + public SetterCall getSetterCall(String attribute, ModelClass viewType, + ModelClass valueType, Map<String, String> imports) { + if (!attribute.startsWith("android:")) { + int colon = attribute.indexOf(':'); + if (colon >= 0) { + attribute = attribute.substring(colon + 1); + } + } + SetterCall setterCall = null; + MethodDescription conversionMethod = null; + if (viewType != null) { + HashMap<AccessorKey, MethodDescription> adapters = mStore.adapterMethods.get(attribute); + ModelMethod bestSetterMethod = getBestSetter(viewType, valueType, attribute, imports); + ModelClass bestViewType = null; + ModelClass bestValueType = null; + if (bestSetterMethod != null) { + bestViewType = bestSetterMethod.getDeclaringClass(); + bestValueType = bestSetterMethod.getParameterTypes()[0]; + setterCall = new ModelMethodSetter(bestSetterMethod); + } + + if (adapters != null) { + for (AccessorKey key : adapters.keySet()) { + try { + ModelClass adapterViewType = mClassAnalyzer + .findClass(key.viewType, imports); + if (adapterViewType.isAssignableFrom(viewType)) { + try { + ModelClass adapterValueType = mClassAnalyzer + .findClass(key.valueType, imports); + boolean isBetterView = bestViewType == null || + bestValueType.isAssignableFrom(adapterValueType); + if (isBetterParameter(valueType, adapterValueType, bestValueType, + isBetterView, imports)) { + bestViewType = adapterViewType; + bestValueType = adapterValueType; + MethodDescription adapter = adapters.get(key); + setterCall = new AdapterSetter(adapter); + } + + } catch (Exception e) { + L.e(e, "Unknown class: %s", key.valueType); + } + } + } catch (Exception e) { + L.e(e, "Unknown class: %s", key.viewType); + } + } + } + + conversionMethod = getConversionMethod(valueType, bestValueType, imports); + if (valueType.isObject() && setterCall != null && bestValueType.isNullable()) { + setterCall.setCast(bestValueType); + } + } + if (setterCall == null) { + setterCall = new DummySetter(getDefaultSetter(attribute)); + // might be an include tag etc. just note it and continue. + L.d("Cannot find the setter for attribute " + attribute + ". might be an include file," + + " moving on."); + } + setterCall.setConverter(conversionMethod); + return setterCall; + } + + public boolean isUntaggable(String viewType) { + return mStore.untaggableTypes.containsKey(viewType); + } + + private ModelMethod getBestSetter(ModelClass viewType, ModelClass argumentType, + String attribute, Map<String, String> imports) { + List<String> setterCandidates = new ArrayList<String>(); + HashMap<String, MethodDescription> renamed = mStore.renamedMethods.get(attribute); + if (renamed != null) { + for (String className : renamed.keySet()) { + try { + ModelClass renamedViewType = mClassAnalyzer.findClass(className, imports); + if (renamedViewType.isAssignableFrom(viewType)) { + setterCandidates.add(renamed.get(className).method); + break; + } + } catch (Exception e) { + //printMessage(Diagnostic.Kind.NOTE, "Unknown class: " + className); + } + } + } + setterCandidates.add(getDefaultSetter(attribute)); + setterCandidates.add(trimAttributeNamespace(attribute)); + + ModelMethod bestMethod = null; + ModelClass bestParameterType = null; + List<ModelClass> args = new ArrayList<ModelClass>(); + args.add(argumentType); + for (String name : setterCandidates) { + ModelMethod[] methods = viewType.getMethods(name, 1); + + for (ModelMethod method : methods) { + ModelClass[] parameterTypes = method.getParameterTypes(); + ModelClass param = parameterTypes[0]; + if (method.isVoid() && + isBetterParameter(argumentType, param, bestParameterType, true, imports)) { + bestParameterType = param; + bestMethod = method; + } + } + } + return bestMethod; + + } + + private static String trimAttributeNamespace(String attribute) { + final int colonIndex = attribute.indexOf(':'); + return colonIndex == -1 ? attribute : attribute.substring(colonIndex + 1); + } + + private static String getDefaultSetter(String attribute) { + return "set" + StringUtils.capitalize(trimAttributeNamespace(attribute)); + } + + private boolean isBetterParameter(ModelClass argument, ModelClass parameter, + ModelClass oldParameter, boolean isBetterViewTypeMatch, Map<String, String> imports) { + // Right view type. Check the value + if (!isBetterViewTypeMatch && oldParameter.equals(argument)) { + return false; + } else if (argument.equals(parameter)) { + // Exact match + return true; + } else if (!isBetterViewTypeMatch && + ModelMethod.isBoxingConversion(oldParameter, argument)) { + return false; + } else if (ModelMethod.isBoxingConversion(parameter, argument)) { + // Boxing/unboxing is second best + return true; + } else { + int oldConversionLevel = ModelMethod.getImplicitConversionLevel(oldParameter); + if (ModelMethod.isImplicitConversion(argument, parameter)) { + // Better implicit conversion + int conversionLevel = ModelMethod.getImplicitConversionLevel(parameter); + return oldConversionLevel < 0 || conversionLevel < oldConversionLevel; + } else if (oldConversionLevel >= 0) { + return false; + } else if (parameter.isAssignableFrom(argument)) { + // Right type, see if it is better than the current best match. + if (oldParameter == null) { + return true; + } else { + return oldParameter.isAssignableFrom(parameter); + } + } else { + MethodDescription conversionMethod = getConversionMethod(argument, parameter, + imports); + if (conversionMethod != null) { + return true; + } + if (getConversionMethod(argument, oldParameter, imports) != null) { + return false; + } + return argument.isObject() && !parameter.isPrimitive(); + } + } + } + + private MethodDescription getConversionMethod(ModelClass from, ModelClass to, + Map<String, String> imports) { + if (from != null && to != null) { + for (String fromClassName : mStore.conversionMethods.keySet()) { + try { + ModelClass convertFrom = mClassAnalyzer.findClass(fromClassName, imports); + if (canUseForConversion(from, convertFrom)) { + HashMap<String, MethodDescription> conversion = + mStore.conversionMethods.get(fromClassName); + for (String toClassName : conversion.keySet()) { + try { + ModelClass convertTo = mClassAnalyzer.findClass(toClassName, + imports); + if (canUseForConversion(convertTo, to)) { + return conversion.get(toClassName); + } + } catch (Exception e) { + L.d(e, "Unknown class: %s", toClassName); + } + } + } + } catch (Exception e) { + L.d(e, "Unknown class: %s", fromClassName); + } + } + } + return null; + } + + private boolean canUseForConversion(ModelClass from, ModelClass to) { + return from.equals(to) || ModelMethod.isBoxingConversion(from, to) || + to.isAssignableFrom(from); + } + + private static void merge(IntermediateV1 store, Intermediate dumpStore) { + IntermediateV1 intermediateV1 = (IntermediateV1) dumpStore.upgrade(); + merge(store.adapterMethods, intermediateV1.adapterMethods); + merge(store.renamedMethods, intermediateV1.renamedMethods); + merge(store.conversionMethods, intermediateV1.conversionMethods); + store.untaggableTypes.putAll(intermediateV1.untaggableTypes); + } + + private static <K, V> void merge(HashMap<K, HashMap<V, MethodDescription>> first, + HashMap<K, HashMap<V, MethodDescription>> second) { + for (K key : second.keySet()) { + HashMap<V, MethodDescription> firstVals = first.get(key); + HashMap<V, MethodDescription> secondVals = second.get(key); + if (firstVals == null) { + first.put(key, secondVals); + } else { + for (V key2 : secondVals.keySet()) { + if (!firstVals.containsKey(key2)) { + firstVals.put(key2, secondVals.get(key2)); + } + } + } + } + } + + private static class MethodDescription implements Serializable { + + private static final long serialVersionUID = 1; + + public final String type; + + public final String method; + + public MethodDescription(String type, String method) { + this.type = type; + this.method = method; + L.d("BINARY created method desc 1 %s %s", type, method ); + } + + public MethodDescription(ExecutableElement method) { + TypeElement enclosingClass = (TypeElement) method.getEnclosingElement(); + this.type = enclosingClass.getQualifiedName().toString(); + this.method = method.getSimpleName().toString(); + L.d("BINARY created method desc 2 %s %s, %s", type, this.method, method); + } + + @Override + public boolean equals(Object obj) { + if (obj instanceof MethodDescription) { + MethodDescription that = (MethodDescription) obj; + return that.type.equals(this.type) && that.method.equals(this.method); + } else { + return false; + } + } + + @Override + public int hashCode() { + return Objects.hash(type, method); + } + + @Override + public String toString() { + return type + "." + method + "()"; + } + } + + private static class AccessorKey implements Serializable { + + private static final long serialVersionUID = 1; + + public final String viewType; + + public final String valueType; + + public AccessorKey(String viewType, String valueType) { + this.viewType = viewType; + this.valueType = valueType; + } + + @Override + public int hashCode() { + return Objects.hash(viewType, valueType); + } + + @Override + public boolean equals(Object obj) { + if (obj instanceof AccessorKey) { + AccessorKey that = (AccessorKey) obj; + return viewType.equals(that.valueType) && valueType.equals(that.valueType); + } else { + return false; + } + } + + @Override + public String toString() { + return "AK(" + viewType + ", " + valueType + ")"; + } + } + + private interface Intermediate extends Serializable { + Intermediate upgrade(); + } + + private static class IntermediateV1 implements Serializable, Intermediate { + private static final long serialVersionUID = 1; + public final HashMap<String, HashMap<AccessorKey, MethodDescription>> adapterMethods = + new HashMap<String, HashMap<AccessorKey, MethodDescription>>(); + public final HashMap<String, HashMap<String, MethodDescription>> renamedMethods = + new HashMap<String, HashMap<String, MethodDescription>>(); + public final HashMap<String, HashMap<String, MethodDescription>> conversionMethods = + new HashMap<String, HashMap<String, MethodDescription>>(); + public final HashMap<String, String> untaggableTypes = new HashMap<String, String>(); + + public IntermediateV1() { + } + + @Override + public Intermediate upgrade() { + return this; + } + } + + public static class DummySetter extends SetterCall { + private String mMethodName; + + public DummySetter(String methodName) { + mMethodName = methodName; + } + + @Override + public String toJavaInternal(String viewExpression, String valueExpression) { + return viewExpression + "." + mMethodName + "(" + valueExpression + ")"; + } + + @Override + public int getMinApi() { + return 1; + } + } + + public static class AdapterSetter extends SetterCall { + final MethodDescription mAdapter; + + public AdapterSetter(MethodDescription adapter) { + mAdapter = adapter; + } + + @Override + public String toJavaInternal(String viewExpression, String valueExpression) { + return mAdapter.type + "." + mAdapter.method + "(" + viewExpression + ", " + + mCastString + valueExpression + ")"; + } + + @Override + public int getMinApi() { + return 1; + } + } + + public static class ModelMethodSetter extends SetterCall { + final ModelMethod mModelMethod; + + public ModelMethodSetter(ModelMethod modelMethod) { + mModelMethod = modelMethod; + } + + @Override + public String toJavaInternal(String viewExpression, String valueExpression) { + return viewExpression + "." + mModelMethod.getName() + "(" + mCastString + + valueExpression + ")"; + } + + @Override + public int getMinApi() { + return mModelMethod.getMinApi(); + } + } + + public static abstract class SetterCall { + private MethodDescription mConverter; + protected String mCastString = ""; + + public SetterCall() { + } + + public void setConverter(MethodDescription converter) { + mConverter = converter; + } + + protected abstract String toJavaInternal(String viewExpression, String converted); + + public final String toJava(String viewExpression, String valueExpression) { + return toJavaInternal(viewExpression, convertValue(valueExpression)); + } + + protected String convertValue(String valueExpression) { + return mConverter == null ? valueExpression : + mConverter.type + "." + mConverter.method + "(" + valueExpression + ")"; + } + + abstract public int getMinApi(); + + public void setCast(ModelClass castTo) { + mCastString = "(" + castTo.toJavaCode() + ") "; + } + } +} diff --git a/tools/data-binding/compiler/src/main/java/android/databinding/tool/util/GenerationalClassUtil.java b/tools/data-binding/compiler/src/main/java/android/databinding/tool/util/GenerationalClassUtil.java new file mode 100644 index 0000000..e7373ac --- /dev/null +++ b/tools/data-binding/compiler/src/main/java/android/databinding/tool/util/GenerationalClassUtil.java @@ -0,0 +1,184 @@ +/* + * 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 android.databinding.tool.util; + +import org.apache.commons.io.FileUtils; +import org.apache.commons.io.IOUtils; +import org.apache.commons.io.filefilter.IOFileFilter; +import org.apache.commons.io.filefilter.TrueFileFilter; + +import java.io.File; +import java.io.IOException; +import java.io.InputStream; +import java.io.ObjectInputStream; +import java.io.ObjectOutputStream; +import java.io.OutputStream; +import java.io.Serializable; +import java.net.URISyntaxException; +import java.net.URL; +import java.net.URLClassLoader; +import java.util.ArrayList; +import java.util.Collection; +import java.util.Enumeration; +import java.util.List; +import java.util.zip.ZipEntry; +import java.util.zip.ZipFile; + +import javax.annotation.processing.ProcessingEnvironment; +import javax.tools.FileObject; +import javax.tools.StandardLocation; + +/** + * A utility class that helps adding build specific objects to the jar file + * and their extraction later on. + */ +public class GenerationalClassUtil { + public static <T extends Serializable> List<T> loadObjects(ClassLoader classLoader, Filter filter) { + final List<T> result = new ArrayList<T>(); + if (!(classLoader instanceof URLClassLoader)) { + L.d("class loader is not url class loader (%s). skipping.", classLoader.getClass()); + return result; + } + final URLClassLoader urlClassLoader = (URLClassLoader) classLoader; + for (URL url : urlClassLoader.getURLs()) { + L.d("checking url %s for intermediate data", url); + try { + final File file = new File(url.toURI()); + if (!file.exists()) { + L.d("cannot load file for %s", url); + continue; + } + if (file.isDirectory()) { + // probably exported classes dir. + loadFromDirectory(filter, result, file); + } else { + // assume it is a zip file + loadFomZipFile(filter, result, file); + } + } catch (IOException e) { + L.d("cannot open zip file from %s", url); + } catch (URISyntaxException e) { + L.d("cannot open zip file from %s", url); + } + } + return result; + } + + private static <T extends Serializable> void loadFromDirectory(final Filter filter, List<T> result, + File directory) { + //noinspection unchecked + Collection<File> files = FileUtils.listFiles(directory, new IOFileFilter() { + @Override + public boolean accept(File file) { + return filter.accept(file.getName()); + } + + @Override + public boolean accept(File dir, String name) { + return filter.accept(name); + } + }, TrueFileFilter.INSTANCE); + for (File file : files) { + InputStream inputStream = null; + try { + inputStream = FileUtils.openInputStream(file); + T item = fromInputStream(result, inputStream); + L.d("loaded item %s from file", item); + if (item != null) { + result.add(item); + } + } catch (IOException e) { + L.e(e, "Could not merge in Bindables from %s", file.getAbsolutePath()); + } catch (ClassNotFoundException e) { + L.e(e, "Could not read Binding properties intermediate file. %s", file.getAbsolutePath()); + } finally { + IOUtils.closeQuietly(inputStream); + } + } + } + + private static <T extends Serializable> void loadFomZipFile(Filter filter, + List<T> result, File file) throws IOException { + ZipFile zipFile = new ZipFile(file); + Enumeration<? extends ZipEntry> entries = zipFile.entries(); + while (entries.hasMoreElements()) { + ZipEntry entry = entries.nextElement(); + if (!filter.accept(entry.getName())) { + continue; + } + L.d("loading data from file %s", entry.getName()); + InputStream inputStream = null; + try { + inputStream = zipFile.getInputStream(entry); + T item = fromInputStream(result, inputStream); + L.d("loaded item %s from zip file", item); + if (item != null) { + result.add(item); + } + } catch (IOException e) { + L.e(e, "Could not merge in Bindables from %s", file.getAbsolutePath()); + } catch (ClassNotFoundException e) { + L.e(e, "Could not read Binding properties intermediate file. %s", file.getAbsolutePath()); + } finally { + IOUtils.closeQuietly(inputStream); + } + } + } + + private static <T extends Serializable> T fromInputStream(List<T> result, + InputStream inputStream) throws IOException, ClassNotFoundException { + ObjectInputStream in = new ObjectInputStream(inputStream); + return (T) in.readObject(); + + } + + public static void writeIntermediateFile(ProcessingEnvironment processingEnv, + String packageName, String fileName, Serializable object) { + ObjectOutputStream oos = null; + try { + FileObject intermediate = processingEnv.getFiler().createResource( + StandardLocation.CLASS_OUTPUT, packageName, + fileName); + OutputStream ios = intermediate.openOutputStream(); + oos = new ObjectOutputStream(ios); + oos.writeObject(object); + oos.close(); + L.d("wrote intermediate bindable file %s %s", packageName, fileName); + } catch (IOException e) { + L.e(e, "Could not write to intermediate file: %s", fileName); + } finally { + IOUtils.closeQuietly(oos); + } + } + + + public static interface Filter { + public boolean accept(String entryName); + } + + public static class ExtensionFilter implements Filter { + private final String mExtension; + public ExtensionFilter(String extension) { + mExtension = extension; + } + + @Override + public boolean accept(String entryName) { + return entryName.endsWith(mExtension); + } + } +} diff --git a/tools/data-binding/compiler/src/main/java/android/databinding/tool/util/L.java b/tools/data-binding/compiler/src/main/java/android/databinding/tool/util/L.java new file mode 100644 index 0000000..5439f59 --- /dev/null +++ b/tools/data-binding/compiler/src/main/java/android/databinding/tool/util/L.java @@ -0,0 +1,63 @@ +/* + * 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 android.databinding.tool.util; + +import org.apache.commons.lang3.exception.ExceptionUtils; + +import android.databinding.tool.reflection.ModelAnalyzer; +import android.databinding.tool.reflection.annotation.AnnotationAnalyzer; + +import javax.tools.Diagnostic; + +public class L { + + public static void d(String msg, Object... args) { + printMessage(Diagnostic.Kind.NOTE, String.format(msg, args)); + } + + public static void d(Throwable t, String msg, Object... args) { + printMessage(Diagnostic.Kind.NOTE, + String.format(msg, args) + " " + ExceptionUtils.getStackTrace(t)); + } + + public static void e(String msg, Object... args) { + printMessage(Diagnostic.Kind.ERROR, String.format(msg, args)); + } + + public static void e(Throwable t, String msg, Object... args) { + printMessage(Diagnostic.Kind.ERROR, + String.format(msg, args) + " " + ExceptionUtils.getStackTrace(t)); + } + + private static void printMessage(Diagnostic.Kind kind, String message) { + ModelAnalyzer modelAnalyzer = ModelAnalyzer.getInstance(); + System.out.println("[" + kind.name() + "]: " + message); + if (modelAnalyzer instanceof AnnotationAnalyzer) { + ((AnnotationAnalyzer) modelAnalyzer).getProcessingEnv().getMessager() + .printMessage(kind, message); + if (kind == Diagnostic.Kind.ERROR) { + throw new RuntimeException("failure, see logs for details.\n" + message); + } + } else { + + if (kind == Diagnostic.Kind.ERROR) { + throw new RuntimeException(message); + } + } + } + +} diff --git a/tools/data-binding/compiler/src/main/java/android/databinding/tool/writer/AnnotationJavaFileWriter.java b/tools/data-binding/compiler/src/main/java/android/databinding/tool/writer/AnnotationJavaFileWriter.java new file mode 100644 index 0000000..4089905 --- /dev/null +++ b/tools/data-binding/compiler/src/main/java/android/databinding/tool/writer/AnnotationJavaFileWriter.java @@ -0,0 +1,52 @@ +/* + * 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 android.databinding.tool.writer; + +import org.apache.commons.io.IOUtils; + +import android.databinding.tool.util.L; + +import java.io.IOException; +import java.io.Writer; + +import javax.annotation.processing.ProcessingEnvironment; +import javax.tools.JavaFileObject; + +public class AnnotationJavaFileWriter extends JavaFileWriter { + private final ProcessingEnvironment mProcessingEnvironment; + + public AnnotationJavaFileWriter(ProcessingEnvironment processingEnvironment) { + mProcessingEnvironment = processingEnvironment; + } + + @Override + public void writeToFile(String canonicalName, String contents) { + Writer writer = null; + try { + L.d("writing file %s", canonicalName); + JavaFileObject javaFileObject = + mProcessingEnvironment.getFiler().createSourceFile(canonicalName); + writer = javaFileObject.openWriter(); + writer.write(contents); + } catch (IOException e) { + L.e(e, "Could not write to %s", canonicalName); + } finally { + if (writer != null) { + IOUtils.closeQuietly(writer); + } + } + } +} diff --git a/tools/data-binding/compiler/src/main/java/android/databinding/tool/writer/FlagSet.java b/tools/data-binding/compiler/src/main/java/android/databinding/tool/writer/FlagSet.java new file mode 100644 index 0000000..a5e799e --- /dev/null +++ b/tools/data-binding/compiler/src/main/java/android/databinding/tool/writer/FlagSet.java @@ -0,0 +1,142 @@ +/* + * 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 android.databinding.tool.writer; + +import java.util.Arrays; +import java.util.BitSet; + +/** + * Used for code generation. A BitSet can be converted into a flag set, + * which is basically a list of longs that can be divided into pieces. + */ +public class FlagSet { + public static final int sBucketSize = 64;// long + public final String type; + public final long[] buckets; + private String mLocalName; + private boolean mIsDynamic = false; + + public FlagSet(BitSet bitSet, int bucketCount) { + buckets = new long[bucketCount]; + for (int i = bitSet.nextSetBit(0); + i != -1; i = bitSet.nextSetBit(i + 1)) { + buckets[i / sBucketSize] |= 1 << (i % sBucketSize); + } + type = "long"; + } + + public FlagSet(long[] buckets) { + this.buckets = new long[buckets.length]; + System.arraycopy(buckets, 0, this.buckets, 0, buckets.length); + type = "long"; + } + + public FlagSet(long[] buckets, int minBucketCount) { + this.buckets = new long[Math.max(buckets.length, minBucketCount)]; + System.arraycopy(buckets, 0, this.buckets, 0, buckets.length); + type = "long"; + } + + public FlagSet(int... bits) { + int max = 0; + for (int i = 0 ; i < bits.length; i ++) { + max = Math.max(i, bits[i]); + } + buckets = new long[1 + (max / sBucketSize)]; + for (int x = 0 ; x < bits.length; x ++) { + final int i = bits[x]; + buckets[i / sBucketSize] |= 1 << (i % sBucketSize); + } + type = "long"; + } + + public boolean intersect(FlagSet other, int bucketIndex) { + return (buckets[bucketIndex] & other.buckets[bucketIndex]) != 0; + } + + public String getLocalName() { + return mLocalName; + } + + public void setLocalName(String localName) { + mLocalName = localName; + } + + public boolean hasLocalName() { + return mLocalName != null; + } + + public boolean isDynamic() { + return mIsDynamic; + } + + public void setDynamic(boolean isDynamic) { + mIsDynamic = isDynamic; + } + + public FlagSet andNot(FlagSet other) { + FlagSet result = new FlagSet(buckets); + final int min = Math.min(buckets.length, other.buckets.length); + for (int i = 0; i < min; i ++) { + result.buckets[i] &= ~(other.buckets[i]); + } + return result; + } + + public FlagSet or(FlagSet other) { + final FlagSet result = new FlagSet(buckets, other.buckets.length); + for (int i = 0; i < other.buckets.length; i ++) { + result.buckets[i] |= other.buckets[i]; + } + return result; + } + + public boolean isEmpty() { + for (int i = 0; i < buckets.length; i ++) { + if (buckets[i] != 0) { + return false; + } + } + return true; + } + + @Override + public String toString() { + StringBuilder sb = new StringBuilder(); + for (int i = 0; i < buckets.length; i ++) { + sb.append(Long.toBinaryString(buckets[i])).append(" "); + } + return sb.toString(); + } + + private long getBucket(int bucketIndex) { + if (bucketIndex >= buckets.length) { + return 0; + } + return buckets[bucketIndex]; + } + + public boolean bitsEqual(FlagSet other) { + final int max = Math.max(buckets.length, other.buckets.length); + for (int i = 0; i < max; i ++) { + if (getBucket(i) != other.getBucket(i)) { + return false; + } + } + return true; + } +} diff --git a/tools/data-binding/compiler/src/main/java/android/databinding/tool/writer/JavaFileWriter.java b/tools/data-binding/compiler/src/main/java/android/databinding/tool/writer/JavaFileWriter.java new file mode 100644 index 0000000..d4e0565 --- /dev/null +++ b/tools/data-binding/compiler/src/main/java/android/databinding/tool/writer/JavaFileWriter.java @@ -0,0 +1,35 @@ +/* + * 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 android.databinding.tool.writer; + +import org.apache.commons.io.FileUtils; + +import android.databinding.tool.util.L; + +import java.io.File; +import java.io.IOException; + +public abstract class JavaFileWriter { + public abstract void writeToFile(String canonicalName, String contents); + public void writeToFile(File exactPath, String contents) { + File parent = exactPath.getParentFile(); + parent.mkdirs(); + try { + L.d("writing file %s", exactPath.getAbsoluteFile()); + FileUtils.writeStringToFile(exactPath, contents); + } catch (IOException e) { + L.e(e, "Could not write to %s", exactPath); + } + } +} diff --git a/tools/data-binding/compiler/src/main/kotlin/android/databinding/tool/ext/ext.kt b/tools/data-binding/compiler/src/main/kotlin/android/databinding/tool/ext/ext.kt new file mode 100644 index 0000000..9d950b1 --- /dev/null +++ b/tools/data-binding/compiler/src/main/kotlin/android/databinding/tool/ext/ext.kt @@ -0,0 +1,80 @@ +/* + * 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 android.databinding.tool.ext + +import kotlin.properties.ReadOnlyProperty +import kotlin.properties.Delegates +import android.databinding.tool.ext.joinToCamelCase +import android.databinding.tool.ext.joinToCamelCaseAsVar +import android.databinding.tool.reflection.ModelAnalyzer +import android.databinding.tool.reflection.ModelClass +import android.databinding.tool.reflection.ModelAnalyzer + +private class LazyExt<K, T>(private val initializer: (k : K) -> T) : ReadOnlyProperty<K, T> { + private val mapping = hashMapOf<K, T>() + override fun get(thisRef: K, desc: PropertyMetadata): T { + val t = mapping.get(thisRef) + if (t != null) { + return t + } + val result = initializer(thisRef) + mapping.put(thisRef, result) + return result + } +} + +fun Delegates.lazy<K, T>(initializer: (k : K) -> T): ReadOnlyProperty<K, T> = LazyExt(initializer) + +public fun Class<*>.toJavaCode() : String { + val name = getName(); + if (name.startsWith('[')) { + val numArray = name.lastIndexOf('[') + 1; + val componentType : String; + when (name.charAt(numArray)) { + 'Z' -> componentType = "boolean" + 'B' -> componentType = "byte" + 'C' -> componentType = "char" + 'L' -> componentType = name.substring(numArray + 1, name.length() - 1).replace('$', '.'); + 'D' -> componentType = "double" + 'F' -> componentType = "float" + 'I' -> componentType = "int" + 'J' -> componentType = "long" + 'S' -> componentType = "short" + else -> componentType = name.substring(numArray) + } + val arrayComp = name.substring(0, numArray).replace("[", "[]"); + return componentType + arrayComp; + } else { + return name.replace("$", ".") + } +} + +public fun String.androidId() : String = this.split("/")[1] + +public fun String.toCamelCase() : String { + val split = this.split("_") + if (split.size == 0) return "" + if (split.size == 1) return split[0].capitalize() + return split.joinToCamelCase() +} + +public fun String.toCamelCaseAsVar() : String { + val split = this.split("_") + if (split.size == 0) return "" + if (split.size == 1) return split[0] + return split.joinToCamelCaseAsVar() +} + +public fun String.br() : String = + "BR.${if (this == "") "_all" else this}" diff --git a/tools/data-binding/compiler/src/main/kotlin/android/databinding/tool/ext/list_ext.kt b/tools/data-binding/compiler/src/main/kotlin/android/databinding/tool/ext/list_ext.kt new file mode 100644 index 0000000..12c8af2 --- /dev/null +++ b/tools/data-binding/compiler/src/main/kotlin/android/databinding/tool/ext/list_ext.kt @@ -0,0 +1,44 @@ +/* + * Copyright (C) 2014 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 android.databinding.tool.ext + +import android.databinding.tool.ext.toCamelCase +import android.databinding.tool.ext.toCamelCaseAsVar + +public fun List<String>.joinToCamelCase(): String = when(size()) { + 0 -> throw IllegalArgumentException("invalid section size, cannot be zero") + 1 -> this.get(0).toCamelCase() + else -> this.map {it.toCamelCase()}.joinToString("") +} + +public fun List<String>.joinToCamelCaseAsVar(): String = when(size()) { + 0 -> throw IllegalArgumentException("invalid section size, cannot be zero") + 1 -> this.get(0).toCamelCaseAsVar() + else -> get(0).toCamelCaseAsVar() + drop(1).joinToCamelCase() +} + +public fun Array<String>.joinToCamelCase(): String = when(size) { + 0 -> throw IllegalArgumentException("invalid section size, cannot be zero") + 1 -> this.get(0).toCamelCase() + else -> this.map {it.toCamelCase()}.joinToString("") +} + +public fun Array<String>.joinToCamelCaseAsVar(): String = when(size) { + 0 -> throw IllegalArgumentException("invalid section size, cannot be zero") + 1 -> this.get(0).toCamelCaseAsVar() + else -> get(0).toCamelCaseAsVar() + drop(1).joinToCamelCase() +}
\ No newline at end of file diff --git a/tools/data-binding/compiler/src/main/kotlin/android/databinding/tool/ext/node_ext.kt b/tools/data-binding/compiler/src/main/kotlin/android/databinding/tool/ext/node_ext.kt new file mode 100644 index 0000000..089e3c8 --- /dev/null +++ b/tools/data-binding/compiler/src/main/kotlin/android/databinding/tool/ext/node_ext.kt @@ -0,0 +1,56 @@ +/* + * Copyright (C) 2014 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 android.databinding.tool.ext + +import org.w3c.dom.Node +import org.w3c.dom.NodeList +import java.util.ArrayList + +public fun Node.getAndroidId() : String? = + getAttributes()?.getNamedItem("android:id")?.getNodeValue() + +public fun Node.getAndroidIdPath(includeRoot : Boolean) : List<String> { + val ids = arrayListOf<String>() + ids.add(getAndroidId()!!) + var parent : Node? = getParentNode() + while (parent != null && (includeRoot || parent?.getParentNode()?.getParentNode() != null)) { + val id = parent?.getAndroidId() + if (id != null) { + ids.add(id) + } + parent = parent?.getParentNode() + } + return ids +} + +public fun NodeList.forEach( f : (Node) -> Unit ) { + val cnt = getLength() + if (cnt == 0) return + for (i in 0..cnt - 1) { + f(item(i)) + } +} +public fun NodeList.toArrayList() : ArrayList<Node> { + val cnt = getLength() + val arrayList = arrayListOf<Node>() + for (i in 0..cnt - 1) { + arrayList.add(item(i)) + } + return arrayList +} + + diff --git a/tools/data-binding/compiler/src/main/kotlin/android/databinding/tool/util/Log.kt b/tools/data-binding/compiler/src/main/kotlin/android/databinding/tool/util/Log.kt new file mode 100644 index 0000000..fb6eb35 --- /dev/null +++ b/tools/data-binding/compiler/src/main/kotlin/android/databinding/tool/util/Log.kt @@ -0,0 +1,27 @@ +/* + * Copyright (C) 2014 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 android.databinding.tool.util + +object Log { + fun d(s : String) { + System.out.println("[debug] $s") + } + + fun d(f : () -> String) { + System.out.println("[debug] ${f()}") + } +}
\ No newline at end of file diff --git a/tools/data-binding/compiler/src/main/kotlin/android/databinding/tool/util/ParserHelper.kt b/tools/data-binding/compiler/src/main/kotlin/android/databinding/tool/util/ParserHelper.kt new file mode 100644 index 0000000..6972448 --- /dev/null +++ b/tools/data-binding/compiler/src/main/kotlin/android/databinding/tool/util/ParserHelper.kt @@ -0,0 +1,27 @@ +/* + * Copyright (C) 2015 The Android Open Source Project + * Licensed under the Apache License, Version 2.0 (the "License"); + * you may not use this file except in compliance with the License. + * You may obtain a copy of the License at + * http://www.apache.org/licenses/LICENSE-2.0 + * Unless required by applicable law or agreed to in writing, software + * distributed under the License is distributed on an "AS IS" BASIS, + * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. + * See the License for the specific language governing permissions and + * limitations under the License. + */ + +package android.databinding.tool.util + +object ParserHelper { + public fun toClassName(name:String) : String { + + return stripExtension(name).split("[_-]").map { it.capitalize() }.join("") + } + + + public fun stripExtension(name : String) : String { + val dot = name.indexOf(".") + return if (dot == -1) name else name.substring(0, name.indexOf(".")) + } +}
\ No newline at end of file diff --git a/tools/data-binding/compiler/src/main/kotlin/android/databinding/tool/util/XmlEditor.kt b/tools/data-binding/compiler/src/main/kotlin/android/databinding/tool/util/XmlEditor.kt new file mode 100644 index 0000000..bea9df5 --- /dev/null +++ b/tools/data-binding/compiler/src/main/kotlin/android/databinding/tool/util/XmlEditor.kt @@ -0,0 +1,242 @@ +/* + * Copyright (C) 2014 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 android.databinding.tool.util + +import java.io.File +import org.antlr.v4.runtime.ANTLRInputStream +import java.io.FileReader +import android.databinding.parser.XMLLexer +import org.antlr.v4.runtime.CommonTokenStream +import android.databinding.parser.XMLParser +import android.databinding.parser.log +import android.databinding.parser.XMLParserBaseVisitor +import android.databinding.parser.Position +import android.databinding.parser.toPosition +import android.databinding.parser.toEndPosition +import java.util.Comparator +import com.google.common.base.Preconditions +import java.util.ArrayList + +/** + * Ugly inefficient class to strip unwanted tags from XML. + * Band-aid solution to unblock development + */ +object XmlEditor { + val reservedElementNames = arrayListOf("variable", "import") + var rootNodeContext: XMLParser.ElementContext? = null + var rootNodeHasTag = false + data class LayoutXmlElements(val start: Position, val end: Position, + val insertionPoint: Position, + val isTag: kotlin.Boolean, val isReserved: kotlin.Boolean, + val attributes: MutableList<LayoutXmlElements>?) + + val visitor = object : XMLParserBaseVisitor<MutableList<LayoutXmlElements>>() { + override fun visitAttribute(ctx: XMLParser.AttributeContext): MutableList<LayoutXmlElements>? { + log { "attr:${ctx.attrName.getText()} ${ctx.attrValue.getText()} . parent: ${ctx.getParent()}" } + if (ctx.getParent() == rootNodeContext && ctx.attrName.getText() == "android:tag") { + rootNodeHasTag = true + } + val isTag = ctx.attrName.getText().equals("android:tag") + if (isTag || ctx.attrName.getText().startsWith("bind:") || + (ctx.attrValue.getText().startsWith("\"@{") && ctx.attrValue.getText().endsWith("}\"")) || + (ctx.attrValue.getText().startsWith("'@{") && ctx.attrValue.getText().endsWith("}'"))) { + return arrayListOf(LayoutXmlElements(ctx.getStart().toPosition(), + ctx.getStop().toEndPosition(), ctx.getStart().toPosition(), isTag, false, null)) + } + + //log{"visiting attr: ${ctx.getText()} at location ${ctx.getStart().toS()} ${ctx.getStop().toS()}"} + return super<XMLParserBaseVisitor>.visitAttribute(ctx) + } + + override fun visitElement(ctx: XMLParser.ElementContext): MutableList<LayoutXmlElements>? { + log { "elm ${ctx.elmName.getText()} || ${ctx.Name()} paren : ${ctx.getParent()}" } + if (rootNodeContext == null) { + rootNodeContext = ctx + } + val insertionPoint : Position + if (ctx.content() == null) { + insertionPoint = ctx.getStop().toPosition() + insertionPoint.charIndex; + } else { + insertionPoint = ctx.content().getStart().toPosition() + insertionPoint.charIndex -= 2; + } + if (reservedElementNames.contains(ctx.elmName?.getText()) || ctx.elmName.getText().startsWith("bind:")) { + return arrayListOf(LayoutXmlElements(ctx.getStart().toPosition(), + ctx.getStop().toEndPosition(), insertionPoint, false, true, arrayListOf())); + } + val elements = super<XMLParserBaseVisitor>.visitElement(ctx); + if (elements != null && !elements.isEmpty()) { + val attributes : MutableList<LayoutXmlElements> = arrayListOf(); + val others : MutableList<LayoutXmlElements> = arrayListOf(); + elements.forEach { + if (it.attributes == null) { + attributes.add(it); + } else { + others.add(it); + } + } + if (attributes.isEmpty()) { + return elements; + } else { + val element = LayoutXmlElements(ctx.getStart().toPosition(), + ctx.getStop().toEndPosition(), insertionPoint, false, false, attributes) + others.add(0, element); + return others; + } + } else { + return elements; + } + } + + override fun defaultResult(): MutableList<LayoutXmlElements>? = arrayListOf() + + override fun aggregateResult(aggregate: MutableList<LayoutXmlElements>?, nextResult: MutableList<LayoutXmlElements>?): MutableList<LayoutXmlElements>? = + if (aggregate == null) { + nextResult + } else if (nextResult == null) { + aggregate + } else { + aggregate.addAll(nextResult) + aggregate + } + } + + fun strip(f: File, newTag: String? = null): String? { + rootNodeContext = null //clear it + rootNodeHasTag = false + val inputStream = ANTLRInputStream(FileReader(f)) + val lexer = XMLLexer(inputStream) + val tokenStream = CommonTokenStream(lexer) + val parser = XMLParser(tokenStream) + val expr = parser.document() + val parsedExpr = expr.accept(visitor) + if (parsedExpr.isEmpty()) { + return null//nothing to strip + } + Preconditions.checkNotNull(rootNodeContext, "Cannot find root node for ${f.getName()}") + Preconditions.checkState(rootNodeHasTag == false, """You cannot set a tag in the layout + root if you are using binding. Invalid file: ${f}""") + val rootNodeBounds = Pair(rootNodeContext!!.getStart().toPosition(), + rootNodeContext!!.getStop().toEndPosition()) + + log { "root node bounds: ${rootNodeBounds}" } + val out = StringBuilder() + val lines = f.readLines("utf-8") + + lines.forEach { out.appendln(it) } + + // TODO we probably don't need to sort + val sorted = parsedExpr.sortBy(object : Comparator<LayoutXmlElements> { + override fun compare(o1: LayoutXmlElements, o2: LayoutXmlElements): Int { + val lineCmp = o1.start.line.compareTo(o2.start.charIndex) + if (lineCmp != 0) { + return lineCmp + } + return o1.start.line.compareTo(o2.start.charIndex) + } + }) + var lineStarts = arrayListOf(0) + lines.withIndex().forEach { + if (it.index > 0) { + lineStarts.add(lineStarts[it.index - 1] + lines[it.index - 1].length() + 1) + } + } + + val separator = System.lineSeparator().charAt(0) + val noTag : ArrayList<Pair<String, LayoutXmlElements>> = ArrayList() + var bindingIndex = 0 + val rootNodeEnd = toIndex(lineStarts, rootNodeContext!!.content().getStart().toPosition()) + sorted.forEach { + if (it.isReserved) { + log {"Replacing reserved tag at ${it.start} to ${it.end}"} + replace(out, lineStarts, it, "", separator); + } else if (it.attributes != null) { + log {"Found attribute for tag at ${it.start} to ${it.end}"} + if (it.attributes.size() == 1 && it.attributes.get(0).isTag) { + log {"only android:tag"} + // no binding, just tag -- don't replace anything + } else { + var replaced = false + val tag : String + if (toIndex(lineStarts, it.start) < rootNodeEnd) { + tag = "" + } else { + val index = bindingIndex++; + tag = "android:tag=\"bindingTag${index}\""; + } + it.attributes.forEach { + if (!replaced && tagWillFit(it.start, it.end, tag)) { + replace(out, lineStarts, it, tag, separator) + replaced = true; + } else { + replace(out, lineStarts, it, "", separator) + } + } + if (!replaced && !tag.isEmpty()) { + log {"Could not find place for ${tag}"} + noTag.add(0, Pair(tag, it)) + } + } + } + } + + noTag.forEach { + val element = it.second + val tag = it.first; + + val insertionPoint = toIndex(lineStarts, element.insertionPoint) + out.insert(insertionPoint, " ${tag}") + } + + Log.d{"new tag to set: $newTag"} + if (newTag != null) { + Preconditions.checkState(rootNodeBounds.first.line != rootNodeBounds.second.line, + """The root tag should be multi line to add the tag. ${f.getName()}""") + val line = rootNodeBounds.first.line + out.insert(lineStarts[line] + lines[line].length(), """ android:tag = "$newTag" """) + } + + + return out.toString() + } + + fun tagWillFit(start: Position, end: Position, tag: String) : kotlin.Boolean { + if (start.line != end.line) { + return end.charIndex >= tag.length(); + } + return (end.charIndex - start.charIndex >= tag.length()); + } + + fun replace(out : StringBuilder, lineStarts : ArrayList<kotlin.Int>, element : LayoutXmlElements, + tag : String, separator : kotlin.Char) { + val posStart = toIndex(lineStarts, element.start) + val posEnd = toIndex(lineStarts, element.end) + log {"replacing '${out.substring(posStart, posEnd)}' with '${tag}'"} + val spaceEnd = posEnd - tag.length(); + for ( i in posStart..(spaceEnd - 1)) { + if (out.charAt(i) != separator) { + out.setCharAt(i, ' ') + } + } + out.replace(spaceEnd, posEnd, tag); + } + + fun toIndex(lineStarts : ArrayList<kotlin.Int>, pos : Position) : kotlin.Int { + return lineStarts[pos.line] + pos.charIndex; + } +} diff --git a/tools/data-binding/compiler/src/main/kotlin/android/databinding/tool/writer/DataBinderWriter.kt b/tools/data-binding/compiler/src/main/kotlin/android/databinding/tool/writer/DataBinderWriter.kt new file mode 100644 index 0000000..f7afc93 --- /dev/null +++ b/tools/data-binding/compiler/src/main/kotlin/android/databinding/tool/writer/DataBinderWriter.kt @@ -0,0 +1,60 @@ +/* + * 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 android.databinding.tool.writer + +import android.databinding.tool.LayoutBinder + +class DataBinderWriter(val pkg: String, val projectPackage: String, val className: String, + val layoutBinders : List<LayoutBinder>, val minSdk : kotlin.Int) { + fun write() = + kcode("") { + nl("package $pkg;") + nl("import $projectPackage.BR;") + nl("class $className {") { + tab("public android.databinding.ViewDataBinding getDataBinder(android.view.View view, int layoutId) {") { + tab("switch(layoutId) {") { + layoutBinders.groupBy{it.getLayoutname()}.forEach { + tab("case ${it.value.first!!.getModulePackage()}.R.layout.${it.value.first!!.getLayoutname()}:") { + if (it.value.size() == 1) { + tab("return ${it.value.first!!.getPackage()}.${it.value.first!!.getImplementationName()}.bind(view);") + } else { + // we should check the tag to decide which layout we need to inflate + tab("{") { + tab("final Object tag = view.getTag();") + tab("if(tag == null) throw new java.lang.RuntimeException(\"view must have a tag\");") + it.value.forEach { + tab("if (\"${it.getTag()}\".equals(tag)) {") { + tab("return new ${it.getPackage()}.${it.getImplementationName()}(view);") + } tab("}") + } + }tab("}") + } + + } + } + } + tab("}") + tab("return null;") + } + tab("}") + + tab("public int getId(String key) {") { + tab("return BR.getId(key);") + } tab("}") + + tab("final static int TARGET_MIN_SDK = ${minSdk};") + } + nl("}") + }.generate() +}
\ No newline at end of file diff --git a/tools/data-binding/compiler/src/main/kotlin/android/databinding/tool/writer/KCode.kt b/tools/data-binding/compiler/src/main/kotlin/android/databinding/tool/writer/KCode.kt new file mode 100644 index 0000000..f09d018 --- /dev/null +++ b/tools/data-binding/compiler/src/main/kotlin/android/databinding/tool/writer/KCode.kt @@ -0,0 +1,169 @@ +/* + * 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 android.databinding.tool.writer + +import java.util.BitSet + +class KCode (private val s : String? = null){ + + private var sameLine = false + + private val lineSeparator = System.getProperty("line.separator") + + class Appendix(val glue : String, val code : KCode) + + private val nodes = arrayListOf<Any>() + + class object { + private val cachedIndentations = BitSet() + private val indentCache = arrayListOf<String>() + fun indent(n: Int): String { + if (cachedIndentations.get(n)) { + return indentCache.get(n) + } + val s = (0..n-1).fold(""){prev, next -> "${prev} "} + cachedIndentations.set(n, true ) + while (indentCache.size() <= n) { + indentCache.add(""); + } + indentCache.set(n, s) + return s + } + } + + fun isNull(kcode : KCode?) = kcode == null || (kcode.nodes.isEmpty() && (kcode.s == null || kcode.s.trim() == "")) + + fun tab(vararg codes : KCode?) : KCode { + codes.forEach { tab(it) } + return this + } + + fun tab(codes : Collection<KCode?> ) : KCode { + codes.forEach { tab(it) } + return this + } + + fun tab(s : String?, init : (KCode.() -> Unit)? = null) : KCode { + val c = KCode(s) + if (init != null) { + c.init() + } + return tab(c) + } + + private fun tab(c : KCode?) : KCode { + if (isNull(c)) { + return this + } + nodes.add(c) + return this + } + + fun nls(vararg codes : KCode?) : KCode { + codes.forEach { nl(it) } + return this + } + + fun nls(codes : Collection<KCode?>) : KCode { + codes.forEach { nl(it) } + return this + } + + fun nl(c : KCode?) : KCode { + if (isNull(c)) { + return this + } + nodes.add(c) + c!!.sameLine = true + return this + } + + fun nl(s : String?, init : (KCode.() -> Unit)? = null) : KCode { + val c = KCode(s) + if (init != null) { + c.init() + } + return nl(c) + } + + fun apps(glue : String = "", vararg codes : KCode?) : KCode { + codes.forEach { app(glue, it)} + return this + } + + fun apps(glue : String = "", codes : Collection<KCode?>) : KCode { + codes.forEach { app(glue, it)} + return this + } + + fun app(glue : String = "", c : KCode?) : KCode { + if (isNull(c)) { + return this + } + nodes.add(Appendix(glue, c!!)) + return this + } + + fun app(s : String) : KCode { + val c = KCode(s) + return app("", c) + } + + fun app(glue : String = "", s : String?, init : (KCode.() -> Unit)? = null) : KCode { + val c = KCode(s) + if (init != null) { + c.init() + } + return app(glue, c) + } + + + fun toS(n : Int, sb : StringBuilder) { + if (s != null) { + sb.append(s) + } + val newlineFirstNode = s != null || (nodes.isNotEmpty() && nodes.first() is Appendix) + var addedChild = false + nodes.forEach { when(it) { + is Appendix -> { + sb.append(it.glue) + it.code.toS(n, sb) + } + is KCode -> { + val childTab = n + (if(it.sameLine) 0 else 1) + if (addedChild || newlineFirstNode) { + sb.append(lineSeparator) + sb.append("${indent(childTab)}") + } + it.toS(childTab, sb) + addedChild = true + } + } } + + } + + fun generate() : String { + val sb = StringBuilder() + toS(0, sb) + return sb.toString() + } +} + +fun kcode(s : String?, init : (KCode.() -> Unit)? = null) : KCode { + val c = KCode(s) + if (init != null) { + c.init() + } + return c +}
\ No newline at end of file diff --git a/tools/data-binding/compiler/src/main/kotlin/android/databinding/tool/writer/LayoutBinderWriter.kt b/tools/data-binding/compiler/src/main/kotlin/android/databinding/tool/writer/LayoutBinderWriter.kt new file mode 100644 index 0000000..61f0d2d --- /dev/null +++ b/tools/data-binding/compiler/src/main/kotlin/android/databinding/tool/writer/LayoutBinderWriter.kt @@ -0,0 +1,878 @@ +/* + * 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 android.databinding.tool.writer + +import android.databinding.tool.LayoutBinder +import android.databinding.tool.expr.Expr +import kotlin.properties.Delegates +import android.databinding.tool.ext.joinToCamelCaseAsVar +import android.databinding.tool.BindingTarget +import android.databinding.tool.expr.IdentifierExpr +import android.databinding.tool.util.Log +import java.util.BitSet +import android.databinding.tool.expr.ExprModel +import java.util.Arrays +import android.databinding.tool.expr.TernaryExpr +import android.databinding.tool.expr.FieldAccessExpr +import android.databinding.tool.expr.ComparisonExpr +import android.databinding.tool.expr.GroupExpr +import android.databinding.tool.expr.MathExpr +import android.databinding.tool.expr.MethodCallExpr +import android.databinding.tool.expr.StaticIdentifierExpr +import android.databinding.tool.expr.SymbolExpr +import android.databinding.tool.ext.androidId +import android.databinding.tool.ext.lazy +import android.databinding.tool.ext.br +import android.databinding.tool.expr.ResourceExpr +import android.databinding.tool.expr.BracketExpr +import android.databinding.tool.reflection.Callable +import android.databinding.tool.expr.CastExpr +import android.databinding.tool.reflection.ModelAnalyzer +import java.util.HashMap + +fun String.stripNonJava() = this.split("[^a-zA-Z0-9]").map{ it.trim() }.joinToCamelCaseAsVar() + +class ExprModelExt { + val usedFieldNames = hashSetOf<String>() + val localizedFlags = arrayListOf<FlagSet>() + + fun localizeFlag(set : FlagSet, name:String) : FlagSet { + localizedFlags.add(set) + val result = getUniqueFieldName(name) + set.setLocalName(result) + return set + } + + fun getUniqueFieldName(base : String) : String { + var candidate = base + var i = 0 + while (usedFieldNames.contains(candidate)) { + i ++ + candidate = base + i + } + usedFieldNames.add(candidate) + return candidate + } +} + +val ExprModel.ext by Delegates.lazy { target : ExprModel -> + ExprModelExt() +} + +fun ExprModel.getUniqueFieldName(base : String) : String = ext.getUniqueFieldName(base) + +fun ExprModel.localizeFlag(set : FlagSet, base : String) : FlagSet = ext.localizeFlag(set, base) + +val BindingTarget.readableUniqueName by Delegates.lazy { target: BindingTarget -> + val variableName : String + if (target.getId() == null) { + variableName = "boundView" + target.getTag() + } else { + variableName = target.getId().androidId().stripNonJava() + } + target.getModel().ext.getUniqueFieldName(variableName) +} + +fun BindingTarget.superConversion(variable : String) : String { + if (isBinder()) { + return "${getViewClass()}.bind(${variable})" + } else if (getResolvedType() != null && getResolvedType().extendsViewStub()) { + return "new android.databinding.ViewStubProxy((android.view.ViewStub) ${variable})" + } else { + return "(${interfaceType}) ${variable}" + } +} + +val BindingTarget.fieldName by Delegates.lazy { target : BindingTarget -> + if (target.getFieldName() == null) { + if (target.getId() == null) { + target.setFieldName("m${target.readableUniqueName.capitalize()}") + } else { + target.androidId.stripNonJava(); + target.setFieldName(target.readableUniqueName); + } + } + target.getFieldName(); +} + +val BindingTarget.getterName by Delegates.lazy { target : BindingTarget -> + "get${target.readableUniqueName.capitalize()}" +} + +val BindingTarget.androidId by Delegates.lazy { target : BindingTarget -> + "R.id.${target.getId().androidId()}" +} + +val BindingTarget.interfaceType by Delegates.lazy { target : BindingTarget -> + if (target.getResolvedType() != null && target.getResolvedType().extendsViewStub()) { + "android.databinding.ViewStubProxy" + } else { + target.getInterfaceType() + } +} + +val Expr.readableUniqueName by Delegates.lazy { expr : Expr -> + Log.d { "readableUniqueName for ${expr.getUniqueKey()}" } + val stripped = "${expr.getUniqueKey().stripNonJava()}" + expr.getModel().ext.getUniqueFieldName(stripped) +} + +val Expr.readableName by Delegates.lazy { expr : Expr -> + Log.d { "readableUniqueName for ${expr.getUniqueKey()}" } + "${expr.getUniqueKey().stripNonJava()}" +} + +val Expr.fieldName by Delegates.lazy { expr : Expr -> + "m${expr.readableName.capitalize()}" +} + +val Expr.hasFlag by Delegates.lazy { expr : Expr -> + expr.getId() < expr.getModel().getInvalidateableFieldLimit() +} + +val Expr.localName by Delegates.lazy { expr : Expr -> + if(expr.isVariable()) expr.fieldName else "${expr.readableUniqueName}" +} + +val Expr.setterName by Delegates.lazy { expr : Expr -> + "set${expr.readableName.capitalize()}" +} + +val Expr.onChangeName by Delegates.lazy { expr : Expr -> + "onChange${expr.readableUniqueName.capitalize()}" +} + +val Expr.getterName by Delegates.lazy { expr : Expr -> + "get${expr.readableName.capitalize()}" +} + +val Expr.dirtyFlagName by Delegates.lazy { expr : Expr -> + "sFlag${expr.readableUniqueName.capitalize()}" +} + +val Expr.shouldReadFlagName by Delegates.lazy { expr : Expr -> + "sFlagRead${expr.readableUniqueName.capitalize()}" +} + +val Expr.invalidateFlagName by Delegates.lazy { expr : Expr -> + "sFlag${expr.readableUniqueName.capitalize()}Invalid" +} + +val Expr.conditionalFlagPrefix by Delegates.lazy { expr : Expr -> + "sFlag${expr.readableUniqueName.capitalize()}Is" +} + + +fun Expr.toCode(full : Boolean = false) : KCode { + val it = this + if (isDynamic() && !full) { + return kcode(localName) + } + return when (it) { + is ComparisonExpr -> kcode("") { + app("", it.getLeft().toCode()) + app(it.getOp()) + app("", it.getRight().toCode()) + } + is FieldAccessExpr -> kcode("") { + app("", it.getChild().toCode()) + if (it.getGetter().type == Callable.Type.FIELD) { + app(".", it.getGetter().name) + } else { + app(".", it.getGetter().name).app("()") + } + } + is GroupExpr -> kcode("(").app("", it.getWrapped().toCode()).app(")") + is StaticIdentifierExpr -> kcode(it.getResolvedType().toJavaCode()) + is IdentifierExpr -> kcode(it.localName) + is MathExpr -> kcode("") { + app("", it.getLeft().toCode()) + app(it.getOp()) + app("", it.getRight().toCode()) + } + is MethodCallExpr -> kcode("") { + app("", it.getTarget().toCode()) + app(".", it.getGetter().name) + app("(") + var first = true + it.getArgs().forEach { + apps(if (first) "" else ",", it.toCode()) + first = false + } + app(")") + } + is SymbolExpr -> kcode(it.getText()) // TODO + is TernaryExpr -> kcode("") { + app("", it.getPred().toCode()) + app("?", it.getIfTrue().toCode()) + app(":", it.getIfFalse().toCode()) + } + is ResourceExpr -> kcode("") { + app("", it.toJava()) + } + is BracketExpr -> kcode("") { + app("", it.getTarget().toCode()) + val bracketType = it.getAccessor(); + when (bracketType) { + BracketExpr.BracketAccessor.ARRAY -> { + app("[", it.getArg().toCode()) + app("]") + } + BracketExpr.BracketAccessor.LIST -> { + app(".get(") + if (it.argCastsInteger()) { + app("(Integer)") + } + app("", it.getArg().toCode()) + app(")") + } + BracketExpr.BracketAccessor.MAP -> { + app(".get(", it.getArg().toCode()) + app(")") + } + } + } + is CastExpr -> kcode("") { + app("(", it.getCastType()) + app(") ", it.getCastExpr().toCode()) + } + else -> kcode("//NOT IMPLEMENTED YET") + } + +} + +fun Expr.isVariable() = this is IdentifierExpr && this.isDynamic() + +fun Expr.conditionalFlagName(output : Boolean, suffix : String) = "${dirtyFlagName}_${output}$suffix" + + +val Expr.dirtyFlagSet by Delegates.lazy { expr : Expr -> + FlagSet(expr.getInvalidFlags(), expr.getModel().getFlagBucketCount()) +} + +val Expr.invalidateFlagSet by Delegates.lazy { expr : Expr -> + FlagSet(expr.getId()) +} + +val Expr.shouldReadFlagSet by Delegates.lazy { expr : Expr -> + FlagSet(expr.getShouldReadFlags(), expr.getModel().getFlagBucketCount()) +} + +val Expr.conditionalFlags by Delegates.lazy { expr : Expr -> + arrayListOf(FlagSet(expr.getRequirementFlagIndex(false)), + FlagSet(expr.getRequirementFlagIndex(true))) +} + +fun Expr.getRequirementFlagSet(expected : Boolean) : FlagSet = conditionalFlags[if(expected) 1 else 0] + +fun FlagSet.notEmpty(cb : (suffix : String, value : Long) -> Unit) { + buckets.withIndex().forEach { + if (it.value != 0L) { + cb(getWordSuffix(it.index), buckets[it.index]) + } + } +} + +fun FlagSet.getBitSuffix(bitIndex : Int) : String { + val word = bitIndex / FlagSet.sBucketSize + return getWordSuffix(word) +} + +fun FlagSet.getWordSuffix(wordIndex : Int) : String { + return if(wordIndex == 0) "" else "_${wordIndex}" +} + +fun FlagSet.localValue(bucketIndex : Int) = + if (getLocalName() == null) binaryCode(bucketIndex) + else "${getLocalName()}${getWordSuffix(bucketIndex)}" + +fun FlagSet.binaryCode(bucketIndex : Int) = longToBinary(buckets[bucketIndex]) + + +fun longToBinary(l : Long) = + "0b${java.lang.Long.toBinaryString(l)}L" + +fun <T> FlagSet.mapOr(other : FlagSet, cb : (suffix : String, index : Int) -> T) : List<T> { + val min = Math.min(buckets.size(), other.buckets.size()) + val result = arrayListOf<T>() + for (i in 0..(min - 1)) { + // if these two can match by any chance, call the callback + if (intersect(other, i)) { + result.add(cb(getWordSuffix(i), i)) + } + } + return result +} + +class LayoutBinderWriter(val layoutBinder : LayoutBinder) { + val model = layoutBinder.getModel() + val indices = HashMap<BindingTarget, kotlin.Int>() + val mDirtyFlags by Delegates.lazy { + val fs = FlagSet(BitSet(), model.getFlagBucketCount()); + Arrays.fill(fs.buckets, -1) + fs.setDynamic(true) + model.localizeFlag(fs, "mDirtyFlags") + fs + } + + val dynamics by Delegates.lazy { model.getExprMap().values().filter { it.isDynamic() } } + val className = layoutBinder.getImplementationName() + + val identifiers by Delegates.lazy { + dynamics.filter { it is IdentifierExpr } + } + + val baseClassName = "${layoutBinder.getClassName()}" + + val includedBinders by Delegates.lazy { + layoutBinder.getBindingTargets().filter { it.isBinder() } + } + + val variables by Delegates.lazy { + model.getExprMap().values().filterIsInstance(javaClass<IdentifierExpr>()).filter { it.isVariable() } + } + + val usedVariables by Delegates.lazy { + variables.filter {it.isUsed()} + } + + public fun write() : String { + layoutBinder.resolveWhichExpressionsAreUsed() + calculateIndices(); + return kcode("package ${layoutBinder.getPackage()};") { + nl("import ${layoutBinder.getModulePackage()}.R;") + nl("import ${layoutBinder.getModulePackage()}.BR;") + nl("import android.view.View;") + val classDeclaration : String + if (layoutBinder.hasVariations()) { + classDeclaration = "${className} extends ${baseClassName}" + } else { + classDeclaration = "${className} extends android.databinding.ViewDataBinding" + } + nl("public class ${classDeclaration} {") { + tab(declareIncludeViews()) + tab(declareViews()) + tab(declareVariables()) + tab(declareConstructor()) + tab(declareInvalidateAll()) + tab(declareLog()) + tab(declareSetVariable()) + tab(variableSettersAndGetters()) + tab(onFieldChange()) + + tab(executePendingBindings()) + + tab(declareDirtyFlags()) + if (!layoutBinder.hasVariations()) { + tab(declareFactories()) + } + } + nl("}") + tab(flagMapping()) + tab("//end") + }.generate() + } + fun calculateIndices() : Unit { + val numTaggedViews = layoutBinder.getBindingTargets(). + filter{it.isUsed() && it.getTag() != null}.count() + layoutBinder.getBindingTargets().filter{ it.isUsed() && it.getTag() != null }.forEach { + indices.put(it, Integer.parseInt(it.getTag())); + } + layoutBinder.getBindingTargets().filter{ it.isUsed() && it.getTag() == null && it.getId() != null }.withIndex().forEach { + indices.put(it.value, it.index + numTaggedViews); + } + } + fun declareIncludeViews() = kcode("") { + nl("private static final android.util.SparseIntArray sIncludes;") + nl("private static final android.util.SparseIntArray sViewsWithIds;") + nl("static {") { + val hasBinders = layoutBinder.getBindingTargets().firstOrNull{ it.isUsed() && it.isBinder()} != null + if (!hasBinders) { + tab("sIncludes = null;") + } else { + tab("sIncludes = new android.util.SparseIntArray();") + layoutBinder.getBindingTargets().filter{ it.isUsed() && it.isBinder()}.forEach { + tab("sIncludes.put(${it.androidId}, ${indices.get(it)});") + } + } + val hasViewsWithIds = layoutBinder.getBindingTargets().firstOrNull{ + it.isUsed() && (!it.supportsTag() || (it.getId() != null && it.getTag() == null)) + } != null + if (!hasViewsWithIds) { + tab("sViewsWithIds = null;") + } else { + tab("sViewsWithIds = new android.util.SparseIntArray();") + layoutBinder.getBindingTargets().filter{ + it.isUsed() && (!it.supportsTag() || (it.getId() != null && it.getTag() == null)) + }.forEach { + tab("sViewsWithIds.put(${it.androidId}, ${indices.get(it)});") + } + } + } + nl("}") + } + fun declareConstructor() = kcode("") { + val viewCount = layoutBinder.getBindingTargets().filter{it.isUsed()}.count() + if (layoutBinder.hasVariations()) { + nl("") + nl("public ${className}(View root) {") { + tab("this(root, mapChildViews(root, ${viewCount}, sIncludes, sViewsWithIds));") + } + nl("}") + nl("private ${className}(View root, View[] views) {") { + tab("super(root, ${model.getObservables().size()}") { + layoutBinder.getBindingTargets().filter { it.getId() != null }.forEach { + tab(", ${fieldConversion(it)}") + } + tab(");") + } + } + } else { + nl("${baseClassName}(View root) {") { + tab("super(root, ${model.getObservables().size()});") + tab("final View[] views = mapChildViews(root, ${viewCount}, sIncludes, sViewsWithIds);") + } + } + val taggedViews = layoutBinder.getBindingTargets().filter{it.isUsed()} + taggedViews.forEach { + if (!layoutBinder.hasVariations() || it.getId() == null) { + tab("this.${it.fieldName} = ${fieldConversion(it)};") + } + if (!it.isBinder()) { + if (it.getResolvedType() != null && it.getResolvedType().extendsViewStub()) { + tab("this.${it.fieldName}.setContainingBinding(this);") + } + if (it.supportsTag() && it.getTag() != null) { + val originalTag = it.getOriginalTag(); + var tagValue = "null" + if (originalTag != null) { + tagValue = "\"${originalTag}\"" + if (originalTag.startsWith("@")) { + var packageName = layoutBinder.getModulePackage() + if (originalTag.startsWith("@android:")) { + packageName = "android" + } + val slashIndex = originalTag.indexOf('/') + val resourceId = originalTag.substring(slashIndex + 1) + tagValue = "root.getResources().getString(${packageName}.R.string.${resourceId})" + } + } + tab("this.${it.fieldName}.setTag(${tagValue});") + } + } + } + tab("invalidateAll();"); + nl("}") + } + + fun fieldConversion(target : BindingTarget) : String { + val index = indices.get(target) + if (!target.isUsed()) { + return "null" + } else { + val variableName: String + if (index == null) { + variableName = "root"; + } else { + variableName = "views[${index}]" + } + return target.superConversion(variableName) + } + } + + fun declareInvalidateAll() = kcode("") { + nl("@Override") + nl("public void invalidateAll() {") { + val bs = BitSet() + bs.set(0, model.getInvalidateableFieldLimit()) + val fs = FlagSet(bs, mDirtyFlags.buckets.size()) + for (i in (0..(mDirtyFlags.buckets.size() - 1))) { + tab("${mDirtyFlags.localValue(i)} = ${fs.localValue(i)};") + } + includedBinders.filter{it.isUsed()}.forEach { binder -> + tab("${binder.fieldName}.invalidateAll();") + } + } + nl("}") + } + + fun declareSetVariable() = kcode("") { + nl("public boolean setVariable(int variableId, Object variable) {") { + tab("switch(variableId) {") { + usedVariables.forEach { + tab ("case ${it.getName().br()} :") { + tab("${it.setterName}((${it.getResolvedType().toJavaCode()}) variable);") + tab("return true;") + } + } + } + tab("}") + tab("return false;") + } + nl("}") + } + + fun declareLog() = kcode("") { + nl("private void log(String msg, long i) {") { + tab("""android.util.Log.d("BINDER", msg + ":" + Long.toHexString(i));""") + } + nl("}") + } + + fun variableSettersAndGetters() = kcode("") { + variables.filterNot{it.isUsed()}.forEach { + nl("public void ${it.setterName}(${it.getResolvedType().toJavaCode()} ${it.readableUniqueName}) {") { + tab("// not used, ignore") + } + nl("}") + nl("") + nl("public ${it.getResolvedType().toJavaCode()} ${it.getterName}() {") { + tab("return ${it.getDefaultValue()};") + } + nl("}") + } + usedVariables.forEach { + if (it.getUserDefinedType() != null) { + nl("public void ${it.setterName}(${it.getResolvedType().toJavaCode()} ${it.readableUniqueName}) {") { + if (it.isObservable()) { + tab("updateRegistration(${it.getId()}, ${it.readableUniqueName});"); + } + tab("this.${it.fieldName} = ${it.readableUniqueName};") + // set dirty flags! + val flagSet = it.invalidateFlagSet + mDirtyFlags.mapOr(flagSet) { suffix, index -> + tab("${mDirtyFlags.getLocalName()}$suffix |= ${flagSet.localValue(index)};") + } + tab("super.requestRebind();") + } + nl("}") + nl("") + nl("public ${it.getResolvedType().toJavaCode()} ${it.getterName}() {") { + tab("return ${it.fieldName};") + } + nl("}") + } + } + } + + fun onFieldChange() = kcode("") { + nl("@Override") + nl("protected boolean onFieldChange(int localFieldId, Object object, int fieldId) {") { + tab("switch (localFieldId) {") { + model.getObservables().forEach { + tab("case ${it.getId()} :") { + tab("return ${it.onChangeName}((${it.getResolvedType().toJavaCode()}) object, fieldId);") + } + } + } + tab("}") + tab("return false;") + } + nl("}") + nl("") + + model.getObservables().forEach { + nl("private boolean ${it.onChangeName}(${it.getResolvedType().toJavaCode()} ${it.readableUniqueName}, int fieldId) {") { + tab("switch (fieldId) {", { + val accessedFields: List<FieldAccessExpr> = it.getParents().filterIsInstance(javaClass<FieldAccessExpr>()) + accessedFields.filter { it.canBeInvalidated() } + .groupBy { it.getName() } + .forEach { + tab("case ${it.key.br()}:") { + val field = it.value.first() + mDirtyFlags.mapOr(field.invalidateFlagSet) { suffix, index -> + tab("${mDirtyFlags.localValue(index)} |= ${field.invalidateFlagSet.localValue(index)};") + } + tab("return true;") + } + + } + tab("case ${"".br()}:") { + val flagSet = it.invalidateFlagSet + mDirtyFlags.mapOr(flagSet) { suffix, index -> + tab("${mDirtyFlags.getLocalName()}$suffix |= ${flagSet.localValue(index)};") + } + tab("return true;") + } + + }) + tab("}") + tab("return false;") + } + nl("}") + nl("") + } + } + + fun declareViews() = kcode("// views") { + val oneLayout = !layoutBinder.hasVariations(); + layoutBinder.getBindingTargets().filter {it.isUsed() && (oneLayout || it.getId() == null)}.forEach { + val access : String + if (oneLayout && it.getId() != null) { + access = "public" + } else { + access = "private" + } + nl("${access} final ${it.interfaceType} ${it.fieldName};") + } + } + + fun declareVariables() = kcode("// variables") { + usedVariables.forEach { + nl("private ${it.getResolvedType().toJavaCode()} ${it.fieldName};") + } + } + + fun declareDirtyFlags() = kcode("// dirty flag") { + model.ext.localizedFlags.forEach { flag -> + flag.notEmpty { suffix, value -> + nl("private") + app(" ", if(flag.isDynamic()) null else "static final"); + app(" ", " ${flag.type} ${flag.getLocalName()}$suffix = ${longToBinary(value)};") + } + } + } + + fun flagMapping() = kcode("/* flag mapping") { + if (model.getFlagMapping() != null) { + val mapping = model.getFlagMapping() + for (i in mapping.indices) { + tab("flag $i: ${mapping[i]}") + } + } + nl("flag mapping end*/") + } + + fun executePendingBindings() = kcode("") { + nl("@Override") + nl("public void executePendingBindings() {") { + val tmpDirtyFlags = FlagSet(mDirtyFlags.buckets) + tmpDirtyFlags.setLocalName("dirtyFlags"); + for (i in (0..mDirtyFlags.buckets.size() - 1)) { + tab("${tmpDirtyFlags.type} ${tmpDirtyFlags.localValue(i)} = ${mDirtyFlags.localValue(i)};") + tab("${mDirtyFlags.localValue(i)} = 0;") + } + model.getPendingExpressions().filterNot {it.isVariable()}.forEach { + tab("${it.getResolvedType().toJavaCode()} ${it.localName} = ${it.getDefaultValue()};") + } + + do { + val batch = ExprModel.filterShouldRead(model.getPendingExpressions()).toArrayList() + val mJustRead = arrayListOf<Expr>() + while (!batch.none()) { + val readNow = batch.filter { it.shouldReadNow(mJustRead) } + if (readNow.isEmpty()) { + throw IllegalStateException("do not know what I can read. bailing out ${batch.joinToString("\n")}") + } + Log.d { "new read now. batch size: ${batch.size()}, readNow size: ${readNow.size()}" } + + readNow.forEach { + nl(readWithDependants(it, mJustRead, batch, tmpDirtyFlags)) + } + batch.removeAll(mJustRead) + } + tab("// batch finished") + } while(model.markBitsRead()) + + // + layoutBinder.getBindingTargets().filter { it.isUsed() } + .flatMap { it.getBindings() } + .groupBy { it.getExpr() } + .forEach { + val flagSet = it.key.dirtyFlagSet + tab("if (${tmpDirtyFlags.mapOr(flagSet){ suffix, index -> + "(${tmpDirtyFlags.localValue(index)} & ${flagSet.localValue(index)}) != 0" + }.joinToString(" || ") + }) {") { + it.value.forEach { binding -> + tab("// api target ${binding.getMinApi()}") + val fieldName : String + if (binding.getTarget().getViewClass(). + equals(binding.getTarget().getInterfaceType())) { + fieldName = "this.${binding.getTarget().fieldName}" + } else { + fieldName = "((${binding.getTarget().getViewClass()}) this.${binding.getTarget().fieldName})" + } + val bindingCode = binding.toJavaCode(fieldName, binding.getExpr().toCode().generate()) + if (binding.getMinApi() > 1) { + tab("if(getBuildSdkInt() >= ${binding.getMinApi()}) {") { + tab("$bindingCode;") + } + tab("}") + } else { + tab("$bindingCode;") + } + } + } + tab("}") + } + includedBinders.filter{it.isUsed()}.forEach { binder -> + tab("${binder.fieldName}.executePendingBindings();") + } + layoutBinder.getBindingTargets().filter{ + it.isUsed() && it.getResolvedType() != null && it.getResolvedType().extendsViewStub() + }.forEach { + tab("if (${it.fieldName}.getBinding() != null) {") { + tab("${it.fieldName}.getBinding().executePendingBindings();") + } + tab("}") + } + } + nl("}") + } + + fun readWithDependants(expr : Expr, mJustRead : MutableList<Expr>, batch : MutableList<Expr>, + tmpDirtyFlags : FlagSet, inheritedFlags : FlagSet? = null) : KCode = kcode("") { + mJustRead.add(expr) + Log.d { expr.getUniqueKey() } + val flagSet = expr.shouldReadFlagSet + val needsIfWrapper = inheritedFlags == null || !flagSet.bitsEqual(inheritedFlags) + val ifClause = "if (${tmpDirtyFlags.mapOr(flagSet){ suffix, index -> + "(${tmpDirtyFlags.localValue(index)} & ${flagSet.localValue(index)}) != 0" + }.joinToString(" || ") + })" + + val readCode = kcode("") { + if (!expr.isVariable()) { + // it is not a variable read it. + tab("// read ${expr.getUniqueKey()}") + // create an if case for all dependencies that might be null + val nullables = expr.getDependencies().filter { + it.isMandatory() && it.getOther().getResolvedType().isNullable() + }.map { it.getOther() } + if (!expr.isEqualityCheck() && nullables.isNotEmpty()) { + tab ("if ( ${nullables.map { "${it.localName} != null" }.joinToString(" && ")}) {") { + tab("${expr.localName}").app(" = ", expr.toCode(true)).app(";") + } + tab("}") + } else { + tab("${expr.localName}").app(" = ", expr.toCode(true)).app(";") + } + if (expr.isObservable()) { + tab("updateRegistration(${expr.getId()}, ${expr.localName});") + } + } + + // if I am the condition for an expression, set its flag + val conditionals = expr.getDependants().filter { !it.isConditional() + && it.getDependant() is TernaryExpr && (it.getDependant() as TernaryExpr).getPred() == expr } + .map { it.getDependant() } + if (conditionals.isNotEmpty()) { + tab("// setting conditional flags") + tab("if (${expr.localName}) {") { + conditionals.forEach { + val set = it.getRequirementFlagSet(true) + mDirtyFlags.mapOr(set) { suffix , index -> + tab("${tmpDirtyFlags.localValue(index)} |= ${set.localValue(index)};") + } + } + } + tab("} else {") { + conditionals.forEach { + val set = it.getRequirementFlagSet(false) + mDirtyFlags.mapOr(set) { suffix , index -> + tab("${tmpDirtyFlags.localValue(index)} |= ${set.localValue(index)};") + } + } + } tab("}") + } + + val chosen = expr.getDependants().filter { + val dependant = it.getDependant() + batch.contains(dependant) && + dependant.shouldReadFlagSet.andNot(flagSet).isEmpty() && + dependant.shouldReadNow(mJustRead) + } + if (chosen.isNotEmpty()) { + val nextInheritedFlags = if (needsIfWrapper) flagSet else inheritedFlags + chosen.forEach { + nl(readWithDependants(it.getDependant(), mJustRead, batch, tmpDirtyFlags, nextInheritedFlags)) + } + } + } + if (needsIfWrapper) { + tab(ifClause) { + app(" {") + nl(readCode) + } + tab("}") + } else { + nl(readCode) + } + } + + fun declareFactories() = kcode("") { + nl("public static ${baseClassName} inflate(android.view.ViewGroup root) {") { + tab("return bind(android.view.LayoutInflater.from(root.getContext()).inflate(${layoutBinder.getModulePackage()}.R.layout.${layoutBinder.getLayoutname()}, root, true));") + } + nl("}") + nl("public static ${baseClassName} inflate(android.content.Context context) {") { + tab("return bind(android.view.LayoutInflater.from(context).inflate(${layoutBinder.getModulePackage()}.R.layout.${layoutBinder.getLayoutname()}, null, false));") + } + nl("}") + nl("public static ${baseClassName} bind(android.view.View view) {") { + tab("if (!\"${layoutBinder.getTag()}\".equals(view.getTag())) {") { + tab("throw new RuntimeException(\"view tag isn't correct on view\");") + } + tab("}") + tab("return new ${baseClassName}(view);") + } + nl("}") + } + + public fun writeBaseClass() : String = + kcode("package ${layoutBinder.getPackage()};") { + nl("import android.databinding.Bindable;") + nl("import android.databinding.DataBindingUtil;") + nl("import android.databinding.ViewDataBinding;") + nl("public abstract class ${baseClassName} extends ViewDataBinding {") + layoutBinder.getBindingTargets().filter{it.getId() != null}.forEach { + tab("public final ${it.interfaceType} ${it.fieldName};") + } + nl("") + tab("protected ${baseClassName}(android.view.View root_, int localFieldCount") { + layoutBinder.getBindingTargets().filter{it.getId() != null}.forEach { + tab(", ${it.interfaceType} ${it.readableUniqueName}") + } + } + tab(") {") { + tab("super(root_, localFieldCount);") + layoutBinder.getBindingTargets().filter{it.getId() != null}.forEach { + tab("this.${it.fieldName} = ${it.readableUniqueName};") + } + } + tab("}") + nl("") + variables.forEach { + if (it.getUserDefinedType() != null) { + //it.getExpandedUserDefinedType(ModelAnalyzer.getInstance()); + val type = ModelAnalyzer.getInstance().applyImports(it.getUserDefinedType(), model.getImports()) + tab("public abstract void ${it.setterName}(${type} ${it.readableUniqueName});") + } + } + tab("public static ${baseClassName} inflate(android.view.ViewGroup root) {") { + tab("return DataBindingUtil.<${baseClassName}>inflate(root.getContext(), ${layoutBinder.getModulePackage()}.R.layout.${layoutBinder.getLayoutname()}, root, true);") + } + tab("}") + tab("public static ${baseClassName} inflate(android.content.Context context) {") { + tab("return DataBindingUtil.<${baseClassName}>inflate(context, ${layoutBinder.getModulePackage()}.R.layout.${layoutBinder.getLayoutname()}, null, false);") + } + tab("}") + tab("public static ${baseClassName} bind(android.view.View view) {") { + tab("return (${baseClassName})DataBindingUtil.bindTo(view, ${layoutBinder.getModulePackage()}.R.layout.${layoutBinder.getLayoutname()});") + } + tab("}") + nl("}") + }.generate() +} diff --git a/tools/data-binding/compiler/src/test/java/android/databinding/CallbackRegistryTest.java b/tools/data-binding/compiler/src/test/java/android/databinding/CallbackRegistryTest.java new file mode 100644 index 0000000..fd1562d --- /dev/null +++ b/tools/data-binding/compiler/src/test/java/android/databinding/CallbackRegistryTest.java @@ -0,0 +1,313 @@ +/* + * Copyright (C) 2014 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 android.databinding; + +import org.junit.Test; + +import java.util.ArrayList; + +import static org.junit.Assert.assertEquals; +import static org.junit.Assert.assertFalse; +import static org.junit.Assert.assertNotNull; +import static org.junit.Assert.assertTrue; + +public class CallbackRegistryTest { + + final Integer callback1 = 1; + final Integer callback2 = 2; + final Integer callback3 = 3; + CallbackRegistry<Integer, CallbackRegistryTest, Integer> registry; + int notify1; + int notify2; + int notify3; + int[] deepNotifyCount = new int[300]; + Integer argValue; + + private void addNotifyCount(Integer callback) { + if (callback == callback1) { + notify1++; + } else if (callback == callback2) { + notify2++; + } else if (callback == callback3) { + notify3++; + } + deepNotifyCount[callback]++; + } + + @Test + public void testAddListener() { + CallbackRegistry.NotifierCallback<Integer, CallbackRegistryTest, Integer> notifier = + new CallbackRegistry.NotifierCallback<Integer, CallbackRegistryTest, Integer>() { + @Override + public void onNotifyCallback(Integer callback, CallbackRegistryTest sender, + int arg, Integer arg2) { + } + }; + registry = new CallbackRegistry<Integer, CallbackRegistryTest, Integer>(notifier); + Integer callback = 0; + + assertNotNull(registry.copyListeners()); + assertEquals(0, registry.copyListeners().size()); + + registry.add(callback); + ArrayList<Integer> callbacks = registry.copyListeners(); + assertEquals(1, callbacks.size()); + assertEquals(callback, callbacks.get(0)); + + registry.add(callback); + callbacks = registry.copyListeners(); + assertEquals(1, callbacks.size()); + assertEquals(callback, callbacks.get(0)); + + Integer otherListener = 1; + registry.add(otherListener); + callbacks = registry.copyListeners(); + assertEquals(2, callbacks.size()); + assertEquals(callback, callbacks.get(0)); + assertEquals(otherListener, callbacks.get(1)); + + registry.remove(callback); + registry.add(callback); + callbacks = registry.copyListeners(); + assertEquals(2, callbacks.size()); + assertEquals(callback, callbacks.get(1)); + assertEquals(otherListener, callbacks.get(0)); + } + + @Test + public void testSimpleNotify() { + CallbackRegistry.NotifierCallback<Integer, CallbackRegistryTest, Integer> notifier = + new CallbackRegistry.NotifierCallback<Integer, CallbackRegistryTest, Integer>() { + @Override + public void onNotifyCallback(Integer callback, CallbackRegistryTest sender, + int arg1, Integer arg) { + assertEquals(arg1, (int) arg); + addNotifyCount(callback); + argValue = arg; + } + }; + registry = new CallbackRegistry<Integer, CallbackRegistryTest, Integer>(notifier); + registry.add(callback2); + Integer arg = 1; + registry.notifyCallbacks(this, arg, arg); + assertEquals(arg, argValue); + assertEquals(1, notify2); + } + + @Test + public void testRemoveWhileNotifying() { + CallbackRegistry.NotifierCallback<Integer, CallbackRegistryTest, Integer> notifier = + new CallbackRegistry.NotifierCallback<Integer, CallbackRegistryTest, Integer>() { + @Override + public void onNotifyCallback(Integer callback, CallbackRegistryTest sender, + int arg1, Integer arg) { + addNotifyCount(callback); + if (callback == callback1) { + registry.remove(callback1); + registry.remove(callback2); + } + } + }; + registry = new CallbackRegistry<Integer, CallbackRegistryTest, Integer>(notifier); + registry.add(callback1); + registry.add(callback2); + registry.add(callback3); + registry.notifyCallbacks(this, 0, null); + assertEquals(1, notify1); + assertEquals(1, notify2); + assertEquals(1, notify3); + + ArrayList<Integer> callbacks = registry.copyListeners(); + assertEquals(1, callbacks.size()); + assertEquals(callback3, callbacks.get(0)); + } + + @Test + public void testDeepRemoveWhileNotifying() { + CallbackRegistry.NotifierCallback<Integer, CallbackRegistryTest, Integer> notifier = + new CallbackRegistry.NotifierCallback<Integer, CallbackRegistryTest, Integer>() { + @Override + public void onNotifyCallback(Integer callback, CallbackRegistryTest sender, + int arg1, Integer arg) { + addNotifyCount(callback); + registry.remove(callback); + registry.notifyCallbacks(CallbackRegistryTest.this, arg1, null); + } + }; + registry = new CallbackRegistry<Integer, CallbackRegistryTest, Integer>(notifier); + registry.add(callback1); + registry.add(callback2); + registry.add(callback3); + registry.notifyCallbacks(this, 0, null); + assertEquals(1, notify1); + assertEquals(2, notify2); + assertEquals(3, notify3); + + ArrayList<Integer> callbacks = registry.copyListeners(); + assertEquals(0, callbacks.size()); + } + + @Test + public void testAddRemovedListener() { + + CallbackRegistry.NotifierCallback<Integer, CallbackRegistryTest, Integer> notifier = + new CallbackRegistry.NotifierCallback<Integer, CallbackRegistryTest, Integer>() { + @Override + public void onNotifyCallback(Integer callback, CallbackRegistryTest sender, + int arg1, Integer arg) { + addNotifyCount(callback); + if (callback == callback1) { + registry.remove(callback2); + } else if (callback == callback3) { + registry.add(callback2); + } + } + }; + registry = new CallbackRegistry<Integer, CallbackRegistryTest, Integer>(notifier); + + registry.add(callback1); + registry.add(callback2); + registry.add(callback3); + registry.notifyCallbacks(this, 0, null); + + ArrayList<Integer> callbacks = registry.copyListeners(); + assertEquals(3, callbacks.size()); + assertEquals(callback1, callbacks.get(0)); + assertEquals(callback3, callbacks.get(1)); + assertEquals(callback2, callbacks.get(2)); + assertEquals(1, notify1); + assertEquals(1, notify2); + assertEquals(1, notify3); + } + + @Test + public void testVeryDeepRemoveWhileNotifying() { + final Integer[] callbacks = new Integer[deepNotifyCount.length]; + for (int i = 0; i < callbacks.length; i++) { + callbacks[i] = i; + } + CallbackRegistry.NotifierCallback<Integer, CallbackRegistryTest, Integer> notifier = + new CallbackRegistry.NotifierCallback<Integer, CallbackRegistryTest, Integer>() { + @Override + public void onNotifyCallback(Integer callback, CallbackRegistryTest sender, + int arg1, Integer arg) { + addNotifyCount(callback); + registry.remove(callback); + registry.remove(callbacks[callbacks.length - callback - 1]); + registry.notifyCallbacks(CallbackRegistryTest.this, arg1, null); + } + }; + registry = new CallbackRegistry<Integer, CallbackRegistryTest, Integer>(notifier); + for (int i = 0; i < callbacks.length; i++) { + registry.add(callbacks[i]); + } + registry.notifyCallbacks(this, 0, null); + for (int i = 0; i < deepNotifyCount.length; i++) { + int expectedCount = Math.min(i + 1, deepNotifyCount.length - i); + assertEquals(expectedCount, deepNotifyCount[i]); + } + + ArrayList<Integer> callbackList = registry.copyListeners(); + assertEquals(0, callbackList.size()); + } + + @Test + public void testClear() { + CallbackRegistry.NotifierCallback<Integer, CallbackRegistryTest, Integer> notifier = + new CallbackRegistry.NotifierCallback<Integer, CallbackRegistryTest, Integer>() { + @Override + public void onNotifyCallback(Integer callback, CallbackRegistryTest sender, + int arg1, Integer arg) { + addNotifyCount(callback); + } + }; + registry = new CallbackRegistry<Integer, CallbackRegistryTest, Integer>(notifier); + for (int i = 0; i < deepNotifyCount.length; i++) { + registry.add(i); + } + registry.clear(); + + ArrayList<Integer> callbackList = registry.copyListeners(); + assertEquals(0, callbackList.size()); + + registry.notifyCallbacks(this, 0, null); + for (int i = 0; i < deepNotifyCount.length; i++) { + assertEquals(0, deepNotifyCount[i]); + } + } + + @Test + public void testNestedClear() { + CallbackRegistry.NotifierCallback<Integer, CallbackRegistryTest, Integer> notifier = + new CallbackRegistry.NotifierCallback<Integer, CallbackRegistryTest, Integer>() { + @Override + public void onNotifyCallback(Integer callback, CallbackRegistryTest sender, + int arg1, Integer arg) { + addNotifyCount(callback); + registry.clear(); + } + }; + registry = new CallbackRegistry<Integer, CallbackRegistryTest, Integer>(notifier); + for (int i = 0; i < deepNotifyCount.length; i++) { + registry.add(i); + } + registry.notifyCallbacks(this, 0, null); + for (int i = 0; i < deepNotifyCount.length; i++) { + assertEquals(1, deepNotifyCount[i]); + } + + ArrayList<Integer> callbackList = registry.copyListeners(); + assertEquals(0, callbackList.size()); + } + + @Test + public void testIsEmpty() throws Exception { + CallbackRegistry.NotifierCallback<Integer, CallbackRegistryTest, Integer> notifier = + new CallbackRegistry.NotifierCallback<Integer, CallbackRegistryTest, Integer>() { + @Override + public void onNotifyCallback(Integer callback, CallbackRegistryTest sender, + int arg, Integer arg2) { + } + }; + registry = new CallbackRegistry<Integer, CallbackRegistryTest, Integer>(notifier); + Integer callback = 0; + + assertTrue(registry.isEmpty()); + registry.add(callback); + assertFalse(registry.isEmpty()); + } + + @Test + public void testClone() throws Exception { + CallbackRegistry.NotifierCallback<Integer, CallbackRegistryTest, Integer> notifier = + new CallbackRegistry.NotifierCallback<Integer, CallbackRegistryTest, Integer>() { + @Override + public void onNotifyCallback(Integer callback, CallbackRegistryTest sender, + int arg, Integer arg2) { + } + }; + registry = new CallbackRegistry<Integer, CallbackRegistryTest, Integer>(notifier); + + assertTrue(registry.isEmpty()); + CallbackRegistry<Integer, CallbackRegistryTest, Integer> registry2 = registry.clone(); + Integer callback = 0; + registry.add(callback); + assertFalse(registry.isEmpty()); + assertTrue(registry2.isEmpty()); + registry2 = registry.clone(); + assertFalse(registry2.isEmpty()); + } +} diff --git a/tools/data-binding/compiler/src/test/java/android/databinding/tool/ExpressionVisitorTest.java b/tools/data-binding/compiler/src/test/java/android/databinding/tool/ExpressionVisitorTest.java new file mode 100644 index 0000000..e3f3345 --- /dev/null +++ b/tools/data-binding/compiler/src/test/java/android/databinding/tool/ExpressionVisitorTest.java @@ -0,0 +1,200 @@ +/* + * 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 android.databinding.tool; + +import org.junit.Before; +import org.junit.Test; +import org.junit.runner.RunWith; +import org.junit.runners.Parameterized; + +import android.databinding.tool.expr.ComparisonExpr; +import android.databinding.tool.expr.Dependency; +import android.databinding.tool.expr.Expr; +import android.databinding.tool.expr.ExprModel; +import android.databinding.tool.expr.FieldAccessExpr; +import android.databinding.tool.expr.IdentifierExpr; +import android.databinding.tool.expr.MethodCallExpr; +import android.databinding.tool.expr.SymbolExpr; +import android.databinding.tool.expr.TernaryExpr; +import android.databinding.tool.reflection.Callable; +import android.databinding.tool.reflection.java.JavaAnalyzer; +import android.databinding.tool.reflection.java.JavaClass; + +import java.util.Arrays; +import java.util.List; + +import static org.junit.Assert.assertEquals; +import static org.junit.Assert.assertFalse; +import static org.junit.Assert.assertSame; +import static org.junit.Assert.assertTrue; + +public class ExpressionVisitorTest { + ExpressionParser mParser = new ExpressionParser(new ExprModel()); + + @Before + public void setUp() throws Exception { + JavaAnalyzer.initForTests(); + } + + private <T extends Expr> T parse(String input, Class<T> klass) { + final Expr parsed = mParser.parse(input); + assertSame(klass, parsed.getClass()); + return (T) parsed; + } + + @Test + public void testSymbol() { + final SymbolExpr res = parse("null", SymbolExpr.class); + assertEquals(1, mParser.getModel().size()); + assertEquals("null", res.getText()); + assertEquals(new JavaClass(Object.class),res.getResolvedType()); + assertEquals(0, res.getDependencies().size()); + } + + + @RunWith(Parameterized.class) + public static class ComparisonExprTests { + ExpressionParser mParser = new ExpressionParser(new ExprModel()); + private final String mOp; + + @Before + public void setUp() throws Exception { + JavaAnalyzer.initForTests(); + } + + @Parameterized.Parameters + public static List<Object[]> data() { + return Arrays.asList(new Object[][] { + {"=="}, {"<="}, {">="}, {">"}, {"<"} + }); + } + + public ComparisonExprTests(String op) { + mOp = op; + } + + @Test + public void testComparison() { + final Expr res = mParser.parse("3 " + mOp + " 5"); + assertEquals(3, mParser.getModel().size()); + assertTrue(res instanceof ComparisonExpr); + // 0 because they are both static + assertEquals(0, res.getDependencies().size()); + } + } + + + + @Test + public void testSimpleFieldAccess() { + final FieldAccessExpr expr = parse("a.b", FieldAccessExpr.class); + assertEquals(2, mParser.mModel.size()); + assertEquals("b", expr.getName()); + assertEquals(1, expr.getChildren().size()); + final Expr parent = expr.getChildren().get(0); + assertTrue(parent instanceof IdentifierExpr); + final IdentifierExpr id = (IdentifierExpr) parent; + assertEquals("a", id.getName()); + assertEquals(0, id.getDependencies().size()); + assertEquals(1, expr.getDependencies().size()); + } + + @Test + public void testIdentifier() { + final IdentifierExpr id = parse("myStr", IdentifierExpr.class); + assertEquals(1, mParser.mModel.size()); + assertEquals("myStr", id.getName()); + id.setUserDefinedType("java.lang.String"); + assertEquals(new JavaClass(String.class), id.getResolvedType()); + } + + @Test + public void testTernary() { + final TernaryExpr parsed = parse("a > b ? 5 : 4", TernaryExpr.class); + assertEquals(6, mParser.getModel().size()); + assertTrue(parsed.getPred() instanceof ComparisonExpr); + assertTrue(parsed.getIfTrue() instanceof SymbolExpr); + assertTrue(parsed.getIfFalse() instanceof SymbolExpr); + ComparisonExpr pred = (ComparisonExpr) parsed.getPred(); + SymbolExpr ifTrue = (SymbolExpr) parsed.getIfTrue(); + SymbolExpr ifFalse = (SymbolExpr) parsed.getIfFalse(); + assertEquals("5", ifTrue.getText()); + assertEquals("4", ifFalse.getText()); + assertEquals(1, parsed.getDependencies().size()); + for (Dependency dependency : parsed.getDependencies()) { + assertEquals(dependency.getOther() != pred, dependency.isConditional()); + } + } + + @Test + public void testInheritedFieldResolution() { + final FieldAccessExpr parsed = parse("myStr.length", FieldAccessExpr.class); + assertTrue(parsed.getChild() instanceof IdentifierExpr); + final IdentifierExpr id = (IdentifierExpr) parsed.getChild(); + id.setUserDefinedType("java.lang.String"); + assertEquals(new JavaClass(int.class), parsed.getResolvedType()); + Callable getter = parsed.getGetter(); + assertEquals(Callable.Type.METHOD, getter.type); + assertEquals("length", getter.name); + assertEquals(1, parsed.getDependencies().size()); + final Dependency dep = parsed.getDependencies().get(0); + assertSame(id, dep.getOther()); + assertFalse(dep.isConditional()); + } + + @Test + public void testGetterResolution() { + final FieldAccessExpr parsed = parse("myStr.bytes", FieldAccessExpr.class); + assertTrue(parsed.getChild() instanceof IdentifierExpr); + final IdentifierExpr id = (IdentifierExpr) parsed.getChild(); + id.setUserDefinedType("java.lang.String"); + assertEquals(new JavaClass(byte[].class), parsed.getResolvedType()); + Callable getter = parsed.getGetter(); + assertEquals(Callable.Type.METHOD, getter.type); + assertEquals("getBytes", getter.name); + assertEquals(1, parsed.getDependencies().size()); + final Dependency dep = parsed.getDependencies().get(0); + assertSame(id, dep.getOther()); + assertFalse(dep.isConditional()); + } + + @Test + public void testMethodCall() { + final MethodCallExpr parsed = parse("user.getName()", MethodCallExpr.class); + assertTrue(parsed.getTarget() instanceof IdentifierExpr); + assertEquals("getName", parsed.getName()); + assertEquals(0, parsed.getArgs().size()); + assertEquals(1, parsed.getDependencies().size()); + final Dependency dep = parsed.getDependencies().get(0); + assertSame(mParser.parse("user"), dep.getOther()); + assertFalse(dep.isConditional()); + } + + @Test + public void testMethodCallWithArgs() { + final MethodCallExpr parsed = parse("str.substring(1, a)", MethodCallExpr.class); + assertTrue(parsed.getTarget() instanceof IdentifierExpr); + assertEquals("substring", parsed.getName()); + final List<Expr> args = parsed.getArgs(); + assertEquals(2, args.size()); + assertTrue(args.get(0) instanceof SymbolExpr); + assertTrue(args.get(1) instanceof IdentifierExpr); + final List<Dependency> deps = parsed.getDependencies(); + assertEquals(2, deps.size()); + } + +} diff --git a/tools/data-binding/compiler/src/test/java/android/databinding/tool/LayoutBinderTest.java b/tools/data-binding/compiler/src/test/java/android/databinding/tool/LayoutBinderTest.java new file mode 100644 index 0000000..4219647 --- /dev/null +++ b/tools/data-binding/compiler/src/test/java/android/databinding/tool/LayoutBinderTest.java @@ -0,0 +1,110 @@ +/* + * 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 android.databinding.tool; + + +import org.junit.Before; +import org.junit.Test; + +import android.databinding.tool.expr.Expr; +import android.databinding.tool.expr.ExprModel; +import android.databinding.tool.expr.FieldAccessExpr; +import android.databinding.tool.expr.IdentifierExpr; +import android.databinding.tool.expr.StaticIdentifierExpr; +import android.databinding.tool.reflection.Callable; +import android.databinding.tool.reflection.java.JavaClass; + +import java.util.List; +import java.util.Map; + +import static org.junit.Assert.assertEquals; +import static org.junit.Assert.assertFalse; +import static org.junit.Assert.assertSame; +import static org.junit.Assert.assertTrue; + +public class LayoutBinderTest { + LayoutBinder mLayoutBinder; + ExprModel mExprModel; + @Before + public void setUp() throws Exception { + mLayoutBinder = new MockLayoutBinder(); + mExprModel = mLayoutBinder.getModel(); + } + + @Test + public void testRegisterId() { + mLayoutBinder.addVariable("test", "java.lang.String"); + assertEquals(1, mExprModel.size()); + final Map.Entry<String, Expr> entry = mExprModel.getExprMap().entrySet().iterator().next(); + final Expr value = entry.getValue(); + assertEquals(value.getClass(), IdentifierExpr.class); + final IdentifierExpr id = (IdentifierExpr) value; + assertEquals("test", id.getName()); + assertEquals(new JavaClass(String.class), id.getResolvedType()); + assertTrue(id.isDynamic()); + } + + @Test + public void testRegisterImport() { + mExprModel.addImport("test", "java.lang.String"); + assertEquals(1, mExprModel.size()); + final Map.Entry<String, Expr> entry = mExprModel.getExprMap().entrySet().iterator().next(); + final Expr value = entry.getValue(); + assertEquals(value.getClass(), StaticIdentifierExpr.class); + final IdentifierExpr id = (IdentifierExpr) value; + assertEquals("test", id.getName()); + assertEquals(new JavaClass(String.class), id.getResolvedType()); + assertFalse(id.isDynamic()); + } + + @Test + public void testParse() { + mLayoutBinder.addVariable("user", "android.databinding.tool2.LayoutBinderTest.TestUser"); + mLayoutBinder.parse("user.name"); + mLayoutBinder.parse("user.lastName"); + assertEquals(3, mExprModel.size()); + final List<Expr> bindingExprs = mExprModel.getBindingExpressions(); + assertEquals(2, bindingExprs.size()); + IdentifierExpr id = mExprModel.identifier("user"); + assertTrue(bindingExprs.get(0) instanceof FieldAccessExpr); + assertTrue(bindingExprs.get(1) instanceof FieldAccessExpr); + assertEquals(2, id.getParents().size()); + assertTrue(bindingExprs.get(0).getChildren().contains(id)); + assertTrue(bindingExprs.get(1).getChildren().contains(id)); + } + + @Test + public void testParseWithMethods() { + mLayoutBinder.addVariable("user", "android.databinding.tool.LayoutBinderTest.TestUser"); + mLayoutBinder.parse("user.fullName"); + Expr item = mExprModel.getBindingExpressions().get(0); + assertTrue(item instanceof FieldAccessExpr); + IdentifierExpr id = mExprModel.identifier("user"); + FieldAccessExpr fa = (FieldAccessExpr) item; + fa.getResolvedType(); + final Callable getter = fa.getGetter(); + assertTrue(getter.type == Callable.Type.METHOD); + assertSame(id, fa.getChild()); + assertTrue(fa.isDynamic()); + } + + static class TestUser { + public String name; + public String lastName; + + public String fullName() { + return name + " " + lastName; + } + } +} diff --git a/tools/data-binding/compiler/src/test/java/android/databinding/tool/MockLayoutBinder.java b/tools/data-binding/compiler/src/test/java/android/databinding/tool/MockLayoutBinder.java new file mode 100644 index 0000000..068de85 --- /dev/null +++ b/tools/data-binding/compiler/src/test/java/android/databinding/tool/MockLayoutBinder.java @@ -0,0 +1,24 @@ +/* + * 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 android.databinding.tool; + +import android.databinding.tool.store.ResourceBundle; + +public class MockLayoutBinder extends LayoutBinder { + + public MockLayoutBinder() { + super(new ResourceBundle("com.test"), + new ResourceBundle.LayoutFileBundle("blah.xml", 1, ".", "com.test.submodule")); + } +}
\ No newline at end of file diff --git a/tools/data-binding/compiler/src/test/java/android/databinding/tool/SdkVersionTest.java b/tools/data-binding/compiler/src/test/java/android/databinding/tool/SdkVersionTest.java new file mode 100644 index 0000000..70db3a7 --- /dev/null +++ b/tools/data-binding/compiler/src/test/java/android/databinding/tool/SdkVersionTest.java @@ -0,0 +1,48 @@ +/* + * 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 android.databinding.tool; + +import org.junit.Before; +import org.junit.Test; + +import android.databinding.tool.reflection.ModelAnalyzer; +import android.databinding.tool.reflection.ModelClass; +import android.databinding.tool.reflection.ModelMethod; +import android.databinding.tool.reflection.SdkUtil; +import android.databinding.tool.reflection.java.JavaAnalyzer; + +import static org.junit.Assert.assertEquals; + +public class SdkVersionTest { + + @Before + public void setUp() throws Exception { + JavaAnalyzer.initForTests(); + } + + @Test + public void testNewApiMethod() { + ModelClass view = ModelAnalyzer.getInstance().findClass("android.view.View", null); + ModelMethod setElevation = view.getMethods("setElevation", 1)[0]; + assertEquals(21, SdkUtil.getMinApi(setElevation)); + } + + @Test + public void testCustomCode() { + ModelClass view = ModelAnalyzer.getInstance() + .findClass("android.databinding.tool.SdkVersionTest", null); + ModelMethod setElevation = view.getMethods("testCustomCode", 0)[0]; + assertEquals(1, SdkUtil.getMinApi(setElevation)); + } +} diff --git a/tools/data-binding/compiler/src/test/java/android/databinding/tool/expr/ExprModelTest.java b/tools/data-binding/compiler/src/test/java/android/databinding/tool/expr/ExprModelTest.java new file mode 100644 index 0000000..7bccd6e --- /dev/null +++ b/tools/data-binding/compiler/src/test/java/android/databinding/tool/expr/ExprModelTest.java @@ -0,0 +1,564 @@ +/* + * 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 android.databinding.tool.expr; + +import com.google.common.base.Predicate; +import com.google.common.collect.Iterables; + +import org.apache.commons.lang3.ArrayUtils; +import org.apache.commons.lang3.NotImplementedException; +import org.junit.Before; +import org.junit.Rule; +import org.junit.Test; +import org.junit.rules.TestWatcher; +import org.junit.runner.Description; + +import android.databinding.tool.LayoutBinder; +import android.databinding.tool.MockLayoutBinder; +import android.databinding.tool.reflection.ModelAnalyzer; +import android.databinding.tool.reflection.ModelClass; +import android.databinding.tool.reflection.java.JavaAnalyzer; +import android.databinding.tool.util.L; + +import java.util.ArrayList; +import java.util.Arrays; +import java.util.BitSet; +import java.util.List; + +import static org.junit.Assert.assertEquals; +import static org.junit.Assert.assertFalse; +import static org.junit.Assert.assertNotNull; +import static org.junit.Assert.assertNull; +import static org.junit.Assert.assertSame; +import static org.junit.Assert.assertTrue; + +public class ExprModelTest { + + private static class DummyExpr extends Expr { + + String mKey; + + public DummyExpr(String key, DummyExpr... children) { + super(children); + mKey = key; + } + + @Override + protected ModelClass resolveType(ModelAnalyzer modelAnalyzer) { + return modelAnalyzer.findClass(Integer.class); + } + + @Override + protected List<Dependency> constructDependencies() { + return constructDynamicChildrenDependencies(); + } + + @Override + protected String computeUniqueKey() { + return mKey + super.computeUniqueKey(); + } + } + + ExprModel mExprModel; + + @Rule + public TestWatcher mTestWatcher = new TestWatcher() { + @Override + protected void failed(Throwable e, Description description) { + if (mExprModel != null && mExprModel.getFlagMapping() != null) { + final String[] mapping = mExprModel.getFlagMapping(); + for (int i = 0; i < mapping.length; i++) { + L.d("flag %d: %s", i, mapping[i]); + } + } + } + }; + + @Before + public void setUp() throws Exception { + JavaAnalyzer.initForTests(); + mExprModel = new ExprModel(); + } + + @Test + public void testAddNormal() { + final DummyExpr d = new DummyExpr("a"); + assertSame(d, mExprModel.register(d)); + assertSame(d, mExprModel.register(d)); + assertEquals(1, mExprModel.mExprMap.size()); + } + + @Test + public void testAddDupe1() { + final DummyExpr d = new DummyExpr("a"); + assertSame(d, mExprModel.register(d)); + assertSame(d, mExprModel.register(new DummyExpr("a"))); + assertEquals(1, mExprModel.mExprMap.size()); + } + + @Test + public void testAddMultiple() { + mExprModel.register(new DummyExpr("a")); + mExprModel.register(new DummyExpr("b")); + assertEquals(2, mExprModel.mExprMap.size()); + } + + + @Test + public void testAddWithChildren() { + DummyExpr a = new DummyExpr("a"); + DummyExpr b = new DummyExpr("b"); + DummyExpr c = new DummyExpr("c", a, b); + mExprModel.register(c); + DummyExpr a2 = new DummyExpr("a"); + DummyExpr b2 = new DummyExpr("b"); + DummyExpr c2 = new DummyExpr("c", a, b); + assertEquals(c, mExprModel.register(c2)); + } + + @Test + public void testShouldRead() { + LayoutBinder lb = new MockLayoutBinder(); + mExprModel = lb.getModel(); + IdentifierExpr a = lb.addVariable("a", "java.lang.String"); + IdentifierExpr b = lb.addVariable("b", "java.lang.String"); + IdentifierExpr c = lb.addVariable("c", "java.lang.String"); + lb.parse("a == null ? b : c"); + mExprModel.comparison("==", a, mExprModel.symbol("null", Object.class)); + lb.getModel().seal(); + Iterable<Expr> shouldRead = getShouldRead(); + // a and a == null + assertEquals(2, Iterables.size(shouldRead)); + final Iterable<Expr> readFirst = getReadFirst(shouldRead, null); + assertEquals(1, Iterables.size(readFirst)); + final Expr first = Iterables.getFirst(readFirst, null); + assertSame(a, first); + // now , assume we've read this + final BitSet shouldReadFlags = first.getShouldReadFlags(); + assertNotNull(shouldReadFlags); + } + + @Test + public void testTernaryWithPlus() { + LayoutBinder lb = new MockLayoutBinder(); + mExprModel = lb.getModel(); + IdentifierExpr user = lb + .addVariable("user", "android.databinding.tool.expr.ExprModelTest.User"); + MathExpr parsed = parse(lb, "user.name + \" \" + (user.lastName ?? \"\")", MathExpr.class); + mExprModel.seal(); + Iterable<Expr> toRead = getShouldRead(); + Iterable<Expr> readNow = getReadFirst(toRead); + assertEquals(1, Iterables.size(readNow)); + assertSame(user, Iterables.getFirst(readNow, null)); + List<Expr> justRead = new ArrayList<Expr>(); + justRead.add(user); + readNow = filterOut(getReadFirst(toRead, justRead), justRead); + assertEquals(2, Iterables.size(readNow)); //user.name && user.lastName + Iterables.addAll(justRead, readNow); + // user.lastname (T, F), user.name + " " + readNow = filterOut(getReadFirst(toRead, justRead), justRead); + assertEquals(2, Iterables.size(readNow)); //user.name && user.lastName + Iterables.addAll(justRead, readNow); + readNow = filterOut(getReadFirst(toRead, justRead), justRead); + assertEquals(0, Iterables.size(readNow)); + mExprModel.markBitsRead(); + + toRead = getShouldRead(); + assertEquals(2, Iterables.size(toRead)); + justRead.clear(); + readNow = filterOut(getReadFirst(toRead, justRead), justRead); + assertEquals(1, Iterables.size(readNow)); + assertSame(parsed.getRight(), Iterables.getFirst(readNow, null)); + Iterables.addAll(justRead, readNow); + + readNow = filterOut(getReadFirst(toRead, justRead), justRead); + assertEquals(1, Iterables.size(readNow)); + assertSame(parsed, Iterables.getFirst(readNow, null)); + Iterables.addAll(justRead, readNow); + + readNow = filterOut(getReadFirst(toRead, justRead), justRead); + assertEquals(0, Iterables.size(readNow)); + mExprModel.markBitsRead(); + assertEquals(0, Iterables.size(getShouldRead())); + } + + private List<Expr> filterOut(Iterable itr, final Iterable exclude) { + return Arrays.asList(Iterables.toArray(Iterables.filter(itr, new Predicate() { + @Override + public boolean apply(Object input) { + return !Iterables.contains(exclude, input); + } + }), Expr.class)); + } + + @Test + public void testTernaryInsideTernary() { + LayoutBinder lb = new MockLayoutBinder(); + mExprModel = lb.getModel(); + IdentifierExpr cond1 = lb.addVariable("cond1", "boolean"); + IdentifierExpr cond2 = lb.addVariable("cond2", "boolean"); + + IdentifierExpr a = lb.addVariable("a", "boolean"); + IdentifierExpr b = lb.addVariable("b", "boolean"); + IdentifierExpr c = lb.addVariable("c", "boolean"); + + final TernaryExpr ternaryExpr = parse(lb, "cond1 ? cond2 ? a : b : c", TernaryExpr.class); + final TernaryExpr innerTernary = (TernaryExpr) ternaryExpr.getIfTrue(); + mExprModel.seal(); + + Iterable<Expr> toRead = getShouldRead(); + assertEquals(1, Iterables.size(toRead)); + assertEquals(ternaryExpr.getPred(), Iterables.getFirst(toRead, null)); + + Iterable<Expr> readNow = getReadFirst(toRead); + assertEquals(1, Iterables.size(readNow)); + assertEquals(ternaryExpr.getPred(), Iterables.getFirst(readNow, null)); + int cond1True = ternaryExpr.getRequirementFlagIndex(true); + int cond1False = ternaryExpr.getRequirementFlagIndex(false); + // ok, it is read now. + mExprModel.markBitsRead(); + + // now it should read cond2 or c, depending on the flag from first + toRead = getShouldRead(); + assertEquals(2, Iterables.size(toRead)); + assertExactMatch(toRead, ternaryExpr.getIfFalse(), innerTernary.getPred()); + assertFlags(ternaryExpr.getIfFalse(), cond1False); + assertFlags(ternaryExpr.getIfTrue(), cond1True); + + mExprModel.markBitsRead(); + + // now it should read a or b, innerTernary, outerTernary + toRead = getShouldRead(); + assertExactMatch(toRead, innerTernary.getIfTrue(), innerTernary.getIfFalse(), ternaryExpr, + innerTernary); + assertFlags(innerTernary.getIfTrue(), innerTernary.getRequirementFlagIndex(true)); + assertFlags(innerTernary.getIfFalse(), innerTernary.getRequirementFlagIndex(false)); + assertFalse(mExprModel.markBitsRead()); + } + + @Test + public void testRequirementFlags() { + LayoutBinder lb = new MockLayoutBinder(); + mExprModel = lb.getModel(); + IdentifierExpr a = lb.addVariable("a", "java.lang.String"); + IdentifierExpr b = lb.addVariable("b", "java.lang.String"); + IdentifierExpr c = lb.addVariable("c", "java.lang.String"); + IdentifierExpr d = lb.addVariable("d", "java.lang.String"); + IdentifierExpr e = lb.addVariable("e", "java.lang.String"); + final Expr aTernary = lb.parse("a == null ? b == null ? c : d : e"); + assertTrue(aTernary instanceof TernaryExpr); + final Expr bTernary = ((TernaryExpr) aTernary).getIfTrue(); + assertTrue(bTernary instanceof TernaryExpr); + final Expr aIsNull = mExprModel + .comparison("==", a, mExprModel.symbol("null", Object.class)); + final Expr bIsNull = mExprModel + .comparison("==", b, mExprModel.symbol("null", Object.class)); + lb.getModel().seal(); + Iterable<Expr> shouldRead = getShouldRead(); + // a and a == null + assertEquals(2, Iterables.size(shouldRead)); + assertFalse(a.getShouldReadFlags().isEmpty()); + assertTrue(a.getShouldReadFlags().get(a.getId())); + assertTrue(b.getShouldReadFlags().isEmpty()); + assertTrue(c.getShouldReadFlags().isEmpty()); + assertTrue(d.getShouldReadFlags().isEmpty()); + assertTrue(e.getShouldReadFlags().isEmpty()); + + Iterable<Expr> readFirst = getReadFirst(shouldRead, null); + assertEquals(1, Iterables.size(readFirst)); + final Expr first = Iterables.getFirst(readFirst, null); + assertSame(a, first); + assertTrue(mExprModel.markBitsRead()); + for (Expr expr : mExprModel.getPendingExpressions()) { + assertNull(expr.mShouldReadFlags); + } + shouldRead = getShouldRead(); + assertExactMatch(shouldRead, e, b, bIsNull); + + assertFlags(e, aTernary.getRequirementFlagIndex(false)); + + assertFlags(b, aTernary.getRequirementFlagIndex(true)); + assertFlags(bIsNull, aTernary.getRequirementFlagIndex(true)); + assertTrue(mExprModel.markBitsRead()); + shouldRead = getShouldRead(); + assertEquals(4, Iterables.size(shouldRead)); + assertTrue(Iterables.contains(shouldRead, c)); + assertTrue(Iterables.contains(shouldRead, d)); + assertTrue(Iterables.contains(shouldRead, aTernary)); + assertTrue(Iterables.contains(shouldRead, bTernary)); + + assertTrue(c.getShouldReadFlags().get(bTernary.getRequirementFlagIndex(true))); + assertEquals(1, c.getShouldReadFlags().cardinality()); + + assertTrue(d.getShouldReadFlags().get(bTernary.getRequirementFlagIndex(false))); + assertEquals(1, d.getShouldReadFlags().cardinality()); + + assertTrue(bTernary.getShouldReadFlags().get(aTernary.getRequirementFlagIndex(true))); + assertEquals(1, bTernary.getShouldReadFlags().cardinality()); + + assertEquals(5, aTernary.getShouldReadFlags().cardinality()); + for (Expr expr : new Expr[]{a, b, c, d, e}) { + assertTrue(aTernary.getShouldReadFlags().get(expr.getId())); + } + + readFirst = getReadFirst(shouldRead); + assertEquals(2, Iterables.size(readFirst)); + assertTrue(Iterables.contains(readFirst, c)); + assertTrue(Iterables.contains(readFirst, d)); + assertFalse(mExprModel.markBitsRead()); + } + + @Test + public void testPostConditionalDependencies() { + LayoutBinder lb = new MockLayoutBinder(); + mExprModel = lb.getModel(); + + IdentifierExpr u1 = lb.addVariable("u1", User.class.getCanonicalName()); + IdentifierExpr u2 = lb.addVariable("u2", User.class.getCanonicalName()); + IdentifierExpr a = lb.addVariable("a", int.class.getCanonicalName()); + IdentifierExpr b = lb.addVariable("b", int.class.getCanonicalName()); + IdentifierExpr c = lb.addVariable("c", int.class.getCanonicalName()); + IdentifierExpr d = lb.addVariable("d", int.class.getCanonicalName()); + IdentifierExpr e = lb.addVariable("e", int.class.getCanonicalName()); + TernaryExpr abTernary = parse(lb, "a > b ? u1.name : u2.name", TernaryExpr.class); + TernaryExpr bcTernary = parse(lb, "b > c ? u1.getCond(d) ? u1.lastName : u2.lastName : `xx`" + + " + u2.getCond(e) ", TernaryExpr.class); + Expr abCmp = abTernary.getPred(); + Expr bcCmp = bcTernary.getPred(); + Expr u1GetCondD = ((TernaryExpr) bcTernary.getIfTrue()).getPred(); + final MathExpr xxPlusU2getCondE = (MathExpr) bcTernary.getIfFalse(); + Expr u2GetCondE = xxPlusU2getCondE.getRight(); + Expr u1Name = abTernary.getIfTrue(); + Expr u2Name = abTernary.getIfFalse(); + Expr u1LastName = ((TernaryExpr) bcTernary.getIfTrue()).getIfTrue(); + Expr u2LastName = ((TernaryExpr) bcTernary.getIfTrue()).getIfFalse(); + + mExprModel.seal(); + Iterable<Expr> shouldRead = getShouldRead(); + + assertExactMatch(shouldRead, a, b, c, abCmp, bcCmp); + + Iterable<Expr> firstRead = getReadFirst(shouldRead); + + assertExactMatch(firstRead, a, b, c); + + assertFlags(a, a, b, u1, u2, u1Name, u2Name); + assertFlags(b, a, b, u1, u2, u1Name, u2Name, c, d, u1LastName, u2LastName, e); + assertFlags(c, b, c, u1, d, u1LastName, u2LastName, e); + assertFlags(abCmp, a, b, u1, u2, u1Name, u2Name); + assertFlags(bcCmp, b, c, u1, d, u1LastName, u2LastName, e); + + assertTrue(mExprModel.markBitsRead()); + + shouldRead = getShouldRead(); + Expr[] batch = {d, e, u1, u2, u1GetCondD, u2GetCondE, xxPlusU2getCondE, abTernary, + abTernary.getIfTrue(), abTernary.getIfFalse()}; + assertExactMatch(shouldRead, batch); + firstRead = getReadFirst(shouldRead); + assertExactMatch(firstRead, d, e, u1, u2); + + assertFlags(d, bcTernary.getRequirementFlagIndex(true)); + assertFlags(e, bcTernary.getRequirementFlagIndex(false)); + assertFlags(u1, bcTernary.getRequirementFlagIndex(true), + abTernary.getRequirementFlagIndex(true)); + assertFlags(u2, bcTernary.getRequirementFlagIndex(false), + abTernary.getRequirementFlagIndex(false)); + + assertFlags(u1GetCondD, bcTernary.getRequirementFlagIndex(true)); + assertFlags(u2GetCondE, bcTernary.getRequirementFlagIndex(false)); + assertFlags(xxPlusU2getCondE, bcTernary.getRequirementFlagIndex(false)); + assertFlags(abTernary, a, b, u1, u2, u1Name, u2Name); + assertFlags(abTernary.getIfTrue(), abTernary.getRequirementFlagIndex(true)); + assertFlags(abTernary.getIfFalse(), abTernary.getRequirementFlagIndex(false)); + + assertTrue(mExprModel.markBitsRead()); + + shouldRead = getShouldRead(); + // actually, there is no real case to read u1 anymore because if b>c was not true, + // u1.getCond(d) will never be set. Right now, we don't have mechanism to figure this out + // and also it does not affect correctness (just an unnecessary if stmt) + assertExactMatch(shouldRead, u2, u1LastName, u2LastName, bcTernary.getIfTrue(), bcTernary); + firstRead = getReadFirst(shouldRead); + assertExactMatch(firstRead, u1LastName, u2); + + assertFlags(u1LastName, bcTernary.getIfTrue().getRequirementFlagIndex(true)); + assertFlags(u2LastName, bcTernary.getIfTrue().getRequirementFlagIndex(false)); + assertFlags(u2, bcTernary.getIfTrue().getRequirementFlagIndex(false)); + + assertFlags(bcTernary.getIfTrue(), bcTernary.getRequirementFlagIndex(true)); + assertFlags(bcTernary, b, c, u1, u2, d, u1LastName, u2LastName, e); + } + + @Test + public void testCircularDependency() { + LayoutBinder lb = new MockLayoutBinder(); + mExprModel = lb.getModel(); + IdentifierExpr a = lb.addVariable("a", int.class.getCanonicalName()); + IdentifierExpr b = lb.addVariable("b", int.class.getCanonicalName()); + final TernaryExpr abTernary = parse(lb, "a > 3 ? a : b", TernaryExpr.class); + mExprModel.seal(); + Iterable<Expr> shouldRead = getShouldRead(); + assertExactMatch(shouldRead, a, abTernary.getPred()); + assertTrue(mExprModel.markBitsRead()); + shouldRead = getShouldRead(); + assertExactMatch(shouldRead, b, abTernary); + assertFalse(mExprModel.markBitsRead()); + } + + @Test + public void testNestedCircularDependency() { + LayoutBinder lb = new MockLayoutBinder(); + mExprModel = lb.getModel(); + IdentifierExpr a = lb.addVariable("a", int.class.getCanonicalName()); + IdentifierExpr b = lb.addVariable("b", int.class.getCanonicalName()); + IdentifierExpr c = lb.addVariable("c", int.class.getCanonicalName()); + final TernaryExpr a3Ternary = parse(lb, "a > 3 ? c > 4 ? a : b : c", TernaryExpr.class); + final TernaryExpr c4Ternary = (TernaryExpr) a3Ternary.getIfTrue(); + mExprModel.seal(); + Iterable<Expr> shouldRead = getShouldRead(); + assertExactMatch(shouldRead, a, a3Ternary.getPred()); + assertTrue(mExprModel.markBitsRead()); + shouldRead = getShouldRead(); + assertExactMatch(shouldRead, c, c4Ternary.getPred()); + assertFlags(c, a3Ternary.getRequirementFlagIndex(true), + a3Ternary.getRequirementFlagIndex(false)); + assertFlags(c4Ternary.getPred(), a3Ternary.getRequirementFlagIndex(true)); + } + + @Test + public void testNoFlagsForNonBindingStatic() { + LayoutBinder lb = new MockLayoutBinder(); + mExprModel = lb.getModel(); + lb.addVariable("a", int.class.getCanonicalName()); + final MathExpr parsed = parse(lb, "a * (3 + 2)", MathExpr.class); + mExprModel.seal(); + assertTrue(parsed.getRight().getInvalidFlags().isEmpty()); + assertEquals(1, parsed.getLeft().getInvalidFlags().cardinality()); + assertEquals(1, mExprModel.getInvalidateableFieldLimit()); + } + + @Test + public void testFlagsForBindingStatic() { + LayoutBinder lb = new MockLayoutBinder(); + mExprModel = lb.getModel(); + lb.addVariable("a", int.class.getCanonicalName()); + final Expr staticParsed = parse(lb, "3 + 2", MathExpr.class); + final MathExpr parsed = parse(lb, "a * (3 + 2)", MathExpr.class); + mExprModel.seal(); + assertTrue(staticParsed.isBindingExpression()); + assertEquals(1, staticParsed.getInvalidFlags().cardinality()); + assertEquals(parsed.getRight().getInvalidFlags(), staticParsed.getInvalidFlags()); + assertEquals(1, parsed.getLeft().getInvalidFlags().cardinality()); + assertEquals(2, mExprModel.getInvalidateableFieldLimit()); + } + + @Test + public void testPartialNeededRead() { + throw new NotImplementedException("create a test that has a variable which can be read for " + + "some flags and also may be read for some condition. Try both must match and" + + " partial match and none-match in conditionals"); + } + + private void assertFlags(Expr a, int... flags) { + BitSet bitset = new BitSet(); + for (int flag : flags) { + bitset.set(flag); + } + assertEquals("flag test for " + a.getUniqueKey(), bitset, a.getShouldReadFlags()); + } + + private void assertFlags(Expr a, Expr... exprs) { + BitSet bitSet = a.getShouldReadFlags(); + for (Expr expr : exprs) { + BitSet clone = (BitSet) bitSet.clone(); + clone.and(expr.getInvalidFlags()); + assertEquals("should read flags of " + a.getUniqueKey() + " should include " + expr + .getUniqueKey(), expr.getInvalidFlags(), clone); + } + + BitSet composite = new BitSet(); + for (Expr expr : exprs) { + composite.or(expr.getInvalidFlags()); + } + assertEquals("composite flags should match", composite, bitSet); + } + + private void assertExactMatch(Iterable<Expr> iterable, Expr... exprs) { + int i = 0; + log("list", iterable); + for (Expr expr : exprs) { + assertTrue((i++) + ":must contain " + expr.getUniqueKey(), + Iterables.contains(iterable, expr)); + } + i = 0; + for (Expr expr : iterable) { + assertTrue((i++) + ":must be expected " + expr.getUniqueKey(), + ArrayUtils.contains(exprs, expr)); + } + } + + private <T extends Expr> T parse(LayoutBinder binder, String input, Class<T> klass) { + final Expr parsed = binder.parse(input); + assertTrue(klass.isAssignableFrom(parsed.getClass())); + return (T) parsed; + } + + private void log(String s, Iterable<Expr> iterable) { + L.d(s); + for (Expr e : iterable) { + L.d(": %s : %s allFlags: %s readSoFar: %s", e.getUniqueKey(), e.getShouldReadFlags(), + e.getShouldReadFlagsWithConditionals(), e.getReadSoFar()); + } + L.d("end of %s", s); + } + + private Iterable<Expr> getReadFirst(Iterable<Expr> shouldRead) { + return getReadFirst(shouldRead, null); + } + + private Iterable<Expr> getReadFirst(Iterable<Expr> shouldRead, final Iterable<Expr> justRead) { + return Iterables.filter(shouldRead, new Predicate<Expr>() { + @Override + public boolean apply(Expr input) { + return input.shouldReadNow(justRead); + } + }); + } + + private Iterable<Expr> getShouldRead() { + return mExprModel.filterShouldRead(mExprModel.getPendingExpressions()); + } + + public static class User { + + String name; + + String lastName; + + public String getName() { + return name; + } + + public String getLastName() { + return lastName; + } + + public boolean getCond(int i) { + return true; + } + } +} diff --git a/tools/data-binding/compiler/src/test/java/android/databinding/tool/expr/ExprTest.java b/tools/data-binding/compiler/src/test/java/android/databinding/tool/expr/ExprTest.java new file mode 100644 index 0000000..f051fde --- /dev/null +++ b/tools/data-binding/compiler/src/test/java/android/databinding/tool/expr/ExprTest.java @@ -0,0 +1,163 @@ +/* + * 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 android.databinding.tool.expr; + +import org.junit.Before; +import org.junit.Test; + +import android.databinding.tool.reflection.ModelAnalyzer; +import android.databinding.tool.reflection.ModelClass; +import android.databinding.tool.reflection.java.JavaAnalyzer; + +import java.util.BitSet; +import java.util.List; + +import static org.junit.Assert.assertEquals; +import static org.junit.Assert.assertTrue; + +public class ExprTest{ + private static class DummyExpr extends Expr { + String mKey; + public DummyExpr(String key, DummyExpr... children) { + super(children); + mKey = key; + } + + @Override + protected ModelClass resolveType(ModelAnalyzer modelAnalyzer) { + return modelAnalyzer.findClass(Integer.class); + } + + @Override + protected List<Dependency> constructDependencies() { + return constructDynamicChildrenDependencies(); + } + + @Override + protected String computeUniqueKey() { + return mKey + super.computeUniqueKey(); + } + + @Override + public boolean isDynamic() { + return true; + } + } + + @Before + public void setUp() throws Exception { + JavaAnalyzer.initForTests(); + } + + @Test(expected=IllegalStateException.class) + public void testBadExpr() { + Expr expr = new Expr() { + @Override + protected ModelClass resolveType(ModelAnalyzer modelAnalyzer) { + return modelAnalyzer.findClass(Integer.class); + } + + @Override + protected List<Dependency> constructDependencies() { + return constructDynamicChildrenDependencies(); + } + }; + expr.getUniqueKey(); + } + + @Test + public void testBasicInvalidationFlag() { + DummyExpr d = new DummyExpr("a"); + d.setId(3); + d.enableDirectInvalidation(); + assertTrue(d.getInvalidFlags().get(3)); + } + + @Test + public void testCannotBeInvalidated() { + DummyExpr d = new DummyExpr("a"); + d.setId(3); + assertTrue(d.getInvalidFlags().isEmpty()); + } + + @Test + public void testInvalidationInheritance() { + ExprModel model = new ExprModel(); + DummyExpr a = model.register(new DummyExpr("a")); + DummyExpr b = model.register(new DummyExpr("b")); + DummyExpr c = model.register(new DummyExpr("c", a, b)); + a.enableDirectInvalidation(); + b.enableDirectInvalidation(); + c.setBindingExpression(true); + model.seal(); + assertFlags(c, a, b); + } + + @Test + public void testInvalidationInheritance2() { + ExprModel model = new ExprModel(); + DummyExpr a = model.register(new DummyExpr("a")); + DummyExpr b = model.register(new DummyExpr("b", a)); + DummyExpr c = model.register(new DummyExpr("c", b)); + a.enableDirectInvalidation(); + b.enableDirectInvalidation(); + c.setBindingExpression(true); + model.seal(); + assertFlags(c, a, b); + } + + @Test + public void testShouldReadFlags() { + ExprModel model = new ExprModel(); + DummyExpr a = model.register(new DummyExpr("a")); + a.enableDirectInvalidation(); + a.setBindingExpression(true); + model.seal(); + assertFlags(a, a); + } + + @Test + public void testShouldReadDependencyFlags() { + ExprModel model = new ExprModel(); + DummyExpr a = model.register(new DummyExpr("a")); + DummyExpr b = model.register(new DummyExpr("b", a)); + DummyExpr c = model.register(new DummyExpr("c", b)); + a.enableDirectInvalidation(); + b.enableDirectInvalidation(); + b.setBindingExpression(true); + c.setBindingExpression(true); + model.seal(); + assertFlags(b, a, b); + assertFlags(c, a, b); + } + + private void assertFlags(Expr a, Expr... exprs) { + BitSet bitSet = a.getShouldReadFlags(); + for (Expr expr : exprs) { + BitSet clone = (BitSet) bitSet.clone(); + clone.and(expr.getInvalidFlags()); + assertEquals("should read flags of " + a.getUniqueKey() + " should include " + expr + .getUniqueKey(), expr.getInvalidFlags(), clone); + } + + BitSet composite = new BitSet(); + for (Expr expr : exprs) { + composite.or(expr.getInvalidFlags()); + } + assertEquals("composite flags should match", composite, bitSet); + } +} diff --git a/tools/data-binding/compiler/src/test/java/android/databinding/tool/reflection/java/JavaAnalyzer.java b/tools/data-binding/compiler/src/test/java/android/databinding/tool/reflection/java/JavaAnalyzer.java new file mode 100644 index 0000000..19b14b4 --- /dev/null +++ b/tools/data-binding/compiler/src/test/java/android/databinding/tool/reflection/java/JavaAnalyzer.java @@ -0,0 +1,151 @@ +/* + * 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 android.databinding.tool.reflection.java; + +import com.google.common.collect.ImmutableMap; + +import android.databinding.tool.reflection.ModelAnalyzer; +import android.databinding.tool.reflection.ModelClass; +import android.databinding.tool.reflection.SdkUtil; +import android.databinding.tool.reflection.TypeUtil; +import android.databinding.tool.util.L; + +import java.io.File; +import java.net.MalformedURLException; +import java.net.URL; +import java.net.URLClassLoader; +import java.util.HashMap; +import java.util.Map; + +public class JavaAnalyzer extends ModelAnalyzer { + public static final Map<String, Class> PRIMITIVE_TYPES = + new ImmutableMap.Builder<String, Class>() + .put("boolean", boolean.class) + .put("byte", byte.class) + .put("short", short.class) + .put("char", char.class) + .put("int", int.class) + .put("long", long.class) + .put("float", float.class) + .put("double", double.class) + .build(); + + private HashMap<String, JavaClass> mClassCache = new HashMap<String, JavaClass>(); + + private final ClassLoader mClassLoader; + + public JavaAnalyzer(ClassLoader classLoader) { + setInstance(this); + mClassLoader = classLoader; + } + + @Override + public JavaClass loadPrimitive(String className) { + Class clazz = PRIMITIVE_TYPES.get(className); + if (clazz == null) { + return null; + } else { + return new JavaClass(clazz); + } + } + + @Override + public ModelClass findClass(String className, Map<String, String> imports) { + // TODO handle imports + JavaClass 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'); + JavaClass baseClass = (JavaClass) findClass( + className.substring(indexOfL + 1, className.length() - 1), null); + String realClassName = className.substring(0, indexOfL + 1) + + baseClass.mClass.getCanonicalName() + ';'; + loaded = new JavaClass(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); + } + } + // expr visitor may call this to resolve statics. Sometimes, it is OK not to find a class. + if (loaded == null) { + return null; + } + L.d("loaded class %s", loaded.mClass.getCanonicalName()); + return loaded; + } + + @Override + public ModelClass findClass(Class classType) { + return new JavaClass(classType); + } + + @Override + public TypeUtil createTypeUtil() { + return new JavaTypeUtil(); + } + + private JavaClass loadRecursively(String className) throws ClassNotFoundException { + try { + L.d("recursively checking %s", className); + return new JavaClass(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() { + Map<String, String> env = System.getenv(); + for (Map.Entry<String, String> entry : env.entrySet()) { + L.d("%s %s", entry.getKey(), entry.getValue()); + } + String androidHome = env.get("ANDROID_HOME"); + if (androidHome == null) { + throw new IllegalStateException( + "you need to have ANDROID_HOME set in your environment" + + " to run compiler tests"); + } + File androidJar = new File(androidHome + "/platforms/android-21/android.jar"); + if (!androidJar.exists() || !androidJar.canRead()) { + throw new IllegalStateException( + "cannot find android jar at " + androidJar.getAbsolutePath()); + } + // now load android data binding library as well + + try { + ClassLoader classLoader = new URLClassLoader(new URL[]{androidJar.toURI().toURL()}, + ModelAnalyzer.class.getClassLoader()); + new JavaAnalyzer(classLoader); + } catch (MalformedURLException e) { + throw new RuntimeException("cannot create class loader", e); + } + + SdkUtil.initialize(8, new File(androidHome)); + } +} diff --git a/tools/data-binding/compiler/src/test/java/android/databinding/tool/reflection/java/JavaClass.java b/tools/data-binding/compiler/src/test/java/android/databinding/tool/reflection/java/JavaClass.java new file mode 100644 index 0000000..121a569 --- /dev/null +++ b/tools/data-binding/compiler/src/test/java/android/databinding/tool/reflection/java/JavaClass.java @@ -0,0 +1,242 @@ +/* + * 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 android.databinding.tool.reflection.java; + +import android.databinding.tool.reflection.ModelClass; +import android.databinding.tool.reflection.ModelField; +import android.databinding.tool.reflection.ModelMethod; +import android.databinding.tool.reflection.TypeUtil; + +import java.lang.reflect.Field; +import java.lang.reflect.Method; + +public class JavaClass extends ModelClass { + public final Class mClass; + + public JavaClass(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 ModelClass getComponentType() { + if (mClass.isArray()) { + return new JavaClass(mClass.getComponentType()); + } else if (isList() || isMap()) { + return new JavaClass(Object.class); + } else { + return null; + } + } + + @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 isVoid() { + return void.class.equals(mClass); + } + + @Override + public ModelClass unbox() { + if (mClass.isPrimitive()) { + return this; + } + if (Integer.class.equals(mClass)) { + return new JavaClass(int.class); + } else if (Long.class.equals(mClass)) { + return new JavaClass(long.class); + } else if (Short.class.equals(mClass)) { + return new JavaClass(short.class); + } else if (Byte.class.equals(mClass)) { + return new JavaClass(byte.class); + } else if (Character.class.equals(mClass)) { + return new JavaClass(char.class); + } else if (Double.class.equals(mClass)) { + return new JavaClass(double.class); + } else if (Float.class.equals(mClass)) { + return new JavaClass(float.class); + } else if (Boolean.class.equals(mClass)) { + return new JavaClass(boolean.class); + } else { + // not a boxed type + return this; + } + + } + + @Override + public JavaClass box() { + if (!mClass.isPrimitive()) { + return this; + } + if (int.class.equals(mClass)) { + return new JavaClass(Integer.class); + } else if (long.class.equals(mClass)) { + return new JavaClass(Long.class); + } else if (short.class.equals(mClass)) { + return new JavaClass(Short.class); + } else if (byte.class.equals(mClass)) { + return new JavaClass(Byte.class); + } else if (char.class.equals(mClass)) { + return new JavaClass(Character.class); + } else if (double.class.equals(mClass)) { + return new JavaClass(Double.class); + } else if (float.class.equals(mClass)) { + return new JavaClass(Float.class); + } else if (boolean.class.equals(mClass)) { + return new JavaClass(Boolean.class); + } else { + // not a valid type? + return this; + } + } + + @Override + public boolean isAssignableFrom(ModelClass that) { + Class thatClass = ((JavaClass) that).mClass; + return mClass.isAssignableFrom(thatClass); + } + + @Override + public ModelClass getSuperclass() { + if (mClass.getSuperclass() == null) { + return null; + } + return new JavaClass(mClass.getSuperclass()); + } + + @Override + public String getCanonicalName() { + return mClass.getCanonicalName(); + } + + @Override + public ModelClass erasure() { + return this; + } + + @Override + public String getJniDescription() { + return TypeUtil.getInstance().getDescription(this); + } + + @Override + protected ModelField[] getDeclaredFields() { + Field[] fields = mClass.getDeclaredFields(); + ModelField[] modelFields; + if (fields == null) { + modelFields = new ModelField[0]; + } else { + modelFields = new ModelField[fields.length]; + for (int i = 0; i < fields.length; i++) { + modelFields[i] = new JavaField(fields[i]); + } + } + return modelFields; + } + + @Override + protected ModelMethod[] getDeclaredMethods() { + Method[] methods = mClass.getDeclaredMethods(); + if (methods == null) { + return new ModelMethod[0]; + } else { + ModelMethod[] classMethods = new ModelMethod[methods.length]; + for (int i = 0; i < methods.length; i++) { + classMethods[i] = new JavaMethod(methods[i]); + } + return classMethods; + } + } + + @Override + public boolean equals(Object obj) { + if (obj instanceof JavaClass) { + return mClass.equals(((JavaClass) obj).mClass); + } else { + return false; + } + } + + @Override + public int hashCode() { + return mClass.hashCode(); + } +} diff --git a/tools/data-binding/compiler/src/test/java/android/databinding/tool/reflection/java/JavaField.java b/tools/data-binding/compiler/src/test/java/android/databinding/tool/reflection/java/JavaField.java new file mode 100644 index 0000000..6821f16 --- /dev/null +++ b/tools/data-binding/compiler/src/test/java/android/databinding/tool/reflection/java/JavaField.java @@ -0,0 +1,59 @@ +/* + * 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 android.databinding.tool.reflection.java; + +import android.databinding.Bindable; +import android.databinding.tool.reflection.ModelClass; +import android.databinding.tool.reflection.ModelField; + +import java.lang.reflect.Field; +import java.lang.reflect.Modifier; + +public class JavaField extends ModelField { + public final Field mField; + + public JavaField(Field field) { + mField = field; + } + + @Override + public boolean isBindable() { + return mField.getAnnotation(Bindable.class) != null; + } + + @Override + public String getName() { + return mField.getName(); + } + + @Override + public boolean isPublic() { + return Modifier.isPublic(mField.getModifiers()); + } + + @Override + public boolean isStatic() { + return Modifier.isStatic(mField.getModifiers()); + } + + @Override + public boolean isFinal() { + return Modifier.isFinal(mField.getModifiers()); + } + + @Override + public ModelClass getFieldType() { + return new JavaClass(mField.getType()); + } +} diff --git a/tools/data-binding/compiler/src/test/java/android/databinding/tool/reflection/java/JavaMethod.java b/tools/data-binding/compiler/src/test/java/android/databinding/tool/reflection/java/JavaMethod.java new file mode 100644 index 0000000..4ef566f --- /dev/null +++ b/tools/data-binding/compiler/src/test/java/android/databinding/tool/reflection/java/JavaMethod.java @@ -0,0 +1,94 @@ +/* + * 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 android.databinding.tool.reflection.java; + + +import android.databinding.Bindable; +import android.databinding.tool.reflection.ModelClass; +import android.databinding.tool.reflection.ModelMethod; +import android.databinding.tool.reflection.SdkUtil; +import android.databinding.tool.reflection.TypeUtil; + +import java.lang.reflect.Method; +import java.lang.reflect.Modifier; +import java.util.List; + +public class JavaMethod extends ModelMethod { + public final Method mMethod; + + public JavaMethod(Method method) { + mMethod = method; + } + + + @Override + public ModelClass getDeclaringClass() { + return new JavaClass(mMethod.getDeclaringClass()); + } + + @Override + public ModelClass[] getParameterTypes() { + Class[] parameterTypes = mMethod.getParameterTypes(); + ModelClass[] parameterClasses = new ModelClass[parameterTypes.length]; + for (int i = 0; i < parameterTypes.length; i++) { + parameterClasses[i] = new JavaClass(parameterTypes[i]); + } + return parameterClasses; + } + + @Override + public String getName() { + return mMethod.getName(); + } + + @Override + public ModelClass getReturnType(List<ModelClass> args) { + return new JavaClass(mMethod.getReturnType()); + } + + @Override + public boolean isVoid() { + return void.class.equals(mMethod.getReturnType()); + } + + @Override + public boolean isPublic() { + return Modifier.isPublic(mMethod.getModifiers()); + } + + @Override + public boolean isStatic() { + return Modifier.isStatic(mMethod.getModifiers()); + } + + @Override + public boolean isBindable() { + return mMethod.getAnnotation(Bindable.class) != null; + } + + @Override + public int getMinApi() { + return SdkUtil.getMinApi(this); + } + + @Override + public String getJniDescription() { + return TypeUtil.getInstance().getDescription(this); + } + + @Override + public boolean isVarArgs() { + return false; + } +} diff --git a/tools/data-binding/compiler/src/test/java/android/databinding/tool/reflection/java/JavaTypeUtil.java b/tools/data-binding/compiler/src/test/java/android/databinding/tool/reflection/java/JavaTypeUtil.java new file mode 100644 index 0000000..33bff3b --- /dev/null +++ b/tools/data-binding/compiler/src/test/java/android/databinding/tool/reflection/java/JavaTypeUtil.java @@ -0,0 +1,89 @@ +/* + * 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 android.databinding.tool.reflection.java; + +import android.databinding.tool.reflection.ModelClass; +import android.databinding.tool.reflection.ModelMethod; +import android.databinding.tool.reflection.TypeUtil; +import android.databinding.tool.util.L; + +import java.lang.reflect.Array; +import java.lang.reflect.Method; + +public class JavaTypeUtil extends TypeUtil { + + @Override + public String getDescription(ModelClass modelClass) { + return modelClass.getCanonicalName().replace('.', '/'); + } + + @Override + public String getDescription(ModelMethod modelMethod) { + Method method = ((JavaMethod) modelMethod).mMethod; + StringBuilder sb = new StringBuilder(); + sb.append(method.getName()); + sb.append("("); + for (Class param : method.getParameterTypes()) { + sb.append(getDescription(param)); + } + sb.append(")"); + sb.append(getDescription(method.getReturnType())); + return sb.toString(); + } + + private String getDescription(Class klass) { + if (klass == null) { + throw new UnsupportedOperationException(); + } + if (boolean.class.equals(klass)) { + return BOOLEAN; + } + if (byte.class.equals(klass)) { + return BYTE; + } + if (short.class.equals(klass)) { + return SHORT; + } + if (int.class.equals(klass)) { + return INT; + } + if (long.class.equals(klass)) { + return LONG; + } + if (char.class.equals(klass)) { + return CHAR; + } + if (float.class.equals(klass)) { + return FLOAT; + } + if (double.class.equals(klass)) { + return DOUBLE; + } + if (void.class.equals(klass)) { + return VOID; + } + if (Object.class.isAssignableFrom(klass)) { + return CLASS_PREFIX + klass.getCanonicalName().replace('.', '/') + CLASS_SUFFIX; + } + if (Array.class.isAssignableFrom(klass)) { + return ARRAY + getDescription(klass.getComponentType()); + } + + UnsupportedOperationException ex + = new UnsupportedOperationException("cannot understand type " + + klass.toString() + ", kind:"); + L.e(ex, "cannot create JNI type for %s", klass.getCanonicalName()); + throw ex; + } +} diff --git a/tools/data-binding/compiler/src/test/java/android/databinding/tool/writer/FlagSetTest.java b/tools/data-binding/compiler/src/test/java/android/databinding/tool/writer/FlagSetTest.java new file mode 100644 index 0000000..327593a --- /dev/null +++ b/tools/data-binding/compiler/src/test/java/android/databinding/tool/writer/FlagSetTest.java @@ -0,0 +1,60 @@ +/* + * 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 android.databinding.tool.writer; + +import org.junit.Test; + +import java.util.BitSet; + +import static org.junit.Assert.assertEquals; + +public class FlagSetTest { + @Test + public void testSimple1Level() { + BitSet bs = new BitSet(); + bs.set(7); + FlagSet flagSet = new FlagSet(bs, 3); + assertEquals(3, flagSet.buckets.length); + assertEquals(1 << 7, flagSet.buckets[0]); + assertEquals(0, flagSet.buckets[1]); + assertEquals(0, flagSet.buckets[2]); + } + + @Test + public void testSimple2Level() { + BitSet bs = new BitSet(); + bs.set(FlagSet.sBucketSize + 2); + FlagSet flagSet = new FlagSet(bs, 3); + assertEquals(3, flagSet.buckets.length); + assertEquals(0, flagSet.buckets[0]); + assertEquals(1 << 2, flagSet.buckets[1]); + assertEquals(0, flagSet.buckets[2]); + } + + @Test + public void testSimple3Level() { + BitSet bs = new BitSet(); + bs.set(5); + bs.set(FlagSet.sBucketSize + 2); + bs.set(FlagSet.sBucketSize * 2 + 10); + FlagSet flagSet = new FlagSet(bs, 3); + assertEquals(3, flagSet.buckets.length); + assertEquals(1 << 5, flagSet.buckets[0]); + assertEquals(1 << 2, flagSet.buckets[1]); + assertEquals(1 << 10, flagSet.buckets[2]); + } +} diff --git a/tools/data-binding/databinding.properties b/tools/data-binding/databinding.properties new file mode 100644 index 0000000..0bda1db --- /dev/null +++ b/tools/data-binding/databinding.properties @@ -0,0 +1,11 @@ +# global settings for projects +kotlinVersion = 0.11.91 +releaseVersion = 0.3 +snapshotVersion = 0.3-SNAPSHOT +androidPluginVersion = 1.0.1 +javaTargetCompatibility = 1.6 +javaSourceCompatibility = 1.6 +mavenRepoName=maven-repo +group=com.android.databinding +testGroup=com.android.databinding.test + diff --git a/tools/data-binding/extensions/baseAdapters/build.gradle b/tools/data-binding/extensions/baseAdapters/build.gradle new file mode 100644 index 0000000..34ca4b9 --- /dev/null +++ b/tools/data-binding/extensions/baseAdapters/build.gradle @@ -0,0 +1,96 @@ +/* + * 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. + */ + + +apply plugin: 'maven' +apply plugin: 'com.android.library' +apply plugin: 'com.android.databinding' + +android { + compileSdkVersion 21 + buildToolsVersion "21.1.2" + + defaultConfig { + minSdkVersion 7 + targetSdkVersion 21 + versionCode 1 + versionName "1.0" + } + buildTypes { + release { + minifyEnabled false + proguardFiles getDefaultProguardFile('proguard-android.txt'), 'proguard-rules.pro' + } + } + + packagingOptions { + exclude 'META-INF/services/javax.annotation.processing.Processor' + exclude 'META-INF/LICENSE.txt' + exclude 'META-INF/NOTICE.txt' + } +} + +dependencies { + compile "com.android.databinding:baseLibrary:${config.snapshotVersion}" + provided "com.android.databinding:annotationprocessor:${config.snapshotVersion}" + compile 'com.android.support:support-v4:+' + compile 'com.android.support:cardview-v7:+' + compile 'com.android.support:appcompat-v7:+' +} + +configurations { + jarArchives +} + + +//create jar tasks +android.libraryVariants.all { variant -> + def name = variant.buildType.name + + if (name.equals(com.android.builder.core.BuilderConstants.DEBUG)) { + return; // Skip debug builds. + } + // @Jar version is needed to run compiler tests + def task = project.tasks.create "jar${name.capitalize()}", Jar + task.dependsOn variant.javaCompile + task.from variant.javaCompile.destinationDir + def packageName = "com.android.databinding.library.baseAdapters" + def appPkgAsClass = packageName.replace('.', '/') + task.exclude("android/databinding/layouts/*.*") + task.exclude("$appPkgAsClass/databinding/*") + task.exclude("$appPkgAsClass/BR.*") + artifacts.add('jarArchives', task); +} + +uploadArchives { +} + +uploadJarArchives { + repositories { + mavenDeployer { + repository(url: "file://${config.mavenRepoDir}") + pom.artifactId = "adapters" + pom.whenConfigured { + println("configured pom, $it") + it.dependencies.find {dep -> dep.groupId == 'com.android.support' && dep.artifactId == 'support-v4' }.optional = true + it.dependencies.find {dep -> dep.groupId == 'com.android.support' && dep.artifactId == 'cardview-v7' }.optional = true + it.dependencies.find {dep -> dep.groupId == 'com.android.support' && dep.artifactId == 'appcompat-v7' }.optional = true + } + } + } +} + +uploadArchives.dependsOn uploadJarArchives diff --git a/tools/data-binding/extensions/baseAdapters/src/main/AndroidManifest.xml b/tools/data-binding/extensions/baseAdapters/src/main/AndroidManifest.xml new file mode 100644 index 0000000..38cf779 --- /dev/null +++ b/tools/data-binding/extensions/baseAdapters/src/main/AndroidManifest.xml @@ -0,0 +1,19 @@ +<!-- + ~ 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. + --> + +<manifest xmlns:android="http://schemas.android.com/apk/res/android" + package="com.android.databinding.library.baseAdapters"> +</manifest> diff --git a/tools/data-binding/extensions/baseAdapters/src/main/java/android/databinding/adapters/AbsListViewBindingAdapter.java b/tools/data-binding/extensions/baseAdapters/src/main/java/android/databinding/adapters/AbsListViewBindingAdapter.java new file mode 100644 index 0000000..e645bff --- /dev/null +++ b/tools/data-binding/extensions/baseAdapters/src/main/java/android/databinding/adapters/AbsListViewBindingAdapter.java @@ -0,0 +1,28 @@ +/* + * 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 android.databinding.adapters; + +import android.databinding.BindingMethod; +import android.databinding.BindingMethods; + +@BindingMethods({ + @BindingMethod(type = "android.widget.AbsListView", attribute = "android:listSelector", method = "setSelector"), + @BindingMethod(type = "android.widget.AbsListView", attribute = "android:scrollingCache", method = "setScrollingCacheEnabled"), + @BindingMethod(type = "android.widget.AbsListView", attribute = "android:smoothScrollbar", method = "setSmoothScrollbarEnabled"), +}) +public class AbsListViewBindingAdapter { + +} diff --git a/tools/data-binding/extensions/baseAdapters/src/main/java/android/databinding/adapters/AbsSeekBarBindingAdapter.java b/tools/data-binding/extensions/baseAdapters/src/main/java/android/databinding/adapters/AbsSeekBarBindingAdapter.java new file mode 100644 index 0000000..4494ec7 --- /dev/null +++ b/tools/data-binding/extensions/baseAdapters/src/main/java/android/databinding/adapters/AbsSeekBarBindingAdapter.java @@ -0,0 +1,27 @@ +/* + * Copyright (C) 2015 The Android Open Source Project + * + * Licensed under the Apache License, Version 2.0 (the "License"); + * you may not use this file except in compliance with the License. + * You may obtain a copy of the License at + * + * http://www.apache.org/licenses/LICENSE-2.0 + * + * Unless required by applicable law or agreed to in writing, software + * distributed under the License is distributed on an "AS IS" BASIS, + * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. + * See the License for the specific language governing permissions and + * limitations under the License. + */ +package android.databinding.adapters; + +import android.databinding.BindingMethod; +import android.databinding.BindingMethods; + +@BindingMethods({ + @BindingMethod(type = "android.widget.AbsSeekBar", attribute = "android:thumbTint", method = "setThumbTintList"), + +}) +public class AbsSeekBarBindingAdapter { + +} diff --git a/tools/data-binding/extensions/baseAdapters/src/main/java/android/databinding/adapters/AbsSpinnerBindingAdapter.java b/tools/data-binding/extensions/baseAdapters/src/main/java/android/databinding/adapters/AbsSpinnerBindingAdapter.java new file mode 100644 index 0000000..adf84b2 --- /dev/null +++ b/tools/data-binding/extensions/baseAdapters/src/main/java/android/databinding/adapters/AbsSpinnerBindingAdapter.java @@ -0,0 +1,50 @@ +/* + * 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 android.databinding.adapters; + +import android.databinding.BindingAdapter; +import android.widget.AbsSpinner; +import android.widget.ArrayAdapter; +import android.widget.SpinnerAdapter; + +public class AbsSpinnerBindingAdapter { + + @BindingAdapter("android:entries") + public static void setEntries(AbsSpinner view, CharSequence[] entries) { + if (entries != null) { + SpinnerAdapter oldAdapter = view.getAdapter(); + boolean changed = true; + if (oldAdapter != null && oldAdapter.getCount() == entries.length) { + changed = false; + for (int i = 0; i < entries.length; i++) { + if (!entries[i].equals(oldAdapter.getItem(i))) { + changed = true; + break; + } + } + } + if (changed) { + ArrayAdapter<CharSequence> adapter = + new ArrayAdapter<CharSequence>(view.getContext(), + android.R.layout.simple_spinner_item, entries); + adapter.setDropDownViewResource(android.R.layout.simple_spinner_dropdown_item); + view.setAdapter(adapter); + } + } else { + view.setAdapter(null); + } + } +} diff --git a/tools/data-binding/extensions/baseAdapters/src/main/java/android/databinding/adapters/AutoCompleteTextViewBindingAdapter.java b/tools/data-binding/extensions/baseAdapters/src/main/java/android/databinding/adapters/AutoCompleteTextViewBindingAdapter.java new file mode 100644 index 0000000..334d818 --- /dev/null +++ b/tools/data-binding/extensions/baseAdapters/src/main/java/android/databinding/adapters/AutoCompleteTextViewBindingAdapter.java @@ -0,0 +1,27 @@ +/* + * Copyright (C) 2015 The Android Open Source Project + * + * Licensed under the Apache License, Version 2.0 (the "License"); + * you may not use this file except in compliance with the License. + * You may obtain a copy of the License at + * + * http://www.apache.org/licenses/LICENSE-2.0 + * + * Unless required by applicable law or agreed to in writing, software + * distributed under the License is distributed on an "AS IS" BASIS, + * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. + * See the License for the specific language governing permissions and + * limitations under the License. + */ +package android.databinding.adapters; + +import android.databinding.BindingMethod; +import android.databinding.BindingMethods; + +@BindingMethods({ + @BindingMethod(type = "android.widget.AutoCompleteTextView", attribute = "android:completionThreshold", method = "setThreshold"), + @BindingMethod(type = "android.widget.AutoCompleteTextView", attribute = "android:popupBackground", method = "setDropDownBackgroundDrawable"), +}) +public class AutoCompleteTextViewBindingAdapter { + +} diff --git a/tools/data-binding/extensions/baseAdapters/src/main/java/android/databinding/adapters/CardViewBindingAdapter.java b/tools/data-binding/extensions/baseAdapters/src/main/java/android/databinding/adapters/CardViewBindingAdapter.java new file mode 100644 index 0000000..0545141 --- /dev/null +++ b/tools/data-binding/extensions/baseAdapters/src/main/java/android/databinding/adapters/CardViewBindingAdapter.java @@ -0,0 +1,67 @@ +/* + * 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 android.databinding.adapters; + +import android.databinding.BindingAdapter; +import android.databinding.BindingMethod; +import android.databinding.BindingMethods; +import android.support.v7.widget.CardView; + +@BindingMethods({ + @BindingMethod(type = "android.support.v7.widget.CardView", attribute = "cardCornerRadius", method = "setRadius"), + @BindingMethod(type = "android.support.v7.widget.CardView", attribute = "cardMaxElevation", method = "setMaxCardElevation"), + @BindingMethod(type = "android.support.v7.widget.CardView", attribute = "cardPreventCornerOverlap", method = "setPreventCornerOverlap"), + @BindingMethod(type = "android.support.v7.widget.CardView", attribute = "cardUseCompatPadding", method = "setUseCompatPadding"), +}) +public class CardViewBindingAdapter { + + @BindingAdapter("contentPadding") + public static void setContentPadding(CardView view, int padding) { + view.setContentPadding(padding, padding, padding, padding); + } + + @BindingAdapter("contentPaddingLeft") + public static void setContentPaddingLeft(CardView view, int left) { + int top = view.getContentPaddingTop(); + int right = view.getContentPaddingRight(); + int bottom = view.getContentPaddingBottom(); + view.setContentPadding(left, top, right, bottom); + } + + @BindingAdapter("contentPaddingTop") + public static void setContentPaddingTop(CardView view, int top) { + int left = view.getContentPaddingLeft(); + int right = view.getContentPaddingRight(); + int bottom = view.getContentPaddingBottom(); + view.setContentPadding(left, top, right, bottom); + } + + @BindingAdapter("contentPaddingRight") + public static void setContentPaddingRight(CardView view, int right) { + int left = view.getContentPaddingLeft(); + int top = view.getContentPaddingTop(); + int bottom = view.getContentPaddingBottom(); + view.setContentPadding(left, top, right, bottom); + } + + @BindingAdapter("contentPaddingBottom") + public static void setContentPaddingBottom(CardView view, int bottom) { + int left = view.getContentPaddingLeft(); + int top = view.getContentPaddingTop(); + int right = view.getContentPaddingRight(); + view.setContentPadding(left, top, right, bottom); + } +} diff --git a/tools/data-binding/extensions/baseAdapters/src/main/java/android/databinding/adapters/CheckedTextViewBindingAdapter.java b/tools/data-binding/extensions/baseAdapters/src/main/java/android/databinding/adapters/CheckedTextViewBindingAdapter.java new file mode 100644 index 0000000..aa16057 --- /dev/null +++ b/tools/data-binding/extensions/baseAdapters/src/main/java/android/databinding/adapters/CheckedTextViewBindingAdapter.java @@ -0,0 +1,27 @@ +/* + * Copyright (C) 2015 The Android Open Source Project + * + * Licensed under the Apache License, Version 2.0 (the "License"); + * you may not use this file except in compliance with the License. + * You may obtain a copy of the License at + * + * http://www.apache.org/licenses/LICENSE-2.0 + * + * Unless required by applicable law or agreed to in writing, software + * distributed under the License is distributed on an "AS IS" BASIS, + * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. + * See the License for the specific language governing permissions and + * limitations under the License. + */ +package android.databinding.adapters; + +import android.databinding.BindingMethod; +import android.databinding.BindingMethods; + +@BindingMethods({ + @BindingMethod(type = "android.widget.CheckedTextView", attribute = "android:checkMark", method = "setCheckMarkDrawable"), + @BindingMethod(type = "android.widget.CheckedTextView", attribute = "android:checkMarkTint", method = "setCheckMarkTintList"), +}) +public class CheckedTextViewBindingAdapter { + +} diff --git a/tools/data-binding/extensions/baseAdapters/src/main/java/android/databinding/adapters/CompoundButtonBindingAdapter.java b/tools/data-binding/extensions/baseAdapters/src/main/java/android/databinding/adapters/CompoundButtonBindingAdapter.java new file mode 100644 index 0000000..9ed5dd7 --- /dev/null +++ b/tools/data-binding/extensions/baseAdapters/src/main/java/android/databinding/adapters/CompoundButtonBindingAdapter.java @@ -0,0 +1,26 @@ +/* + * 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 android.databinding.adapters; + +import android.databinding.BindingMethod; +import android.databinding.BindingMethods; + +@BindingMethods({ + @BindingMethod(type = "android.widget.CompoundButton", attribute = "android:buttonTint", method = "setButtonTintList"), +}) +public class CompoundButtonBindingAdapter { + +} diff --git a/tools/data-binding/extensions/baseAdapters/src/main/java/android/databinding/adapters/Converters.java b/tools/data-binding/extensions/baseAdapters/src/main/java/android/databinding/adapters/Converters.java new file mode 100644 index 0000000..44eda4d --- /dev/null +++ b/tools/data-binding/extensions/baseAdapters/src/main/java/android/databinding/adapters/Converters.java @@ -0,0 +1,32 @@ +/* + * 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 android.databinding.adapters; + +import android.databinding.BindingConversion; +import android.content.res.ColorStateList; +import android.graphics.drawable.ColorDrawable; + +public class Converters { + @BindingConversion + public static ColorDrawable convertColorToDrawable(int color) { + return new ColorDrawable(color); + } + + @BindingConversion + public static ColorStateList convertColorToColorStateList(int color) { + return ColorStateList.valueOf(color); + } +} diff --git a/tools/data-binding/extensions/baseAdapters/src/main/java/android/databinding/adapters/FrameLayoutBindingAdapter.java b/tools/data-binding/extensions/baseAdapters/src/main/java/android/databinding/adapters/FrameLayoutBindingAdapter.java new file mode 100644 index 0000000..82641a6 --- /dev/null +++ b/tools/data-binding/extensions/baseAdapters/src/main/java/android/databinding/adapters/FrameLayoutBindingAdapter.java @@ -0,0 +1,26 @@ +/* + * 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 android.databinding.adapters; + +import android.databinding.BindingMethod; +import android.databinding.BindingMethods; + +@BindingMethods({ + @BindingMethod(type = "android.widget.FrameLayout", attribute = "android:foregroundTint", method = "setForegroundTintList"), +}) +public class FrameLayoutBindingAdapter { + +} diff --git a/tools/data-binding/extensions/baseAdapters/src/main/java/android/databinding/adapters/ImageViewBindingAdapter.java b/tools/data-binding/extensions/baseAdapters/src/main/java/android/databinding/adapters/ImageViewBindingAdapter.java new file mode 100644 index 0000000..9be1a14 --- /dev/null +++ b/tools/data-binding/extensions/baseAdapters/src/main/java/android/databinding/adapters/ImageViewBindingAdapter.java @@ -0,0 +1,28 @@ +/* + * 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 android.databinding.adapters; + +import android.databinding.BindingMethod; +import android.databinding.BindingMethods; + +@BindingMethods({ + @BindingMethod(type = "android.widget.ImageView", attribute = "android:src", method = "setImageDrawable"), + @BindingMethod(type = "android.widget.ImageView", attribute = "android:tint", method = "setImageTintList"), + @BindingMethod(type = "android.widget.ImageView", attribute = "android:tintMode", method = "setImageTintMode"), +}) +public class ImageViewBindingAdapter { + +} diff --git a/tools/data-binding/extensions/baseAdapters/src/main/java/android/databinding/adapters/LinearLayoutBindingAdapter.java b/tools/data-binding/extensions/baseAdapters/src/main/java/android/databinding/adapters/LinearLayoutBindingAdapter.java new file mode 100644 index 0000000..7bb85e9 --- /dev/null +++ b/tools/data-binding/extensions/baseAdapters/src/main/java/android/databinding/adapters/LinearLayoutBindingAdapter.java @@ -0,0 +1,27 @@ +/* + * Copyright (C) 2015 The Android Open Source Project + * + * Licensed under the Apache License, Version 2.0 (the "License"); + * you may not use this file except in compliance with the License. + * You may obtain a copy of the License at + * + * http://www.apache.org/licenses/LICENSE-2.0 + * + * Unless required by applicable law or agreed to in writing, software + * distributed under the License is distributed on an "AS IS" BASIS, + * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. + * See the License for the specific language governing permissions and + * limitations under the License. + */ +package android.databinding.adapters; + +import android.databinding.BindingMethod; +import android.databinding.BindingMethods; + +@BindingMethods({ + @BindingMethod(type = "android.widget.LinearLayout", attribute = "android:divider", method = "setDividerDrawable"), + @BindingMethod(type = "android.widget.LinearLayout", attribute = "android:measureWithLargestChild", method = "setMeasureWithLargestChildEnabled"), +}) +public class LinearLayoutBindingAdapter { + +} diff --git a/tools/data-binding/extensions/baseAdapters/src/main/java/android/databinding/adapters/ProgressBarBindingAdapter.java b/tools/data-binding/extensions/baseAdapters/src/main/java/android/databinding/adapters/ProgressBarBindingAdapter.java new file mode 100644 index 0000000..fdbab8f --- /dev/null +++ b/tools/data-binding/extensions/baseAdapters/src/main/java/android/databinding/adapters/ProgressBarBindingAdapter.java @@ -0,0 +1,28 @@ +/* + * 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 android.databinding.adapters; + +import android.databinding.BindingMethod; +import android.databinding.BindingMethods; + +@BindingMethods({ + @BindingMethod(type = "android.widget.ProgressBar", attribute = "android:indeterminateTint", method = "setIndeterminateTintList"), + @BindingMethod(type = "android.widget.ProgressBar", attribute = "android:progressTint", method = "setProgressTintList"), + @BindingMethod(type = "android.widget.ProgressBar", attribute = "android:secondaryProgressTint", method = "setSecondaryProgressTintList"), +}) +public class ProgressBarBindingAdapter { + +} diff --git a/tools/data-binding/extensions/baseAdapters/src/main/java/android/databinding/adapters/RadioGroupBindingAdapter.java b/tools/data-binding/extensions/baseAdapters/src/main/java/android/databinding/adapters/RadioGroupBindingAdapter.java new file mode 100644 index 0000000..727fecf --- /dev/null +++ b/tools/data-binding/extensions/baseAdapters/src/main/java/android/databinding/adapters/RadioGroupBindingAdapter.java @@ -0,0 +1,26 @@ +/* + * 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 android.databinding.adapters; + +import android.databinding.BindingMethod; +import android.databinding.BindingMethods; + +@BindingMethods({ + @BindingMethod(type = "android.widget.RadioGroup", attribute = "android:checkedButton", method = "check"), +}) +public class RadioGroupBindingAdapter { + +} diff --git a/tools/data-binding/extensions/baseAdapters/src/main/java/android/databinding/adapters/SpinnerBindingAdapter.java b/tools/data-binding/extensions/baseAdapters/src/main/java/android/databinding/adapters/SpinnerBindingAdapter.java new file mode 100644 index 0000000..eb98629 --- /dev/null +++ b/tools/data-binding/extensions/baseAdapters/src/main/java/android/databinding/adapters/SpinnerBindingAdapter.java @@ -0,0 +1,26 @@ +/* + * 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 android.databinding.adapters; + +import android.databinding.BindingMethod; +import android.databinding.BindingMethods; + +@BindingMethods({ + @BindingMethod(type = "android.widget.Spinner", attribute = "android:popupBackground", method = "setPopupBackgroundDrawable"), +}) +public class SpinnerBindingAdapter { + +} diff --git a/tools/data-binding/extensions/baseAdapters/src/main/java/android/databinding/adapters/SwitchBindingAdapter.java b/tools/data-binding/extensions/baseAdapters/src/main/java/android/databinding/adapters/SwitchBindingAdapter.java new file mode 100644 index 0000000..05307c7 --- /dev/null +++ b/tools/data-binding/extensions/baseAdapters/src/main/java/android/databinding/adapters/SwitchBindingAdapter.java @@ -0,0 +1,36 @@ +/* + * 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 android.databinding.adapters; + +import android.annotation.TargetApi; +import android.databinding.BindingAdapter; +import android.databinding.BindingMethod; +import android.databinding.BindingMethods; +import android.os.Build; +import android.widget.Switch; + +@BindingMethods({ + @BindingMethod(type = "android.widget.Switch", attribute = "android:thumb", method = "setThumbDrawable"), + @BindingMethod(type = "android.widget.Switch", attribute = "android:track", method = "setTrackDrawable"), +}) +@TargetApi(Build.VERSION_CODES.ICE_CREAM_SANDWICH) +public class SwitchBindingAdapter { + + @BindingAdapter("android:switchTextAppearance") + public static void setSwitchTextAppearance(Switch view, int value) { + view.setSwitchTextAppearance(null, value); + } +} diff --git a/tools/data-binding/extensions/baseAdapters/src/main/java/android/databinding/adapters/SwitchCompatBindingAdapter.java b/tools/data-binding/extensions/baseAdapters/src/main/java/android/databinding/adapters/SwitchCompatBindingAdapter.java new file mode 100644 index 0000000..8dca9ac --- /dev/null +++ b/tools/data-binding/extensions/baseAdapters/src/main/java/android/databinding/adapters/SwitchCompatBindingAdapter.java @@ -0,0 +1,33 @@ +/* + * 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 android.databinding.adapters; + +import android.databinding.BindingAdapter; +import android.databinding.BindingMethod; +import android.databinding.BindingMethods; +import android.support.v7.widget.SwitchCompat; + +@BindingMethods({ + @BindingMethod(type = "android.support.v7.widget.SwitchCompat", attribute = "android:thumb", method = "setThumbDrawable"), + @BindingMethod(type = "android.support.v7.widget.SwitchCompat", attribute = "android:track", method = "setTrackDrawable"), +}) +public class SwitchCompatBindingAdapter { + + @BindingAdapter("android:switchTextAppearance") + public static void setSwitchTextAppearance(SwitchCompat view, int value) { + view.setSwitchTextAppearance(null, value); + } +} diff --git a/tools/data-binding/extensions/baseAdapters/src/main/java/android/databinding/adapters/TabWidgetBindingAdapter.java b/tools/data-binding/extensions/baseAdapters/src/main/java/android/databinding/adapters/TabWidgetBindingAdapter.java new file mode 100644 index 0000000..c5fec7f --- /dev/null +++ b/tools/data-binding/extensions/baseAdapters/src/main/java/android/databinding/adapters/TabWidgetBindingAdapter.java @@ -0,0 +1,29 @@ +/* + * 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 android.databinding.adapters; + +import android.databinding.BindingMethod; +import android.databinding.BindingMethods; + +@BindingMethods({ + @BindingMethod(type = "android.widget.TabWidget", attribute = "android:divider", method = "setDividerDrawable"), + @BindingMethod(type = "android.widget.TabWidget", attribute = "android:tabStripEnabled", method = "setStripEnabled"), + @BindingMethod(type = "android.widget.TabWidget", attribute = "android:tabStripLeft", method = "setLeftStripDrawable"), + @BindingMethod(type = "android.widget.TabWidget", attribute = "android:tabStripRight", method = "setRightStripDrawable"), +}) +public class TabWidgetBindingAdapter { + +} diff --git a/tools/data-binding/extensions/baseAdapters/src/main/java/android/databinding/adapters/TableLayoutBindingAdapter.java b/tools/data-binding/extensions/baseAdapters/src/main/java/android/databinding/adapters/TableLayoutBindingAdapter.java new file mode 100644 index 0000000..e0c7591 --- /dev/null +++ b/tools/data-binding/extensions/baseAdapters/src/main/java/android/databinding/adapters/TableLayoutBindingAdapter.java @@ -0,0 +1,100 @@ +/* + * 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 android.databinding.adapters; + +import android.databinding.BindingAdapter; +import android.util.SparseBooleanArray; +import android.widget.TableLayout; + +import java.util.regex.Pattern; + +public class TableLayoutBindingAdapter { + + private static Pattern sColumnPattern = Pattern.compile("\\s*,\\s*"); + + private static final int MAX_COLUMNS = 20; + + @BindingAdapter("android:collapseColumns") + public static void setCollapseColumns(TableLayout view, CharSequence columnsStr) { + SparseBooleanArray columns = parseColumns(columnsStr); + for (int i = 0; i < MAX_COLUMNS; i++) { + boolean isCollapsed = columns.get(i, false); + if (isCollapsed != view.isColumnCollapsed(i)) { + view.setColumnCollapsed(i, isCollapsed); + } + } + } + + @BindingAdapter("android:shrinkColumns") + public static void setShrinkColumns(TableLayout view, CharSequence columnsStr) { + if (columnsStr != null && columnsStr.length() > 0 && columnsStr.charAt(0) == '*') { + view.setShrinkAllColumns(true); + } else { + view.setShrinkAllColumns(false); + SparseBooleanArray columns = parseColumns(columnsStr); + int columnCount = columns.size(); + for (int i = 0; i < columnCount; i++) { + int column = columns.keyAt(i); + boolean shrinkable = columns.valueAt(i); + if (shrinkable) { + view.setColumnShrinkable(column, shrinkable); + } + } + } + } + + @BindingAdapter("android:stretchColumns") + public static void setStretchColumns(TableLayout view, CharSequence columnsStr) { + if (columnsStr != null && columnsStr.length() > 0 && columnsStr.charAt(0) == '*') { + view.setStretchAllColumns(true); + } else { + view.setStretchAllColumns(false); + SparseBooleanArray columns = parseColumns(columnsStr); + int columnCount = columns.size(); + for (int i = 0; i < columnCount; i++) { + int column = columns.keyAt(i); + boolean stretchable = columns.valueAt(i); + if (stretchable) { + view.setColumnStretchable(column, stretchable); + } + } + } + } + + private static SparseBooleanArray parseColumns(CharSequence sequence) { + SparseBooleanArray columns = new SparseBooleanArray(); + if (sequence == null) { + return columns; + } + String[] columnDefs = sColumnPattern.split(sequence); + + for (String columnIdentifier : columnDefs) { + try { + int columnIndex = Integer.parseInt(columnIdentifier); + // only valid, i.e. positive, columns indexes are handled + if (columnIndex >= 0) { + // putting true in this sparse array indicates that the + // column index was defined in the XML file + columns.put(columnIndex, true); + } + } catch (NumberFormatException e) { + // we just ignore columns that don't exist + } + } + + return columns; + } +} diff --git a/tools/data-binding/extensions/baseAdapters/src/main/java/android/databinding/adapters/TextViewBindingAdapter.java b/tools/data-binding/extensions/baseAdapters/src/main/java/android/databinding/adapters/TextViewBindingAdapter.java new file mode 100644 index 0000000..3683916 --- /dev/null +++ b/tools/data-binding/extensions/baseAdapters/src/main/java/android/databinding/adapters/TextViewBindingAdapter.java @@ -0,0 +1,283 @@ +/* + * 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 android.databinding.adapters; + +import android.databinding.BindingAdapter; +import android.databinding.BindingMethod; +import android.databinding.BindingMethods; +import android.graphics.drawable.Drawable; +import android.os.Build; +import android.text.InputFilter; +import android.text.InputType; +import android.text.method.DialerKeyListener; +import android.text.method.DigitsKeyListener; +import android.text.method.KeyListener; +import android.text.method.PasswordTransformationMethod; +import android.text.method.TextKeyListener; +import android.util.Log; +import android.util.TypedValue; +import android.widget.TextView; + +@BindingMethods({ + @BindingMethod(type = "android.widget.TextView", attribute = "android:autoLink", method = "setAutoLinkMask"), + @BindingMethod(type = "android.widget.TextView", attribute = "android:drawablePadding", method = "setCompoundDrawablePadding"), + @BindingMethod(type = "android.widget.TextView", attribute = "android:editorExtras", method = "setInputExtras"), + @BindingMethod(type = "android.widget.TextView", attribute = "android:inputType", method = "setRawInputType"), + @BindingMethod(type = "android.widget.TextView", attribute = "android:scrollHorizontally", method = "setHorizontallyScrolling"), + @BindingMethod(type = "android.widget.TextView", attribute = "android:textAllCaps", method = "setAllCaps"), + @BindingMethod(type = "android.widget.TextView", attribute = "android:textColorHighlight", method = "setHighlightColor"), + @BindingMethod(type = "android.widget.TextView", attribute = "android:textColorHint", method = "setHintTextColor"), + @BindingMethod(type = "android.widget.TextView", attribute = "android:textColorLink", method = "setLinkTextColor"), +}) +public class TextViewBindingAdapter { + + private static final String TAG = "TextViewBindingAdapters"; + + public static final int INTEGER = 0x01; + + public static final int SIGNED = 0x03; + + public static final int DECIMAL = 0x05; + + @BindingAdapter("android:autoText") + public static void setAutoText(TextView view, boolean autoText) { + KeyListener listener = view.getKeyListener(); + + TextKeyListener.Capitalize capitalize = TextKeyListener.Capitalize.NONE; + + int inputType = listener != null ? listener.getInputType() : 0; + if ((inputType & InputType.TYPE_TEXT_FLAG_CAP_CHARACTERS) != 0) { + capitalize = TextKeyListener.Capitalize.CHARACTERS; + } else if ((inputType & InputType.TYPE_TEXT_FLAG_CAP_WORDS) != 0) { + capitalize = TextKeyListener.Capitalize.WORDS; + } else if ((inputType & InputType.TYPE_TEXT_FLAG_CAP_SENTENCES) != 0) { + capitalize = TextKeyListener.Capitalize.SENTENCES; + } + view.setKeyListener(TextKeyListener.getInstance(autoText, capitalize)); + } + + @BindingAdapter("android:capitalize") + public static void setCapitalize(TextView view, TextKeyListener.Capitalize capitalize) { + KeyListener listener = view.getKeyListener(); + + int inputType = listener.getInputType(); + boolean autoText = (inputType & InputType.TYPE_TEXT_FLAG_AUTO_CORRECT) != 0; + view.setKeyListener(TextKeyListener.getInstance(autoText, capitalize)); + } + + @BindingAdapter("android:bufferType") + public static void setBufferType(TextView view, TextView.BufferType bufferType) { + view.setText(view.getText(), bufferType); + } + + @BindingAdapter("android:digits") + public static void setDigits(TextView view, CharSequence digits) { + if (digits != null) { + view.setKeyListener(DigitsKeyListener.getInstance(digits.toString())); + } else if (view.getKeyListener() instanceof DigitsKeyListener) { + view.setKeyListener(null); + } + } + + @BindingAdapter("android:numeric") + public static void setNumeric(TextView view, int numeric) { + view.setKeyListener(DigitsKeyListener.getInstance((numeric & SIGNED) != 0, + (numeric & DECIMAL) != 0)); + } + + @BindingAdapter("android:phoneNumber") + public static void setPhoneNumber(TextView view, boolean phoneNumber) { + if (phoneNumber) { + view.setKeyListener(DialerKeyListener.getInstance()); + } else if (view.getKeyListener() instanceof DialerKeyListener) { + view.setKeyListener(null); + } + } + + @BindingAdapter("android:drawableBottom") + public static void setDrawableBottom(TextView view, Drawable drawable) { + Drawable[] drawables = view.getCompoundDrawables(); + view.setCompoundDrawables(drawables[0], drawables[1], drawables[2], drawable); + } + + @BindingAdapter("android:drawableLeft") + public static void setDrawableLeft(TextView view, Drawable drawable) { + Drawable[] drawables = view.getCompoundDrawables(); + view.setCompoundDrawables(drawable, drawables[1], drawables[2], drawables[3]); + } + + @BindingAdapter("android:drawableRight") + public static void setDrawableRight(TextView view, Drawable drawable) { + Drawable[] drawables = view.getCompoundDrawables(); + view.setCompoundDrawables(drawables[0], drawables[1], drawable, drawables[3]); + } + + @BindingAdapter("android:drawableTop") + public static void setDrawableTop(TextView view, Drawable drawable) { + Drawable[] drawables = view.getCompoundDrawables(); + view.setCompoundDrawables(drawables[0], drawable, drawables[2], drawables[3]); + } + + @BindingAdapter("android:drawableStart") + public static void setDrawableStart(TextView view, Drawable drawable) { + if (Build.VERSION.SDK_INT < Build.VERSION_CODES.JELLY_BEAN_MR1) { + setDrawableLeft(view, drawable); + } else { + Drawable[] drawables = view.getCompoundDrawablesRelative(); + view.setCompoundDrawablesRelative(drawable, drawables[1], drawables[2], drawables[3]); + } + } + + @BindingAdapter("android:drawableEnd") + public static void setDrawableEnd(TextView view, Drawable drawable) { + if (Build.VERSION.SDK_INT < Build.VERSION_CODES.JELLY_BEAN_MR1) { + setDrawableRight(view, drawable); + } else { + Drawable[] drawables = view.getCompoundDrawablesRelative(); + view.setCompoundDrawablesRelative(drawables[0], drawables[1], drawable, drawables[3]); + } + } + + @BindingAdapter("android:imeActionLabel") + public static void setImeActionLabel(TextView view, CharSequence value) { + view.setImeActionLabel(value, view.getImeActionId()); + } + + @BindingAdapter("android:imeActionId") + public static void setImeActionLabel(TextView view, int value) { + view.setImeActionLabel(view.getImeActionLabel(), value); + } + + @BindingAdapter("android:inputMethod") + public static void setInputMethod(TextView view, CharSequence inputMethod) { + try { + Class<?> c = Class.forName(inputMethod.toString()); + view.setKeyListener((KeyListener) c.newInstance()); + } catch (ClassNotFoundException e) { + Log.e(TAG, "Could not create input method: " + inputMethod, e); + } catch (InstantiationException e) { + Log.e(TAG, "Could not create input method: " + inputMethod, e); + } catch (IllegalAccessException e) { + Log.e(TAG, "Could not create input method: " + inputMethod, e); + } + } + + @BindingAdapter("android:lineSpacingExtra") + public static void setLineSpacingExtra(TextView view, float value) { + if (Build.VERSION.SDK_INT >= Build.VERSION_CODES.JELLY_BEAN) { + view.setLineSpacing(value, view.getLineSpacingMultiplier()); + } else { + view.setLineSpacing(value, 1); + } + } + + @BindingAdapter("android:lineSpacingMultiplier") + public static void setLineSpacingMultiplier(TextView view, float value) { + if (Build.VERSION.SDK_INT >= Build.VERSION_CODES.JELLY_BEAN) { + view.setLineSpacing(view.getLineSpacingExtra(), value); + } else { + view.setLineSpacing(0, value); + } + } + + @BindingAdapter("android:maxLength") + public static void setMaxLength(TextView view, int value) { + InputFilter[] filters = view.getFilters(); + if (filters == null) { + filters = new InputFilter[]{ + new InputFilter.LengthFilter(value) + }; + } else { + boolean foundMaxLength = false; + for (int i = 0; i < filters.length; i++) { + InputFilter filter = filters[i]; + if (filter instanceof InputFilter.LengthFilter) { + foundMaxLength = true; + boolean replace = true; + if (Build.VERSION.SDK_INT >= Build.VERSION_CODES.LOLLIPOP) { + replace = ((InputFilter.LengthFilter) filter).getMax() != value; + } + if (replace) { + filters[i] = new InputFilter.LengthFilter(value); + } + break; + } + } + if (!foundMaxLength) { + // can't use Arrays.copyOf -- it shows up in API 9 + InputFilter[] oldFilters = filters; + filters = new InputFilter[oldFilters.length + 1]; + System.arraycopy(oldFilters, 0, filters, 0, oldFilters.length); + filters[filters.length - 1] = new InputFilter.LengthFilter(value); + } + } + view.setFilters(filters); + } + + @BindingAdapter("android:password") + public static void setPassword(TextView view, boolean password) { + if (password) { + view.setTransformationMethod(PasswordTransformationMethod.getInstance()); + } else if (view.getTransformationMethod() instanceof PasswordTransformationMethod) { + view.setTransformationMethod(null); + } + } + + @BindingAdapter("android:shadowColor") + public static void setShadowColor(TextView view, int color) { + if (Build.VERSION.SDK_INT >= Build.VERSION_CODES.JELLY_BEAN) { + float dx = view.getShadowDx(); + float dy = view.getShadowDy(); + float r = view.getShadowRadius(); + view.setShadowLayer(r, dx, dy, color); + } + } + + @BindingAdapter("android:shadowDx") + public static void setShadowDx(TextView view, float dx) { + if (Build.VERSION.SDK_INT >= Build.VERSION_CODES.JELLY_BEAN) { + int color = view.getShadowColor(); + float dy = view.getShadowDy(); + float r = view.getShadowRadius(); + view.setShadowLayer(r, dx, dy, color); + } + } + + @BindingAdapter("android:shadowDy") + public static void setShadowDy(TextView view, float dy) { + if (Build.VERSION.SDK_INT >= Build.VERSION_CODES.JELLY_BEAN) { + int color = view.getShadowColor(); + float dx = view.getShadowDx(); + float r = view.getShadowRadius(); + view.setShadowLayer(r, dx, dy, color); + } + } + + @BindingAdapter("android:shadowRadius") + public static void setShadowRadius(TextView view, float r) { + if (Build.VERSION.SDK_INT >= Build.VERSION_CODES.JELLY_BEAN) { + int color = view.getShadowColor(); + float dx = view.getShadowDx(); + float dy = view.getShadowDy(); + view.setShadowLayer(r, dx, dy, color); + } + } + + @BindingAdapter("android:textSize") + public static void setTextSize(TextView view, float size) { + view.setTextSize(TypedValue.COMPLEX_UNIT_PX, size); + } +} diff --git a/tools/data-binding/extensions/baseAdapters/src/main/java/android/databinding/adapters/ViewBindingAdapter.java b/tools/data-binding/extensions/baseAdapters/src/main/java/android/databinding/adapters/ViewBindingAdapter.java new file mode 100644 index 0000000..e8b9e43 --- /dev/null +++ b/tools/data-binding/extensions/baseAdapters/src/main/java/android/databinding/adapters/ViewBindingAdapter.java @@ -0,0 +1,103 @@ +/* + * 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 android.databinding.adapters; + +import android.databinding.BindingAdapter; +import android.databinding.BindingMethod; +import android.databinding.BindingMethods; +import android.os.Build; +import android.view.View; + +@BindingMethods({ + @BindingMethod(type = "android.view.View", attribute = "android:backgroundTint", method = "setBackgroundTintList"), + @BindingMethod(type = "android.view.View", attribute = "android:fadeScrollbars", method = "setScrollbarFadingEnabled"), + @BindingMethod(type = "android.view.View", attribute = "android:nextFocusForward", method = "setNextFocusForwardId"), + @BindingMethod(type = "android.view.View", attribute = "android:nextFocusLeft", method = "setNextFocusLeftId"), + @BindingMethod(type = "android.view.View", attribute = "android:nextFocusRight", method = "setNextFocusRightId"), + @BindingMethod(type = "android.view.View", attribute = "android:nextFocusUp", method = "setNextFocusUpId"), + @BindingMethod(type = "android.view.View", attribute = "android:nextFocusDown", method = "setNextFocusDownId"), + @BindingMethod(type = "android.view.View", attribute = "android:requiresFadingEdge", method = "setVerticalFadingEdgeEnabled"), + @BindingMethod(type = "android.view.View", attribute = "android:scrollbarDefaultDelayBeforeFade", method = "setScrollBarDefaultDelayBeforeFade"), + @BindingMethod(type = "android.view.View", attribute = "android:scrollbarFadeDuration", method = "setScrollBarFadeDuration"), + @BindingMethod(type = "android.view.View", attribute = "android:scrollbarSize", method = "setScrollBarSize"), + @BindingMethod(type = "android.view.View", attribute = "android:scrollbarStyle", method = "setScrollBarStyle"), + @BindingMethod(type = "android.view.View", attribute = "android:transformPivotX", method = "setPivotX"), + @BindingMethod(type = "android.view.View", attribute = "android:transformPivotY", method = "setPivotY"), +}) +public class ViewBindingAdapter { + public static int FADING_EDGE_NONE = 0; + public static int FADING_EDGE_HORIZONTAL = 1; + public static int FADING_EDGE_VERTICAL = 2; + + @BindingAdapter("android:padding") + public static void setPadding(View view, int padding) { + view.setPadding(padding, padding, padding, padding); + } + + @BindingAdapter("android:paddingBottom") + public static void setPaddingBottom(View view, int padding) { + view.setPadding(view.getPaddingLeft(), view.getPaddingTop(), view.getPaddingRight(), + padding); + } + + @BindingAdapter("android:paddingEnd") + public static void setPaddingEnd(View view, int padding) { + if (Build.VERSION.SDK_INT >= Build.VERSION_CODES.JELLY_BEAN_MR1) { + view.setPaddingRelative(view.getPaddingStart(), view.getPaddingTop(), padding, + view.getPaddingBottom()); + } else { + view.setPadding(view.getPaddingLeft(), view.getPaddingTop(), padding, + view.getPaddingBottom()); + } + } + + @BindingAdapter("android:paddingLeft") + public static void setPaddingLeft(View view, int padding) { + view.setPadding(padding, view.getPaddingTop(), view.getPaddingRight(), + view.getPaddingBottom()); + } + + @BindingAdapter("android:paddingRight") + public static void setPaddingRight(View view, int padding) { + view.setPadding(view.getPaddingLeft(), view.getPaddingTop(), padding, + view.getPaddingBottom()); + } + + @BindingAdapter("android:paddingStart") + public static void setPaddingStart(View view, int padding) { + if (Build.VERSION.SDK_INT >= Build.VERSION_CODES.JELLY_BEAN_MR1) { + view.setPaddingRelative(padding, view.getPaddingTop(), view.getPaddingEnd(), + view.getPaddingBottom()); + } else { + view.setPadding(padding, view.getPaddingTop(), view.getPaddingRight(), + view.getPaddingBottom()); + } + } + + @BindingAdapter("android:paddingTop") + public static void setPaddingTop(View view, int padding) { + view.setPadding(view.getPaddingLeft(), padding, view.getPaddingRight(), + view.getPaddingBottom()); + } + + @BindingAdapter("android:requiresFadingEdge") + public static void setRequiresFadingEdge(View view, int value) { + final boolean vertical = (value & FADING_EDGE_VERTICAL) != 0; + final boolean horizontal = (value & FADING_EDGE_HORIZONTAL) != 0; + view.setVerticalFadingEdgeEnabled(vertical); + view.setHorizontalFadingEdgeEnabled(horizontal); + } +} diff --git a/tools/data-binding/extensions/baseAdapters/src/main/java/android/databinding/adapters/ViewGroupBindingAdapter.java b/tools/data-binding/extensions/baseAdapters/src/main/java/android/databinding/adapters/ViewGroupBindingAdapter.java new file mode 100644 index 0000000..aaad4d1 --- /dev/null +++ b/tools/data-binding/extensions/baseAdapters/src/main/java/android/databinding/adapters/ViewGroupBindingAdapter.java @@ -0,0 +1,42 @@ +/* + * 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 android.databinding.adapters; + +import android.animation.LayoutTransition; +import android.annotation.TargetApi; +import android.databinding.BindingAdapter; +import android.databinding.BindingMethod; +import android.databinding.BindingMethods; +import android.os.Build; +import android.view.ViewGroup; + +@BindingMethods({ + @BindingMethod(type = "android.view.ViewGroup", attribute = "android:alwaysDrawnWithCache", method = "setAlwaysDrawnWithCacheEnabled"), + @BindingMethod(type = "android.view.ViewGroup", attribute = "android:animationCache", method = "setAnimationCacheEnabled"), + @BindingMethod(type = "android.view.ViewGroup", attribute = "android:splitMotionEvents", method = "setMotionEventSplittingEnabled"), +}) +public class ViewGroupBindingAdapter { + + @TargetApi(Build.VERSION_CODES.HONEYCOMB) + @BindingAdapter("android:animateLayoutChanges") + public static void setAnimateLayoutChanges(ViewGroup view, boolean animate) { + if (animate) { + view.setLayoutTransition(new LayoutTransition()); + } else { + view.setLayoutTransition(null); + } + } +} diff --git a/tools/data-binding/extensions/baseAdapters/src/main/java/android/databinding/adapters/ViewStubBindingAdapter.java b/tools/data-binding/extensions/baseAdapters/src/main/java/android/databinding/adapters/ViewStubBindingAdapter.java new file mode 100644 index 0000000..37864cc --- /dev/null +++ b/tools/data-binding/extensions/baseAdapters/src/main/java/android/databinding/adapters/ViewStubBindingAdapter.java @@ -0,0 +1,28 @@ +/* + * 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 android.databinding.adapters; + +import android.databinding.BindingMethod; +import android.databinding.BindingMethods; +import android.databinding.Untaggable; + +@Untaggable({"android.view.ViewStub"}) +@BindingMethods({ + @BindingMethod(type = "android.view.ViewStub", attribute = "android:layout", method = "setLayoutResource") +}) +public class ViewStubBindingAdapter { + +} diff --git a/tools/data-binding/extensions/build.gradle b/tools/data-binding/extensions/build.gradle new file mode 100644 index 0000000..cfa2697 --- /dev/null +++ b/tools/data-binding/extensions/build.gradle @@ -0,0 +1,45 @@ +/* + * 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. + */ + + +buildscript { + def Properties dataBindingProperties = new Properties() + dataBindingProperties.load(new FileInputStream("${projectDir}/../databinding.properties")) + dataBindingProperties.mavenRepoDir = "${projectDir}/../${dataBindingProperties.mavenRepoName}" + ext.config = dataBindingProperties + repositories { + jcenter() + maven { + url config.mavenRepoDir + } + } + dependencies { + classpath 'com.android.tools.build:gradle:1.1.3' + classpath "com.android.databinding:dataBinder:${config.snapshotVersion}" + } +} + +subprojects { + apply plugin: 'maven' + group = config.group + version = config.snapshotVersion + repositories { + mavenCentral() + maven { + url config.mavenRepoDir + } + } +}
\ No newline at end of file diff --git a/tools/data-binding/extensions/gradle/wrapper/gradle-wrapper.jar b/tools/data-binding/extensions/gradle/wrapper/gradle-wrapper.jar Binary files differnew file mode 100644 index 0000000..8c0fb64 --- /dev/null +++ b/tools/data-binding/extensions/gradle/wrapper/gradle-wrapper.jar diff --git a/tools/data-binding/extensions/gradle/wrapper/gradle-wrapper.properties b/tools/data-binding/extensions/gradle/wrapper/gradle-wrapper.properties new file mode 100644 index 0000000..e5fd879 --- /dev/null +++ b/tools/data-binding/extensions/gradle/wrapper/gradle-wrapper.properties @@ -0,0 +1,6 @@ +#Thu Mar 12 15:27:48 PDT 2015 +distributionBase=GRADLE_USER_HOME +distributionPath=wrapper/dists +zipStoreBase=GRADLE_USER_HOME +zipStorePath=wrapper/dists +distributionUrl=https\://services.gradle.org/distributions/gradle-2.2.1-all.zip diff --git a/tools/data-binding/extensions/gradlew b/tools/data-binding/extensions/gradlew new file mode 100755 index 0000000..91a7e26 --- /dev/null +++ b/tools/data-binding/extensions/gradlew @@ -0,0 +1,164 @@ +#!/usr/bin/env bash + +############################################################################## +## +## Gradle start up script for UN*X +## +############################################################################## + +# Add default JVM options here. You can also use JAVA_OPTS and GRADLE_OPTS to pass JVM options to this script. +DEFAULT_JVM_OPTS="" + +APP_NAME="Gradle" +APP_BASE_NAME=`basename "$0"` + +# Use the maximum available, or set MAX_FD != -1 to use that value. +MAX_FD="maximum" + +warn ( ) { + echo "$*" +} + +die ( ) { + echo + echo "$*" + echo + exit 1 +} + +# OS specific support (must be 'true' or 'false'). +cygwin=false +msys=false +darwin=false +case "`uname`" in + CYGWIN* ) + cygwin=true + ;; + Darwin* ) + darwin=true + ;; + MINGW* ) + msys=true + ;; +esac + +# For Cygwin, ensure paths are in UNIX format before anything is touched. +if $cygwin ; then + [ -n "$JAVA_HOME" ] && JAVA_HOME=`cygpath --unix "$JAVA_HOME"` +fi + +# Attempt to set APP_HOME +# Resolve links: $0 may be a link +PRG="$0" +# Need this for relative symlinks. +while [ -h "$PRG" ] ; do + ls=`ls -ld "$PRG"` + link=`expr "$ls" : '.*-> \(.*\)$'` + if expr "$link" : '/.*' > /dev/null; then + PRG="$link" + else + PRG=`dirname "$PRG"`"/$link" + fi +done +SAVED="`pwd`" +cd "`dirname \"$PRG\"`/" >&- +APP_HOME="`pwd -P`" +cd "$SAVED" >&- + +CLASSPATH=$APP_HOME/gradle/wrapper/gradle-wrapper.jar + +# Determine the Java command to use to start the JVM. +if [ -n "$JAVA_HOME" ] ; then + if [ -x "$JAVA_HOME/jre/sh/java" ] ; then + # IBM's JDK on AIX uses strange locations for the executables + JAVACMD="$JAVA_HOME/jre/sh/java" + else + JAVACMD="$JAVA_HOME/bin/java" + fi + if [ ! -x "$JAVACMD" ] ; then + die "ERROR: JAVA_HOME is set to an invalid directory: $JAVA_HOME + +Please set the JAVA_HOME variable in your environment to match the +location of your Java installation." + fi +else + JAVACMD="java" + which java >/dev/null 2>&1 || die "ERROR: JAVA_HOME is not set and no 'java' command could be found in your PATH. + +Please set the JAVA_HOME variable in your environment to match the +location of your Java installation." +fi + +# Increase the maximum file descriptors if we can. +if [ "$cygwin" = "false" -a "$darwin" = "false" ] ; then + MAX_FD_LIMIT=`ulimit -H -n` + if [ $? -eq 0 ] ; then + if [ "$MAX_FD" = "maximum" -o "$MAX_FD" = "max" ] ; then + MAX_FD="$MAX_FD_LIMIT" + fi + ulimit -n $MAX_FD + if [ $? -ne 0 ] ; then + warn "Could not set maximum file descriptor limit: $MAX_FD" + fi + else + warn "Could not query maximum file descriptor limit: $MAX_FD_LIMIT" + fi +fi + +# For Darwin, add options to specify how the application appears in the dock +if $darwin; then + GRADLE_OPTS="$GRADLE_OPTS \"-Xdock:name=$APP_NAME\" \"-Xdock:icon=$APP_HOME/media/gradle.icns\"" +fi + +# For Cygwin, switch paths to Windows format before running java +if $cygwin ; then + APP_HOME=`cygpath --path --mixed "$APP_HOME"` + CLASSPATH=`cygpath --path --mixed "$CLASSPATH"` + + # We build the pattern for arguments to be converted via cygpath + ROOTDIRSRAW=`find -L / -maxdepth 1 -mindepth 1 -type d 2>/dev/null` + SEP="" + for dir in $ROOTDIRSRAW ; do + ROOTDIRS="$ROOTDIRS$SEP$dir" + SEP="|" + done + OURCYGPATTERN="(^($ROOTDIRS))" + # Add a user-defined pattern to the cygpath arguments + if [ "$GRADLE_CYGPATTERN" != "" ] ; then + OURCYGPATTERN="$OURCYGPATTERN|($GRADLE_CYGPATTERN)" + fi + # Now convert the arguments - kludge to limit ourselves to /bin/sh + i=0 + for arg in "$@" ; do + CHECK=`echo "$arg"|egrep -c "$OURCYGPATTERN" -` + CHECK2=`echo "$arg"|egrep -c "^-"` ### Determine if an option + + if [ $CHECK -ne 0 ] && [ $CHECK2 -eq 0 ] ; then ### Added a condition + eval `echo args$i`=`cygpath --path --ignore --mixed "$arg"` + else + eval `echo args$i`="\"$arg\"" + fi + i=$((i+1)) + done + case $i in + (0) set -- ;; + (1) set -- "$args0" ;; + (2) set -- "$args0" "$args1" ;; + (3) set -- "$args0" "$args1" "$args2" ;; + (4) set -- "$args0" "$args1" "$args2" "$args3" ;; + (5) set -- "$args0" "$args1" "$args2" "$args3" "$args4" ;; + (6) set -- "$args0" "$args1" "$args2" "$args3" "$args4" "$args5" ;; + (7) set -- "$args0" "$args1" "$args2" "$args3" "$args4" "$args5" "$args6" ;; + (8) set -- "$args0" "$args1" "$args2" "$args3" "$args4" "$args5" "$args6" "$args7" ;; + (9) set -- "$args0" "$args1" "$args2" "$args3" "$args4" "$args5" "$args6" "$args7" "$args8" ;; + esac +fi + +# Split up the JVM_OPTS And GRADLE_OPTS values into an array, following the shell quoting and substitution rules +function splitJvmOpts() { + JVM_OPTS=("$@") +} +eval splitJvmOpts $DEFAULT_JVM_OPTS $JAVA_OPTS $GRADLE_OPTS +JVM_OPTS[${#JVM_OPTS[*]}]="-Dorg.gradle.appname=$APP_BASE_NAME" + +exec "$JAVACMD" "${JVM_OPTS[@]}" -classpath "$CLASSPATH" org.gradle.wrapper.GradleWrapperMain "$@" diff --git a/tools/data-binding/extensions/settings.gradle b/tools/data-binding/extensions/settings.gradle new file mode 100644 index 0000000..80ebcc8 --- /dev/null +++ b/tools/data-binding/extensions/settings.gradle @@ -0,0 +1,20 @@ +/* + * Copyright (C) 2015 The Android Open Source Project + * + * Licensed under the Apache License, Version 2.0 (the "License"); + * you may not use this file except in compliance with the License. + * You may obtain a copy of the License at + * + * http://www.apache.org/licenses/LICENSE-2.0 + * + * Unless required by applicable law or agreed to in writing, software + * distributed under the License is distributed on an "AS IS" BASIS, + * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. + * See the License for the specific language governing permissions and + * limitations under the License. + */ + +/** + * These are projects that requires a compiled version of data binding. + */ +include ':baseAdapters' diff --git a/tools/data-binding/gradle.properties b/tools/data-binding/gradle.properties new file mode 100644 index 0000000..24f728c --- /dev/null +++ b/tools/data-binding/gradle.properties @@ -0,0 +1,35 @@ +# +# Copyright (C) 2014 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. +# + +# Project-wide Gradle settings. + +# IDE (e.g. Android Studio) users: +# Gradle settings configured through the IDE *will override* +# any settings specified in this file. + +# For more details on how to configure your build environment visit +# http://www.gradle.org/docs/current/userguide/build_environment.html + +# Specifies the JVM arguments used for the daemon process. +# The setting is particularly useful for tweaking memory settings. +# Default value: -Xmx10248m -XX:MaxPermSize=256m +org.gradle.jvmargs=-Xmx2048m -XX:MaxPermSize=1024m -Dfile.encoding=UTF-8 + +# When configured, Gradle will run in incubating parallel mode. +# This option should only be used with decoupled projects. More details, visit +# http://www.gradle.org/docs/current/userguide/multi_project_builds.html#sec:decoupled_projects +#org.gradle.parallel=true +org.gradle.daemon=true diff --git a/tools/data-binding/gradle/wrapper/gradle-wrapper.jar b/tools/data-binding/gradle/wrapper/gradle-wrapper.jar Binary files differnew file mode 100644 index 0000000..8c0fb64 --- /dev/null +++ b/tools/data-binding/gradle/wrapper/gradle-wrapper.jar diff --git a/tools/data-binding/gradle/wrapper/gradle-wrapper.properties b/tools/data-binding/gradle/wrapper/gradle-wrapper.properties new file mode 100644 index 0000000..e5fd879 --- /dev/null +++ b/tools/data-binding/gradle/wrapper/gradle-wrapper.properties @@ -0,0 +1,6 @@ +#Thu Mar 12 15:27:48 PDT 2015 +distributionBase=GRADLE_USER_HOME +distributionPath=wrapper/dists +zipStoreBase=GRADLE_USER_HOME +zipStorePath=wrapper/dists +distributionUrl=https\://services.gradle.org/distributions/gradle-2.2.1-all.zip diff --git a/tools/data-binding/gradlePlugin/build.gradle b/tools/data-binding/gradlePlugin/build.gradle new file mode 100644 index 0000000..bdfdf79 --- /dev/null +++ b/tools/data-binding/gradlePlugin/build.gradle @@ -0,0 +1,47 @@ +/* + * Copyright (C) 2014 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. + */ + +apply plugin: 'java' +apply plugin: "kotlin" +apply plugin: 'maven' + +sourceCompatibility = config.javaTargetCompatibility +targetCompatibility = config.javaSourceCompatibility + +buildscript { + repositories { + mavenCentral() + } + dependencies { + classpath "org.jetbrains.kotlin:kotlin-gradle-plugin:${config.kotlinVersion}" + } +} + +dependencies { + compile "com.android.tools.build:gradle:${config.androidPluginVersion}" + compile "org.jetbrains.kotlin:kotlin-stdlib:${config.kotlinVersion}" + compile gradleApi() + compile 'commons-io:commons-io:2.4' + compile 'commons-codec:commons-codec:1.10' + compile project(":compiler") +} +uploadArchives { + repositories { + mavenDeployer { + pom.artifactId = 'dataBinder' + } + } +}
\ No newline at end of file diff --git a/tools/data-binding/gradlePlugin/gradle/wrapper/gradle-wrapper.jar b/tools/data-binding/gradlePlugin/gradle/wrapper/gradle-wrapper.jar Binary files differnew file mode 100644 index 0000000..3d0dee6 --- /dev/null +++ b/tools/data-binding/gradlePlugin/gradle/wrapper/gradle-wrapper.jar diff --git a/tools/data-binding/gradlePlugin/gradle/wrapper/gradle-wrapper.properties b/tools/data-binding/gradlePlugin/gradle/wrapper/gradle-wrapper.properties new file mode 100644 index 0000000..8b80a06 --- /dev/null +++ b/tools/data-binding/gradlePlugin/gradle/wrapper/gradle-wrapper.properties @@ -0,0 +1,6 @@ +#Thu Dec 11 16:01:54 PST 2014 +distributionBase=GRADLE_USER_HOME +distributionPath=wrapper/dists +zipStoreBase=GRADLE_USER_HOME +zipStorePath=wrapper/dists +distributionUrl=https\://services.gradle.org/distributions/gradle-2.1-bin.zip diff --git a/tools/data-binding/gradlePlugin/gradlew b/tools/data-binding/gradlePlugin/gradlew new file mode 100755 index 0000000..91a7e26 --- /dev/null +++ b/tools/data-binding/gradlePlugin/gradlew @@ -0,0 +1,164 @@ +#!/usr/bin/env bash + +############################################################################## +## +## Gradle start up script for UN*X +## +############################################################################## + +# Add default JVM options here. You can also use JAVA_OPTS and GRADLE_OPTS to pass JVM options to this script. +DEFAULT_JVM_OPTS="" + +APP_NAME="Gradle" +APP_BASE_NAME=`basename "$0"` + +# Use the maximum available, or set MAX_FD != -1 to use that value. +MAX_FD="maximum" + +warn ( ) { + echo "$*" +} + +die ( ) { + echo + echo "$*" + echo + exit 1 +} + +# OS specific support (must be 'true' or 'false'). +cygwin=false +msys=false +darwin=false +case "`uname`" in + CYGWIN* ) + cygwin=true + ;; + Darwin* ) + darwin=true + ;; + MINGW* ) + msys=true + ;; +esac + +# For Cygwin, ensure paths are in UNIX format before anything is touched. +if $cygwin ; then + [ -n "$JAVA_HOME" ] && JAVA_HOME=`cygpath --unix "$JAVA_HOME"` +fi + +# Attempt to set APP_HOME +# Resolve links: $0 may be a link +PRG="$0" +# Need this for relative symlinks. +while [ -h "$PRG" ] ; do + ls=`ls -ld "$PRG"` + link=`expr "$ls" : '.*-> \(.*\)$'` + if expr "$link" : '/.*' > /dev/null; then + PRG="$link" + else + PRG=`dirname "$PRG"`"/$link" + fi +done +SAVED="`pwd`" +cd "`dirname \"$PRG\"`/" >&- +APP_HOME="`pwd -P`" +cd "$SAVED" >&- + +CLASSPATH=$APP_HOME/gradle/wrapper/gradle-wrapper.jar + +# Determine the Java command to use to start the JVM. +if [ -n "$JAVA_HOME" ] ; then + if [ -x "$JAVA_HOME/jre/sh/java" ] ; then + # IBM's JDK on AIX uses strange locations for the executables + JAVACMD="$JAVA_HOME/jre/sh/java" + else + JAVACMD="$JAVA_HOME/bin/java" + fi + if [ ! -x "$JAVACMD" ] ; then + die "ERROR: JAVA_HOME is set to an invalid directory: $JAVA_HOME + +Please set the JAVA_HOME variable in your environment to match the +location of your Java installation." + fi +else + JAVACMD="java" + which java >/dev/null 2>&1 || die "ERROR: JAVA_HOME is not set and no 'java' command could be found in your PATH. + +Please set the JAVA_HOME variable in your environment to match the +location of your Java installation." +fi + +# Increase the maximum file descriptors if we can. +if [ "$cygwin" = "false" -a "$darwin" = "false" ] ; then + MAX_FD_LIMIT=`ulimit -H -n` + if [ $? -eq 0 ] ; then + if [ "$MAX_FD" = "maximum" -o "$MAX_FD" = "max" ] ; then + MAX_FD="$MAX_FD_LIMIT" + fi + ulimit -n $MAX_FD + if [ $? -ne 0 ] ; then + warn "Could not set maximum file descriptor limit: $MAX_FD" + fi + else + warn "Could not query maximum file descriptor limit: $MAX_FD_LIMIT" + fi +fi + +# For Darwin, add options to specify how the application appears in the dock +if $darwin; then + GRADLE_OPTS="$GRADLE_OPTS \"-Xdock:name=$APP_NAME\" \"-Xdock:icon=$APP_HOME/media/gradle.icns\"" +fi + +# For Cygwin, switch paths to Windows format before running java +if $cygwin ; then + APP_HOME=`cygpath --path --mixed "$APP_HOME"` + CLASSPATH=`cygpath --path --mixed "$CLASSPATH"` + + # We build the pattern for arguments to be converted via cygpath + ROOTDIRSRAW=`find -L / -maxdepth 1 -mindepth 1 -type d 2>/dev/null` + SEP="" + for dir in $ROOTDIRSRAW ; do + ROOTDIRS="$ROOTDIRS$SEP$dir" + SEP="|" + done + OURCYGPATTERN="(^($ROOTDIRS))" + # Add a user-defined pattern to the cygpath arguments + if [ "$GRADLE_CYGPATTERN" != "" ] ; then + OURCYGPATTERN="$OURCYGPATTERN|($GRADLE_CYGPATTERN)" + fi + # Now convert the arguments - kludge to limit ourselves to /bin/sh + i=0 + for arg in "$@" ; do + CHECK=`echo "$arg"|egrep -c "$OURCYGPATTERN" -` + CHECK2=`echo "$arg"|egrep -c "^-"` ### Determine if an option + + if [ $CHECK -ne 0 ] && [ $CHECK2 -eq 0 ] ; then ### Added a condition + eval `echo args$i`=`cygpath --path --ignore --mixed "$arg"` + else + eval `echo args$i`="\"$arg\"" + fi + i=$((i+1)) + done + case $i in + (0) set -- ;; + (1) set -- "$args0" ;; + (2) set -- "$args0" "$args1" ;; + (3) set -- "$args0" "$args1" "$args2" ;; + (4) set -- "$args0" "$args1" "$args2" "$args3" ;; + (5) set -- "$args0" "$args1" "$args2" "$args3" "$args4" ;; + (6) set -- "$args0" "$args1" "$args2" "$args3" "$args4" "$args5" ;; + (7) set -- "$args0" "$args1" "$args2" "$args3" "$args4" "$args5" "$args6" ;; + (8) set -- "$args0" "$args1" "$args2" "$args3" "$args4" "$args5" "$args6" "$args7" ;; + (9) set -- "$args0" "$args1" "$args2" "$args3" "$args4" "$args5" "$args6" "$args7" "$args8" ;; + esac +fi + +# Split up the JVM_OPTS And GRADLE_OPTS values into an array, following the shell quoting and substitution rules +function splitJvmOpts() { + JVM_OPTS=("$@") +} +eval splitJvmOpts $DEFAULT_JVM_OPTS $JAVA_OPTS $GRADLE_OPTS +JVM_OPTS[${#JVM_OPTS[*]}]="-Dorg.gradle.appname=$APP_BASE_NAME" + +exec "$JAVACMD" "${JVM_OPTS[@]}" -classpath "$CLASSPATH" org.gradle.wrapper.GradleWrapperMain "$@" diff --git a/tools/data-binding/gradlePlugin/gradlew.bat b/tools/data-binding/gradlePlugin/gradlew.bat new file mode 100644 index 0000000..aec9973 --- /dev/null +++ b/tools/data-binding/gradlePlugin/gradlew.bat @@ -0,0 +1,90 @@ +@if "%DEBUG%" == "" @echo off
+@rem ##########################################################################
+@rem
+@rem Gradle startup script for Windows
+@rem
+@rem ##########################################################################
+
+@rem Set local scope for the variables with windows NT shell
+if "%OS%"=="Windows_NT" setlocal
+
+@rem Add default JVM options here. You can also use JAVA_OPTS and GRADLE_OPTS to pass JVM options to this script.
+set DEFAULT_JVM_OPTS=
+
+set DIRNAME=%~dp0
+if "%DIRNAME%" == "" set DIRNAME=.
+set APP_BASE_NAME=%~n0
+set APP_HOME=%DIRNAME%
+
+@rem Find java.exe
+if defined JAVA_HOME goto findJavaFromJavaHome
+
+set JAVA_EXE=java.exe
+%JAVA_EXE% -version >NUL 2>&1
+if "%ERRORLEVEL%" == "0" goto init
+
+echo.
+echo ERROR: JAVA_HOME is not set and no 'java' command could be found in your PATH.
+echo.
+echo Please set the JAVA_HOME variable in your environment to match the
+echo location of your Java installation.
+
+goto fail
+
+:findJavaFromJavaHome
+set JAVA_HOME=%JAVA_HOME:"=%
+set JAVA_EXE=%JAVA_HOME%/bin/java.exe
+
+if exist "%JAVA_EXE%" goto init
+
+echo.
+echo ERROR: JAVA_HOME is set to an invalid directory: %JAVA_HOME%
+echo.
+echo Please set the JAVA_HOME variable in your environment to match the
+echo location of your Java installation.
+
+goto fail
+
+:init
+@rem Get command-line arguments, handling Windowz variants
+
+if not "%OS%" == "Windows_NT" goto win9xME_args
+if "%@eval[2+2]" == "4" goto 4NT_args
+
+:win9xME_args
+@rem Slurp the command line arguments.
+set CMD_LINE_ARGS=
+set _SKIP=2
+
+:win9xME_args_slurp
+if "x%~1" == "x" goto execute
+
+set CMD_LINE_ARGS=%*
+goto execute
+
+:4NT_args
+@rem Get arguments from the 4NT Shell from JP Software
+set CMD_LINE_ARGS=%$
+
+:execute
+@rem Setup the command line
+
+set CLASSPATH=%APP_HOME%\gradle\wrapper\gradle-wrapper.jar
+
+@rem Execute Gradle
+"%JAVA_EXE%" %DEFAULT_JVM_OPTS% %JAVA_OPTS% %GRADLE_OPTS% "-Dorg.gradle.appname=%APP_BASE_NAME%" -classpath "%CLASSPATH%" org.gradle.wrapper.GradleWrapperMain %CMD_LINE_ARGS%
+
+:end
+@rem End local scope for the variables with windows NT shell
+if "%ERRORLEVEL%"=="0" goto mainEnd
+
+:fail
+rem Set variable GRADLE_EXIT_CONSOLE if you need the _script_ return code instead of
+rem the _cmd.exe /c_ return code!
+if not "" == "%GRADLE_EXIT_CONSOLE%" exit 1
+exit /b 1
+
+:mainEnd
+if "%OS%"=="Windows_NT" endlocal
+
+:omega
diff --git a/tools/data-binding/gradlePlugin/src/main/kotlin/DataBindingProcessLayoutsTask.kt b/tools/data-binding/gradlePlugin/src/main/kotlin/DataBindingProcessLayoutsTask.kt new file mode 100644 index 0000000..f3e1f1d --- /dev/null +++ b/tools/data-binding/gradlePlugin/src/main/kotlin/DataBindingProcessLayoutsTask.kt @@ -0,0 +1,39 @@ +/* + * 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 android.databinding.tool + +import org.gradle.api.DefaultTask +import org.gradle.api.tasks.TaskAction +import kotlin.properties.Delegates +import android.databinding.tool.util.Log +import java.io.File + +open class DataBindingProcessLayoutsTask : DefaultTask() { + { + Log.d {"created data binding task"} + } + var xmlProcessor: LayoutXmlProcessor by Delegates.notNull() + var sdkDir : File by Delegates.notNull() + [TaskAction] + public fun doIt() { + Log.d {"running process layouts task"} + xmlProcessor.processResources() + } + + public fun writeFiles(xmlOutFolder : File) { + xmlProcessor.writeIntermediateFile(sdkDir, xmlOutFolder) + } +}
\ No newline at end of file diff --git a/tools/data-binding/gradlePlugin/src/main/kotlin/plugin.kt b/tools/data-binding/gradlePlugin/src/main/kotlin/plugin.kt new file mode 100644 index 0000000..5515adf --- /dev/null +++ b/tools/data-binding/gradlePlugin/src/main/kotlin/plugin.kt @@ -0,0 +1,195 @@ +/* + * Copyright (C) 2014 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 android.databinding.tool + +import org.gradle.api.Plugin +import org.gradle.api.Project +import com.android.build.gradle.AppExtension +import com.android.build.gradle.internal.api.ApplicationVariantImpl +import com.android.build.gradle.internal.variant.ApplicationVariantData +import java.io.File +import org.gradle.api.file.FileCollection +import android.databinding.tool.writer.JavaFileWriter +import android.databinding.tool.util.Log +import org.gradle.api.Action +import com.android.build.gradle.BaseExtension +import com.android.build.gradle.LibraryExtension +import com.android.build.gradle.api.LibraryVariant +import com.android.build.gradle.api.ApplicationVariant +import com.android.build.gradle.internal.variant.BaseVariantData +import com.android.build.gradle.internal.variant.LibraryVariantData +import com.android.build.gradle.internal.api.LibraryVariantImpl +import com.android.build.gradle.api.TestVariant +import com.android.build.gradle.internal.variant.TestVariantData +import com.android.build.gradle.internal.api.TestVariantImpl + +class DataBinderPlugin : Plugin<Project> { + + inner class GradleFileWriter(var outputBase: String) : JavaFileWriter() { + override fun writeToFile(canonicalName: String, contents: String) { + val f = File("$outputBase/${canonicalName.replaceAll("\\.", "/")}.java") + log("Asked to write to ${canonicalName}. outputting to:${f.getAbsolutePath()}") + f.getParentFile().mkdirs() + f.writeText(contents, "utf-8") + } + } + + override fun apply(project: Project?) { + if (project == null) return + project.afterEvaluate { + createXmlProcessor(project) + } + } + + fun log(s: String) { + System.out.println("[qwqw data binding]: $s") + } + + fun createXmlProcessor(p: Project) { + val androidExt = p.getExtensions().getByName("android") + if (androidExt !is BaseExtension) { + return + } + log("project build dir:${p.getBuildDir()}") + // TODO this will differ per flavor + + if (androidExt is AppExtension) { + createXmlProcessorForApp(p, androidExt) + } else if (androidExt is LibraryExtension) { + createXmlProcessorForLibrary(p, androidExt) + } else { + throw RuntimeException("cannot understand android extension. What is it? ${androidExt}") + } + } + + fun createXmlProcessorForLibrary(project : Project, lib : LibraryExtension) { + val sdkDir = lib.getSdkDirectory() + lib.getTestVariants().forEach { variant -> + log("test variant $variant. dir name ${variant.getDirName()}") + val variantData = getVariantData(variant) + attachXmlProcessor(project, variantData, sdkDir, false)//tests extend apk variant + } + lib.getLibraryVariants().forEach { variant -> + log("lib variant $variant . dir name ${variant.getDirName()}") + val variantData = getVariantData(variant) + attachXmlProcessor(project, variantData, sdkDir, true) + } + } + + fun getVariantData(appVariant : LibraryVariant) : LibraryVariantData { + val clazz = javaClass<LibraryVariantImpl>() + val field = clazz.getDeclaredField("variantData") + field.setAccessible(true) + return field.get(appVariant) as LibraryVariantData + } + + fun getVariantData(testVariant : TestVariant) : TestVariantData { + val clazz = javaClass<TestVariantImpl>() + val field = clazz.getDeclaredField("variantData") + field.setAccessible(true) + return field.get(testVariant) as TestVariantData + } + + fun getVariantData(appVariant : ApplicationVariant) : ApplicationVariantData { + val clazz = javaClass<ApplicationVariantImpl>() + val field = clazz.getDeclaredField("variantData") + field.setAccessible(true) + return field.get(appVariant) as ApplicationVariantData + } + + fun createXmlProcessorForApp(project : Project, appExt: AppExtension) { + val sdkDir = appExt.getSdkDirectory() + appExt.getTestVariants().forEach { testVariant -> + val variantData = getVariantData(testVariant) + attachXmlProcessor(project, variantData, sdkDir, false) + } + appExt.getApplicationVariants().forEach { appVariant -> + val variantData = getVariantData(appVariant) + attachXmlProcessor(project, variantData, sdkDir, false) + } + } + + fun attachXmlProcessor(project : Project, variantData : BaseVariantData<*>, sdkDir : File, + isLibrary : Boolean) { + val configuration = variantData.getVariantConfiguration() + val minSdkVersion = configuration.getMinSdkVersion() + val generateRTask = variantData.generateRClassTask + val packageName = generateRTask.getPackageForR() + log("r task name $generateRTask . text symbols output dir: ${generateRTask.getTextSymbolOutputDir()}") + val fullName = configuration.getFullName() + val sources = variantData.getJavaSources() + sources.forEach({ + if (it is FileCollection) { + it.forEach { + log("sources for ${variantData} ${it}}") + } + } else { + log("sources for ${variantData}: ${it}"); + } + }) + val resourceFolders = arrayListOf(variantData.mergeResourcesTask.getOutputDir()) + log("MERGE RES OUTPUT ${variantData.mergeResourcesTask.getOutputDir()}") + val codeGenTargetFolder = generateRTask.getSourceOutputDir() + // TODO unnecessary? + + // TODO attach to test module as well! + + variantData.addJavaSourceFoldersToModel(codeGenTargetFolder) + val writerOutBase = codeGenTargetFolder.getAbsolutePath(); + val fileWriter = GradleFileWriter(writerOutBase) + val xmlProcessor = LayoutXmlProcessor(packageName, resourceFolders, fileWriter, + minSdkVersion.getApiLevel(), isLibrary) + val processResTask = generateRTask + + val xmlOutDir = "${project.getBuildDir()}/layout-info/${configuration.getDirName()}"; + log("xml output for ${variantData} is ${xmlOutDir}") + val dataBindingTaskName = "dataBinding${processResTask.getName().capitalize()}" + log("created task $dataBindingTaskName") + project.getTasks().create(dataBindingTaskName, + javaClass<DataBindingProcessLayoutsTask>(), + object : Action<DataBindingProcessLayoutsTask> { + override fun execute(task: DataBindingProcessLayoutsTask) { + task.xmlProcessor = xmlProcessor + task.sdkDir = sdkDir + Log.d { "TASK adding dependency on ${task} for ${processResTask}" } + processResTask.dependsOn(task) + processResTask.getDependsOn().filterNot { it == task }.forEach { + Log.d { "adding dependency on ${it} for ${task}" } + task.dependsOn(it) + } + processResTask.doLast { + task.writeFiles(File(xmlOutDir)) + } + } + }); + + if (isLibrary) { + val packageJarTaskName = "package${fullName.capitalize()}Jar" + val packageTask = project.getTasks().findByName(packageJarTaskName) + if (packageTask !is org.gradle.api.tasks.bundling.Jar) { + throw RuntimeException("cannot find package task in $project $variantData project $packageJarTaskName") + } + val excludePattern = "android/databinding/layouts/*.*" + val appPkgAsClass = packageName.replace('.', '/') + packageTask.exclude(excludePattern) + packageTask.exclude("$appPkgAsClass/databinding/*") + packageTask.exclude("$appPkgAsClass/BR.*") + packageTask.exclude(xmlProcessor.getInfoClassFullName().replace('.', '/') + ".class") + log("excludes ${packageTask.getExcludes()}") + } + } +} diff --git a/tools/data-binding/gradlePlugin/src/main/resources/META-INF/gradle-plugins/com.android.databinding.properties b/tools/data-binding/gradlePlugin/src/main/resources/META-INF/gradle-plugins/com.android.databinding.properties new file mode 100644 index 0000000..2e04d00 --- /dev/null +++ b/tools/data-binding/gradlePlugin/src/main/resources/META-INF/gradle-plugins/com.android.databinding.properties @@ -0,0 +1,17 @@ +# +# Copyright (C) 2014 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. +# + +implementation-class=android.databinding.tool.DataBinderPlugin
\ No newline at end of file diff --git a/tools/data-binding/gradlew b/tools/data-binding/gradlew new file mode 100755 index 0000000..91a7e26 --- /dev/null +++ b/tools/data-binding/gradlew @@ -0,0 +1,164 @@ +#!/usr/bin/env bash + +############################################################################## +## +## Gradle start up script for UN*X +## +############################################################################## + +# Add default JVM options here. You can also use JAVA_OPTS and GRADLE_OPTS to pass JVM options to this script. +DEFAULT_JVM_OPTS="" + +APP_NAME="Gradle" +APP_BASE_NAME=`basename "$0"` + +# Use the maximum available, or set MAX_FD != -1 to use that value. +MAX_FD="maximum" + +warn ( ) { + echo "$*" +} + +die ( ) { + echo + echo "$*" + echo + exit 1 +} + +# OS specific support (must be 'true' or 'false'). +cygwin=false +msys=false +darwin=false +case "`uname`" in + CYGWIN* ) + cygwin=true + ;; + Darwin* ) + darwin=true + ;; + MINGW* ) + msys=true + ;; +esac + +# For Cygwin, ensure paths are in UNIX format before anything is touched. +if $cygwin ; then + [ -n "$JAVA_HOME" ] && JAVA_HOME=`cygpath --unix "$JAVA_HOME"` +fi + +# Attempt to set APP_HOME +# Resolve links: $0 may be a link +PRG="$0" +# Need this for relative symlinks. +while [ -h "$PRG" ] ; do + ls=`ls -ld "$PRG"` + link=`expr "$ls" : '.*-> \(.*\)$'` + if expr "$link" : '/.*' > /dev/null; then + PRG="$link" + else + PRG=`dirname "$PRG"`"/$link" + fi +done +SAVED="`pwd`" +cd "`dirname \"$PRG\"`/" >&- +APP_HOME="`pwd -P`" +cd "$SAVED" >&- + +CLASSPATH=$APP_HOME/gradle/wrapper/gradle-wrapper.jar + +# Determine the Java command to use to start the JVM. +if [ -n "$JAVA_HOME" ] ; then + if [ -x "$JAVA_HOME/jre/sh/java" ] ; then + # IBM's JDK on AIX uses strange locations for the executables + JAVACMD="$JAVA_HOME/jre/sh/java" + else + JAVACMD="$JAVA_HOME/bin/java" + fi + if [ ! -x "$JAVACMD" ] ; then + die "ERROR: JAVA_HOME is set to an invalid directory: $JAVA_HOME + +Please set the JAVA_HOME variable in your environment to match the +location of your Java installation." + fi +else + JAVACMD="java" + which java >/dev/null 2>&1 || die "ERROR: JAVA_HOME is not set and no 'java' command could be found in your PATH. + +Please set the JAVA_HOME variable in your environment to match the +location of your Java installation." +fi + +# Increase the maximum file descriptors if we can. +if [ "$cygwin" = "false" -a "$darwin" = "false" ] ; then + MAX_FD_LIMIT=`ulimit -H -n` + if [ $? -eq 0 ] ; then + if [ "$MAX_FD" = "maximum" -o "$MAX_FD" = "max" ] ; then + MAX_FD="$MAX_FD_LIMIT" + fi + ulimit -n $MAX_FD + if [ $? -ne 0 ] ; then + warn "Could not set maximum file descriptor limit: $MAX_FD" + fi + else + warn "Could not query maximum file descriptor limit: $MAX_FD_LIMIT" + fi +fi + +# For Darwin, add options to specify how the application appears in the dock +if $darwin; then + GRADLE_OPTS="$GRADLE_OPTS \"-Xdock:name=$APP_NAME\" \"-Xdock:icon=$APP_HOME/media/gradle.icns\"" +fi + +# For Cygwin, switch paths to Windows format before running java +if $cygwin ; then + APP_HOME=`cygpath --path --mixed "$APP_HOME"` + CLASSPATH=`cygpath --path --mixed "$CLASSPATH"` + + # We build the pattern for arguments to be converted via cygpath + ROOTDIRSRAW=`find -L / -maxdepth 1 -mindepth 1 -type d 2>/dev/null` + SEP="" + for dir in $ROOTDIRSRAW ; do + ROOTDIRS="$ROOTDIRS$SEP$dir" + SEP="|" + done + OURCYGPATTERN="(^($ROOTDIRS))" + # Add a user-defined pattern to the cygpath arguments + if [ "$GRADLE_CYGPATTERN" != "" ] ; then + OURCYGPATTERN="$OURCYGPATTERN|($GRADLE_CYGPATTERN)" + fi + # Now convert the arguments - kludge to limit ourselves to /bin/sh + i=0 + for arg in "$@" ; do + CHECK=`echo "$arg"|egrep -c "$OURCYGPATTERN" -` + CHECK2=`echo "$arg"|egrep -c "^-"` ### Determine if an option + + if [ $CHECK -ne 0 ] && [ $CHECK2 -eq 0 ] ; then ### Added a condition + eval `echo args$i`=`cygpath --path --ignore --mixed "$arg"` + else + eval `echo args$i`="\"$arg\"" + fi + i=$((i+1)) + done + case $i in + (0) set -- ;; + (1) set -- "$args0" ;; + (2) set -- "$args0" "$args1" ;; + (3) set -- "$args0" "$args1" "$args2" ;; + (4) set -- "$args0" "$args1" "$args2" "$args3" ;; + (5) set -- "$args0" "$args1" "$args2" "$args3" "$args4" ;; + (6) set -- "$args0" "$args1" "$args2" "$args3" "$args4" "$args5" ;; + (7) set -- "$args0" "$args1" "$args2" "$args3" "$args4" "$args5" "$args6" ;; + (8) set -- "$args0" "$args1" "$args2" "$args3" "$args4" "$args5" "$args6" "$args7" ;; + (9) set -- "$args0" "$args1" "$args2" "$args3" "$args4" "$args5" "$args6" "$args7" "$args8" ;; + esac +fi + +# Split up the JVM_OPTS And GRADLE_OPTS values into an array, following the shell quoting and substitution rules +function splitJvmOpts() { + JVM_OPTS=("$@") +} +eval splitJvmOpts $DEFAULT_JVM_OPTS $JAVA_OPTS $GRADLE_OPTS +JVM_OPTS[${#JVM_OPTS[*]}]="-Dorg.gradle.appname=$APP_BASE_NAME" + +exec "$JAVACMD" "${JVM_OPTS[@]}" -classpath "$CLASSPATH" org.gradle.wrapper.GradleWrapperMain "$@" diff --git a/tools/data-binding/grammarBuilder/BindingExpression.g4 b/tools/data-binding/grammarBuilder/BindingExpression.g4 new file mode 100644 index 0000000..3142507 --- /dev/null +++ b/tools/data-binding/grammarBuilder/BindingExpression.g4 @@ -0,0 +1,488 @@ +/* + [The "BSD licence"] + Copyright (c) 2013 Terence Parr, Sam Harwell + All rights reserved. + Redistribution and use in source and binary forms, with or without + modification, are permitted provided that the following conditions + are met: + 1. Redistributions of source code must retain the above copyright + notice, this list of conditions and the following disclaimer. + 2. Redistributions in binary form must reproduce the above copyright + notice, this list of conditions and the following disclaimer in the + documentation and/or other materials provided with the distribution. + 3. The name of the author may not be used to endorse or promote products + derived from this software without specific prior written permission. + THIS SOFTWARE IS PROVIDED BY THE AUTHOR ``AS IS'' AND ANY EXPRESS OR + IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE IMPLIED WARRANTIES + OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE ARE DISCLAIMED. + IN NO EVENT SHALL THE AUTHOR BE LIABLE FOR ANY DIRECT, INDIRECT, + INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING, BUT + NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; LOSS OF USE, + DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND ON ANY + THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT + (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE OF + THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE. +*/ +grammar BindingExpression; + +bindingSyntax + : expression defaults? + ; + +defaults + : ',' 'default' '=' constantValue + ; +constantValue + : literal + | ResourceReference + | identifier + ; + +expression + : '(' expression ')' # Grouping +// this isn't allowed yet. +// | THIS # Primary + | literal # Primary + | identifier # Primary + | classExtraction # Primary + | resources # Resource +// | typeArguments (explicitGenericInvocationSuffix | 'this' arguments) # GenericCall + | expression '.' Identifier # DotOp +// | expression '.' 'this' # ThisReference +// | expression '.' explicitGenericInvocation # ExplicitGenericInvocationOp + | expression '[' expression ']' # BracketOp + | target=expression '.' methodName=Identifier '(' args=expressionList? ')' # MethodInvocation + | '(' type ')' expression # CastOp + | op=('+'|'-') expression # UnaryOp + | op=('~'|'!') expression # UnaryOp + | left=expression op=('*'|'/'|'%') right=expression # MathOp + | left=expression op=('+'|'-') right=expression # MathOp + | left=expression op=('<<' | '>>>' | '>>') right=expression # BitShiftOp + | left=expression op=('<=' | '>=' | '>' | '<') right=expression # ComparisonOp + | expression 'instanceof' type # InstanceOfOp + | left=expression op=('==' | '!=') right=expression # ComparisonOp + | left=expression op='&' right=expression # BinaryOp + | left=expression op='^' right=expression # BinaryOp + | left=expression op='|' right=expression # BinaryOp + | left=expression op='&&' right=expression # AndOrOp + | left=expression op='||' right=expression # AndOrOp + | left=expression op='?' iftrue=expression ':' iffalse=expression # TernaryOp + | left=expression op='??' right=expression # QuestionQuestionOp + ; + +THIS + : 'this' + ; + +classExtraction + : type '.' 'class' + | 'void' '.' 'class' + ; + +expressionList + : expression (',' expression)* + ; + +literal + : javaLiteral + | stringLiteral + ; + +identifier + : Identifier + ; + +javaLiteral + : IntegerLiteral + | FloatingPointLiteral + | BooleanLiteral + | NullLiteral + | CharacterLiteral + ; + +stringLiteral + : SingleQuoteString + | DoubleQuoteString + ; + +explicitGenericInvocation + : typeArguments explicitGenericInvocationSuffix + ; + +typeArguments + : '<' type (',' type)* '>' + ; + +type + : classOrInterfaceType ('[' ']')* + | primitiveType ('[' ']')* + ; + +explicitGenericInvocationSuffix + : Identifier arguments + ; + +arguments + : '(' expressionList? ')' + ; + +classOrInterfaceType + : identifier typeArguments? ('.' Identifier typeArguments? )* + ; + +primitiveType + : 'boolean' + | 'char' + | 'byte' + | 'short' + | 'int' + | 'long' + | 'float' + | 'double' + ; + +resources + : ResourceReference resourceParameters? + ; + +resourceParameters + : '(' expressionList ')' + ; + +// LEXER + +// §3.10.1 Integer Literals + +IntegerLiteral + : DecimalIntegerLiteral + | HexIntegerLiteral + | OctalIntegerLiteral + | BinaryIntegerLiteral + ; + +fragment +DecimalIntegerLiteral + : DecimalNumeral IntegerTypeSuffix? + ; + +fragment +HexIntegerLiteral + : HexNumeral IntegerTypeSuffix? + ; + +fragment +OctalIntegerLiteral + : OctalNumeral IntegerTypeSuffix? + ; + +fragment +BinaryIntegerLiteral + : BinaryNumeral IntegerTypeSuffix? + ; + +fragment +IntegerTypeSuffix + : [lL] + ; + +fragment +DecimalNumeral + : '0' + | NonZeroDigit (Digits? | Underscores Digits) + ; + +fragment +Digits + : Digit (DigitOrUnderscore* Digit)? + ; + +fragment +Digit + : '0' + | NonZeroDigit + ; + +fragment +NonZeroDigit + : [1-9] + ; + +fragment +DigitOrUnderscore + : Digit + | '_' + ; + +fragment +Underscores + : '_'+ + ; + +fragment +HexNumeral + : '0' [xX] HexDigits + ; + +fragment +HexDigits + : HexDigit (HexDigitOrUnderscore* HexDigit)? + ; + +fragment +HexDigit + : [0-9a-fA-F] + ; + +fragment +HexDigitOrUnderscore + : HexDigit + | '_' + ; + +fragment +OctalNumeral + : '0' Underscores? OctalDigits + ; + +fragment +OctalDigits + : OctalDigit (OctalDigitOrUnderscore* OctalDigit)? + ; + +fragment +OctalDigit + : [0-7] + ; + +fragment +OctalDigitOrUnderscore + : OctalDigit + | '_' + ; + +fragment +BinaryNumeral + : '0' [bB] BinaryDigits + ; + +fragment +BinaryDigits + : BinaryDigit (BinaryDigitOrUnderscore* BinaryDigit)? + ; + +fragment +BinaryDigit + : [01] + ; + +fragment +BinaryDigitOrUnderscore + : BinaryDigit + | '_' + ; + +// §3.10.2 Floating-Point Literals + +FloatingPointLiteral + : DecimalFloatingPointLiteral + | HexadecimalFloatingPointLiteral + ; + +fragment +DecimalFloatingPointLiteral + : Digits '.' Digits? ExponentPart? FloatTypeSuffix? + | '.' Digits ExponentPart? FloatTypeSuffix? + | Digits ExponentPart FloatTypeSuffix? + | Digits FloatTypeSuffix + ; + +fragment +ExponentPart + : ExponentIndicator SignedInteger + ; + +fragment +ExponentIndicator + : [eE] + ; + +fragment +SignedInteger + : Sign? Digits + ; + +fragment +Sign + : [+-] + ; + +fragment +FloatTypeSuffix + : [fFdD] + ; + +fragment +HexadecimalFloatingPointLiteral + : HexSignificand BinaryExponent FloatTypeSuffix? + ; + +fragment +HexSignificand + : HexNumeral '.'? + | '0' [xX] HexDigits? '.' HexDigits + ; + +fragment +BinaryExponent + : BinaryExponentIndicator SignedInteger + ; + +fragment +BinaryExponentIndicator + : [pP] + ; + +// §3.10.3 Boolean Literals + +BooleanLiteral + : 'true' + | 'false' + ; + +// §3.10.4 Character Literals + +CharacterLiteral + : '\'' SingleCharacter '\'' + | '\'' EscapeSequence '\'' + ; + +fragment +SingleCharacter + : ~['\\] + ; +// §3.10.5 String Literals +SingleQuoteString + : '`' SingleQuoteStringCharacter* '`' + ; + +DoubleQuoteString + : '"' StringCharacters? '"' + ; + +fragment +StringCharacters + : StringCharacter+ + ; +fragment +StringCharacter + : ~["\\] + | EscapeSequence + ; +fragment +SingleQuoteStringCharacter + : ~[`\\] + | EscapeSequence + ; + +// §3.10.6 Escape Sequences for Character and String Literals +fragment +EscapeSequence + : '\\' [btnfr"'`\\] + | OctalEscape + | UnicodeEscape + ; + +fragment +OctalEscape + : '\\' OctalDigit + | '\\' OctalDigit OctalDigit + | '\\' ZeroToThree OctalDigit OctalDigit + ; + +fragment +UnicodeEscape + : '\\' 'u' HexDigit HexDigit HexDigit HexDigit + ; + +fragment +ZeroToThree + : [0-3] + ; + +// §3.10.7 The Null Literal + +NullLiteral + : 'null' + ; + +// §3.8 Identifiers (must appear after all keywords in the grammar) + +Identifier + : JavaLetter JavaLetterOrDigit* + ; + +fragment +JavaLetter + : [a-zA-Z$_] // these are the "java letters" below 0xFF + | // covers all characters above 0xFF which are not a surrogate + ~[\u0000-\u00FF\uD800-\uDBFF] + {Character.isJavaIdentifierStart(_input.LA(-1))}? + | // covers UTF-16 surrogate pairs encodings for U+10000 to U+10FFFF + [\uD800-\uDBFF] [\uDC00-\uDFFF] + {Character.isJavaIdentifierStart(Character.toCodePoint((char)_input.LA(-2), (char)_input.LA(-1)))}? + ; + +fragment +JavaLetterOrDigit + : [a-zA-Z0-9$_] // these are the "java letters or digits" below 0xFF + | // covers all characters above 0xFF which are not a surrogate + ~[\u0000-\u00FF\uD800-\uDBFF] + {Character.isJavaIdentifierPart(_input.LA(-1))}? + | // covers UTF-16 surrogate pairs encodings for U+10000 to U+10FFFF + [\uD800-\uDBFF] [\uDC00-\uDFFF] + {Character.isJavaIdentifierPart(Character.toCodePoint((char)_input.LA(-2), (char)_input.LA(-1)))}? + ; + +// +// Whitespace and comments +// + +WS : [ \t\r\n\u000C]+ -> skip + ; + +// +// Resource references +// + +ResourceReference + : '@' (PackageName ':')? ResourceType '/' Identifier + ; + +PackageName + : 'android' + | Identifier + ; + +ResourceType + : 'anim' + | 'animator' + | 'bool' + | 'color' + | 'colorStateList' + | 'dimen' + | 'dimenOffset' + | 'dimenSize' + | 'drawable' + | 'fraction' + | 'id' + | 'integer' + | 'intArray' + | 'interpolator' + | 'layout' + | 'plurals' + | 'stateListAnimator' + | 'string' + | 'stringArray' + | 'transition' + | 'typedArray' + ; diff --git a/tools/data-binding/grammarBuilder/build.gradle b/tools/data-binding/grammarBuilder/build.gradle new file mode 100644 index 0000000..b5f85a1 --- /dev/null +++ b/tools/data-binding/grammarBuilder/build.gradle @@ -0,0 +1,55 @@ +/* + * Copyright (C) 2014 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. + */ + +apply plugin: 'java' +apply plugin: 'application' +apply plugin: 'maven' + +sourceCompatibility = config.javaTargetCompatibility +targetCompatibility = config.javaSourceCompatibility + +mainClassName = "org.antlr.v4.Tool" + +run { + args "BindingExpression.g4", "-visitor", "-o", "src/main/java-gen/android/databinding/parser", "-package", "android.databinding.parser" +} + +sourceSets { + main { + java { + srcDir 'src/main/java' + srcDir 'src/main/java-gen' + } + } + test { + java { + srcDir 'src/test/java' + } + } +} + +dependencies { + compile 'com.tunnelvisionlabs:antlr4:4.4' + testCompile 'junit:junit:4.11' +} + +uploadArchives { + repositories { + mavenDeployer { + pom.artifactId = 'grammarBuilder' + } + } +} diff --git a/tools/data-binding/grammarBuilder/gradle/wrapper/gradle-wrapper.jar b/tools/data-binding/grammarBuilder/gradle/wrapper/gradle-wrapper.jar Binary files differnew file mode 100644 index 0000000..3d0dee6 --- /dev/null +++ b/tools/data-binding/grammarBuilder/gradle/wrapper/gradle-wrapper.jar diff --git a/tools/data-binding/grammarBuilder/gradle/wrapper/gradle-wrapper.properties b/tools/data-binding/grammarBuilder/gradle/wrapper/gradle-wrapper.properties new file mode 100644 index 0000000..b8e77ce --- /dev/null +++ b/tools/data-binding/grammarBuilder/gradle/wrapper/gradle-wrapper.properties @@ -0,0 +1,6 @@ +#Thu Dec 11 16:05:17 PST 2014 +distributionBase=GRADLE_USER_HOME +distributionPath=wrapper/dists +zipStoreBase=GRADLE_USER_HOME +zipStorePath=wrapper/dists +distributionUrl=https\://services.gradle.org/distributions/gradle-2.1-bin.zip diff --git a/tools/data-binding/grammarBuilder/gradlew b/tools/data-binding/grammarBuilder/gradlew new file mode 100755 index 0000000..91a7e26 --- /dev/null +++ b/tools/data-binding/grammarBuilder/gradlew @@ -0,0 +1,164 @@ +#!/usr/bin/env bash + +############################################################################## +## +## Gradle start up script for UN*X +## +############################################################################## + +# Add default JVM options here. You can also use JAVA_OPTS and GRADLE_OPTS to pass JVM options to this script. +DEFAULT_JVM_OPTS="" + +APP_NAME="Gradle" +APP_BASE_NAME=`basename "$0"` + +# Use the maximum available, or set MAX_FD != -1 to use that value. +MAX_FD="maximum" + +warn ( ) { + echo "$*" +} + +die ( ) { + echo + echo "$*" + echo + exit 1 +} + +# OS specific support (must be 'true' or 'false'). +cygwin=false +msys=false +darwin=false +case "`uname`" in + CYGWIN* ) + cygwin=true + ;; + Darwin* ) + darwin=true + ;; + MINGW* ) + msys=true + ;; +esac + +# For Cygwin, ensure paths are in UNIX format before anything is touched. +if $cygwin ; then + [ -n "$JAVA_HOME" ] && JAVA_HOME=`cygpath --unix "$JAVA_HOME"` +fi + +# Attempt to set APP_HOME +# Resolve links: $0 may be a link +PRG="$0" +# Need this for relative symlinks. +while [ -h "$PRG" ] ; do + ls=`ls -ld "$PRG"` + link=`expr "$ls" : '.*-> \(.*\)$'` + if expr "$link" : '/.*' > /dev/null; then + PRG="$link" + else + PRG=`dirname "$PRG"`"/$link" + fi +done +SAVED="`pwd`" +cd "`dirname \"$PRG\"`/" >&- +APP_HOME="`pwd -P`" +cd "$SAVED" >&- + +CLASSPATH=$APP_HOME/gradle/wrapper/gradle-wrapper.jar + +# Determine the Java command to use to start the JVM. +if [ -n "$JAVA_HOME" ] ; then + if [ -x "$JAVA_HOME/jre/sh/java" ] ; then + # IBM's JDK on AIX uses strange locations for the executables + JAVACMD="$JAVA_HOME/jre/sh/java" + else + JAVACMD="$JAVA_HOME/bin/java" + fi + if [ ! -x "$JAVACMD" ] ; then + die "ERROR: JAVA_HOME is set to an invalid directory: $JAVA_HOME + +Please set the JAVA_HOME variable in your environment to match the +location of your Java installation." + fi +else + JAVACMD="java" + which java >/dev/null 2>&1 || die "ERROR: JAVA_HOME is not set and no 'java' command could be found in your PATH. + +Please set the JAVA_HOME variable in your environment to match the +location of your Java installation." +fi + +# Increase the maximum file descriptors if we can. +if [ "$cygwin" = "false" -a "$darwin" = "false" ] ; then + MAX_FD_LIMIT=`ulimit -H -n` + if [ $? -eq 0 ] ; then + if [ "$MAX_FD" = "maximum" -o "$MAX_FD" = "max" ] ; then + MAX_FD="$MAX_FD_LIMIT" + fi + ulimit -n $MAX_FD + if [ $? -ne 0 ] ; then + warn "Could not set maximum file descriptor limit: $MAX_FD" + fi + else + warn "Could not query maximum file descriptor limit: $MAX_FD_LIMIT" + fi +fi + +# For Darwin, add options to specify how the application appears in the dock +if $darwin; then + GRADLE_OPTS="$GRADLE_OPTS \"-Xdock:name=$APP_NAME\" \"-Xdock:icon=$APP_HOME/media/gradle.icns\"" +fi + +# For Cygwin, switch paths to Windows format before running java +if $cygwin ; then + APP_HOME=`cygpath --path --mixed "$APP_HOME"` + CLASSPATH=`cygpath --path --mixed "$CLASSPATH"` + + # We build the pattern for arguments to be converted via cygpath + ROOTDIRSRAW=`find -L / -maxdepth 1 -mindepth 1 -type d 2>/dev/null` + SEP="" + for dir in $ROOTDIRSRAW ; do + ROOTDIRS="$ROOTDIRS$SEP$dir" + SEP="|" + done + OURCYGPATTERN="(^($ROOTDIRS))" + # Add a user-defined pattern to the cygpath arguments + if [ "$GRADLE_CYGPATTERN" != "" ] ; then + OURCYGPATTERN="$OURCYGPATTERN|($GRADLE_CYGPATTERN)" + fi + # Now convert the arguments - kludge to limit ourselves to /bin/sh + i=0 + for arg in "$@" ; do + CHECK=`echo "$arg"|egrep -c "$OURCYGPATTERN" -` + CHECK2=`echo "$arg"|egrep -c "^-"` ### Determine if an option + + if [ $CHECK -ne 0 ] && [ $CHECK2 -eq 0 ] ; then ### Added a condition + eval `echo args$i`=`cygpath --path --ignore --mixed "$arg"` + else + eval `echo args$i`="\"$arg\"" + fi + i=$((i+1)) + done + case $i in + (0) set -- ;; + (1) set -- "$args0" ;; + (2) set -- "$args0" "$args1" ;; + (3) set -- "$args0" "$args1" "$args2" ;; + (4) set -- "$args0" "$args1" "$args2" "$args3" ;; + (5) set -- "$args0" "$args1" "$args2" "$args3" "$args4" ;; + (6) set -- "$args0" "$args1" "$args2" "$args3" "$args4" "$args5" ;; + (7) set -- "$args0" "$args1" "$args2" "$args3" "$args4" "$args5" "$args6" ;; + (8) set -- "$args0" "$args1" "$args2" "$args3" "$args4" "$args5" "$args6" "$args7" ;; + (9) set -- "$args0" "$args1" "$args2" "$args3" "$args4" "$args5" "$args6" "$args7" "$args8" ;; + esac +fi + +# Split up the JVM_OPTS And GRADLE_OPTS values into an array, following the shell quoting and substitution rules +function splitJvmOpts() { + JVM_OPTS=("$@") +} +eval splitJvmOpts $DEFAULT_JVM_OPTS $JAVA_OPTS $GRADLE_OPTS +JVM_OPTS[${#JVM_OPTS[*]}]="-Dorg.gradle.appname=$APP_BASE_NAME" + +exec "$JAVACMD" "${JVM_OPTS[@]}" -classpath "$CLASSPATH" org.gradle.wrapper.GradleWrapperMain "$@" diff --git a/tools/data-binding/grammarBuilder/gradlew.bat b/tools/data-binding/grammarBuilder/gradlew.bat new file mode 100644 index 0000000..aec9973 --- /dev/null +++ b/tools/data-binding/grammarBuilder/gradlew.bat @@ -0,0 +1,90 @@ +@if "%DEBUG%" == "" @echo off
+@rem ##########################################################################
+@rem
+@rem Gradle startup script for Windows
+@rem
+@rem ##########################################################################
+
+@rem Set local scope for the variables with windows NT shell
+if "%OS%"=="Windows_NT" setlocal
+
+@rem Add default JVM options here. You can also use JAVA_OPTS and GRADLE_OPTS to pass JVM options to this script.
+set DEFAULT_JVM_OPTS=
+
+set DIRNAME=%~dp0
+if "%DIRNAME%" == "" set DIRNAME=.
+set APP_BASE_NAME=%~n0
+set APP_HOME=%DIRNAME%
+
+@rem Find java.exe
+if defined JAVA_HOME goto findJavaFromJavaHome
+
+set JAVA_EXE=java.exe
+%JAVA_EXE% -version >NUL 2>&1
+if "%ERRORLEVEL%" == "0" goto init
+
+echo.
+echo ERROR: JAVA_HOME is not set and no 'java' command could be found in your PATH.
+echo.
+echo Please set the JAVA_HOME variable in your environment to match the
+echo location of your Java installation.
+
+goto fail
+
+:findJavaFromJavaHome
+set JAVA_HOME=%JAVA_HOME:"=%
+set JAVA_EXE=%JAVA_HOME%/bin/java.exe
+
+if exist "%JAVA_EXE%" goto init
+
+echo.
+echo ERROR: JAVA_HOME is set to an invalid directory: %JAVA_HOME%
+echo.
+echo Please set the JAVA_HOME variable in your environment to match the
+echo location of your Java installation.
+
+goto fail
+
+:init
+@rem Get command-line arguments, handling Windowz variants
+
+if not "%OS%" == "Windows_NT" goto win9xME_args
+if "%@eval[2+2]" == "4" goto 4NT_args
+
+:win9xME_args
+@rem Slurp the command line arguments.
+set CMD_LINE_ARGS=
+set _SKIP=2
+
+:win9xME_args_slurp
+if "x%~1" == "x" goto execute
+
+set CMD_LINE_ARGS=%*
+goto execute
+
+:4NT_args
+@rem Get arguments from the 4NT Shell from JP Software
+set CMD_LINE_ARGS=%$
+
+:execute
+@rem Setup the command line
+
+set CLASSPATH=%APP_HOME%\gradle\wrapper\gradle-wrapper.jar
+
+@rem Execute Gradle
+"%JAVA_EXE%" %DEFAULT_JVM_OPTS% %JAVA_OPTS% %GRADLE_OPTS% "-Dorg.gradle.appname=%APP_BASE_NAME%" -classpath "%CLASSPATH%" org.gradle.wrapper.GradleWrapperMain %CMD_LINE_ARGS%
+
+:end
+@rem End local scope for the variables with windows NT shell
+if "%ERRORLEVEL%"=="0" goto mainEnd
+
+:fail
+rem Set variable GRADLE_EXIT_CONSOLE if you need the _script_ return code instead of
+rem the _cmd.exe /c_ return code!
+if not "" == "%GRADLE_EXIT_CONSOLE%" exit 1
+exit /b 1
+
+:mainEnd
+if "%OS%"=="Windows_NT" endlocal
+
+:omega
diff --git a/tools/data-binding/grammarBuilder/src/main/java-gen/android/databinding/parser/BindingExpression.tokens b/tools/data-binding/grammarBuilder/src/main/java-gen/android/databinding/parser/BindingExpression.tokens new file mode 100644 index 0000000..d379280 --- /dev/null +++ b/tools/data-binding/grammarBuilder/src/main/java-gen/android/databinding/parser/BindingExpression.tokens @@ -0,0 +1,101 @@ +NullLiteral=51 +T__29=14 +T__28=15 +T__27=16 +T__26=17 +T__25=18 +T__24=19 +T__23=20 +T__22=21 +CharacterLiteral=48 +T__21=22 +T__20=23 +SingleQuoteString=49 +T__9=34 +T__8=35 +Identifier=52 +T__7=36 +T__6=37 +T__5=38 +T__4=39 +T__19=24 +T__16=27 +T__15=28 +T__18=25 +T__17=26 +T__12=31 +T__11=32 +T__14=29 +T__13=30 +T__10=33 +THIS=44 +PackageName=55 +DoubleQuoteString=50 +T__42=1 +T__40=3 +T__41=2 +ResourceType=56 +T__30=13 +T__31=12 +T__32=11 +WS=53 +T__33=10 +T__34=9 +T__35=8 +T__36=7 +T__37=6 +T__38=5 +T__39=4 +T__1=42 +T__0=43 +FloatingPointLiteral=46 +T__3=40 +T__2=41 +IntegerLiteral=45 +ResourceReference=54 +BooleanLiteral=47 +'!'=43 +'instanceof'=42 +'|'=41 +'class'=40 +'>='=39 +'~'=38 +'/'=37 +'=='=36 +'??'=35 +'null'=51 +'>'=34 +'||'=33 +'this'=44 +'&&'=32 +'='=31 +'+'=30 +'.'=29 +')'=28 +'byte'=27 +'^'=26 +'%'=25 +'>>'=23 +'char'=24 +'float'=22 +'boolean'=21 +'double'=20 +'<<'=18 +'void'=19 +'?'=17 +'<='=16 +'!='=15 +'<'=13 +'int'=14 +':'=12 +'('=11 +'-'=10 +'['=9 +'*'=8 +','=7 +'default'=6 +'&'=5 +'short'=4 +']'=3 +'>>>'=2 +'long'=1 diff --git a/tools/data-binding/grammarBuilder/src/main/java-gen/android/databinding/parser/BindingExpressionBaseListener.java b/tools/data-binding/grammarBuilder/src/main/java-gen/android/databinding/parser/BindingExpressionBaseListener.java new file mode 100644 index 0000000..db87538 --- /dev/null +++ b/tools/data-binding/grammarBuilder/src/main/java-gen/android/databinding/parser/BindingExpressionBaseListener.java @@ -0,0 +1,495 @@ +// Generated from BindingExpression.g4 by ANTLR 4.4 +package android.databinding.parser; + +import org.antlr.v4.runtime.ParserRuleContext; +import org.antlr.v4.runtime.Token; +import org.antlr.v4.runtime.misc.NotNull; +import org.antlr.v4.runtime.tree.ErrorNode; +import org.antlr.v4.runtime.tree.TerminalNode; + +/** + * This class provides an empty implementation of {@link BindingExpressionListener}, + * which can be extended to create a listener which only needs to handle a subset + * of the available methods. + */ +public class BindingExpressionBaseListener implements BindingExpressionListener { + /** + * {@inheritDoc} + * + * <p>The default implementation does nothing.</p> + */ + @Override public void enterExpression(@NotNull BindingExpressionParser.ExpressionContext ctx) { } + /** + * {@inheritDoc} + * + * <p>The default implementation does nothing.</p> + */ + @Override public void exitExpression(@NotNull BindingExpressionParser.ExpressionContext ctx) { } + + /** + * {@inheritDoc} + * + * <p>The default implementation does nothing.</p> + */ + @Override public void enterResources(@NotNull BindingExpressionParser.ResourcesContext ctx) { } + /** + * {@inheritDoc} + * + * <p>The default implementation does nothing.</p> + */ + @Override public void exitResources(@NotNull BindingExpressionParser.ResourcesContext ctx) { } + + /** + * {@inheritDoc} + * + * <p>The default implementation does nothing.</p> + */ + @Override public void enterBracketOp(@NotNull BindingExpressionParser.BracketOpContext ctx) { } + /** + * {@inheritDoc} + * + * <p>The default implementation does nothing.</p> + */ + @Override public void exitBracketOp(@NotNull BindingExpressionParser.BracketOpContext ctx) { } + + /** + * {@inheritDoc} + * + * <p>The default implementation does nothing.</p> + */ + @Override public void enterUnaryOp(@NotNull BindingExpressionParser.UnaryOpContext ctx) { } + /** + * {@inheritDoc} + * + * <p>The default implementation does nothing.</p> + */ + @Override public void exitUnaryOp(@NotNull BindingExpressionParser.UnaryOpContext ctx) { } + + /** + * {@inheritDoc} + * + * <p>The default implementation does nothing.</p> + */ + @Override public void enterCastOp(@NotNull BindingExpressionParser.CastOpContext ctx) { } + /** + * {@inheritDoc} + * + * <p>The default implementation does nothing.</p> + */ + @Override public void exitCastOp(@NotNull BindingExpressionParser.CastOpContext ctx) { } + + /** + * {@inheritDoc} + * + * <p>The default implementation does nothing.</p> + */ + @Override public void enterResourceParameters(@NotNull BindingExpressionParser.ResourceParametersContext ctx) { } + /** + * {@inheritDoc} + * + * <p>The default implementation does nothing.</p> + */ + @Override public void exitResourceParameters(@NotNull BindingExpressionParser.ResourceParametersContext ctx) { } + + /** + * {@inheritDoc} + * + * <p>The default implementation does nothing.</p> + */ + @Override public void enterAndOrOp(@NotNull BindingExpressionParser.AndOrOpContext ctx) { } + /** + * {@inheritDoc} + * + * <p>The default implementation does nothing.</p> + */ + @Override public void exitAndOrOp(@NotNull BindingExpressionParser.AndOrOpContext ctx) { } + + /** + * {@inheritDoc} + * + * <p>The default implementation does nothing.</p> + */ + @Override public void enterMethodInvocation(@NotNull BindingExpressionParser.MethodInvocationContext ctx) { } + /** + * {@inheritDoc} + * + * <p>The default implementation does nothing.</p> + */ + @Override public void exitMethodInvocation(@NotNull BindingExpressionParser.MethodInvocationContext ctx) { } + + /** + * {@inheritDoc} + * + * <p>The default implementation does nothing.</p> + */ + @Override public void enterExpressionList(@NotNull BindingExpressionParser.ExpressionListContext ctx) { } + /** + * {@inheritDoc} + * + * <p>The default implementation does nothing.</p> + */ + @Override public void exitExpressionList(@NotNull BindingExpressionParser.ExpressionListContext ctx) { } + + /** + * {@inheritDoc} + * + * <p>The default implementation does nothing.</p> + */ + @Override public void enterClassOrInterfaceType(@NotNull BindingExpressionParser.ClassOrInterfaceTypeContext ctx) { } + /** + * {@inheritDoc} + * + * <p>The default implementation does nothing.</p> + */ + @Override public void exitClassOrInterfaceType(@NotNull BindingExpressionParser.ClassOrInterfaceTypeContext ctx) { } + + /** + * {@inheritDoc} + * + * <p>The default implementation does nothing.</p> + */ + @Override public void enterStringLiteral(@NotNull BindingExpressionParser.StringLiteralContext ctx) { } + /** + * {@inheritDoc} + * + * <p>The default implementation does nothing.</p> + */ + @Override public void exitStringLiteral(@NotNull BindingExpressionParser.StringLiteralContext ctx) { } + + /** + * {@inheritDoc} + * + * <p>The default implementation does nothing.</p> + */ + @Override public void enterPrimary(@NotNull BindingExpressionParser.PrimaryContext ctx) { } + /** + * {@inheritDoc} + * + * <p>The default implementation does nothing.</p> + */ + @Override public void exitPrimary(@NotNull BindingExpressionParser.PrimaryContext ctx) { } + + /** + * {@inheritDoc} + * + * <p>The default implementation does nothing.</p> + */ + @Override public void enterType(@NotNull BindingExpressionParser.TypeContext ctx) { } + /** + * {@inheritDoc} + * + * <p>The default implementation does nothing.</p> + */ + @Override public void exitType(@NotNull BindingExpressionParser.TypeContext ctx) { } + + /** + * {@inheritDoc} + * + * <p>The default implementation does nothing.</p> + */ + @Override public void enterBindingSyntax(@NotNull BindingExpressionParser.BindingSyntaxContext ctx) { } + /** + * {@inheritDoc} + * + * <p>The default implementation does nothing.</p> + */ + @Override public void exitBindingSyntax(@NotNull BindingExpressionParser.BindingSyntaxContext ctx) { } + + /** + * {@inheritDoc} + * + * <p>The default implementation does nothing.</p> + */ + @Override public void enterComparisonOp(@NotNull BindingExpressionParser.ComparisonOpContext ctx) { } + /** + * {@inheritDoc} + * + * <p>The default implementation does nothing.</p> + */ + @Override public void exitComparisonOp(@NotNull BindingExpressionParser.ComparisonOpContext ctx) { } + + /** + * {@inheritDoc} + * + * <p>The default implementation does nothing.</p> + */ + @Override public void enterTernaryOp(@NotNull BindingExpressionParser.TernaryOpContext ctx) { } + /** + * {@inheritDoc} + * + * <p>The default implementation does nothing.</p> + */ + @Override public void exitTernaryOp(@NotNull BindingExpressionParser.TernaryOpContext ctx) { } + + /** + * {@inheritDoc} + * + * <p>The default implementation does nothing.</p> + */ + @Override public void enterConstantValue(@NotNull BindingExpressionParser.ConstantValueContext ctx) { } + /** + * {@inheritDoc} + * + * <p>The default implementation does nothing.</p> + */ + @Override public void exitConstantValue(@NotNull BindingExpressionParser.ConstantValueContext ctx) { } + + /** + * {@inheritDoc} + * + * <p>The default implementation does nothing.</p> + */ + @Override public void enterDotOp(@NotNull BindingExpressionParser.DotOpContext ctx) { } + /** + * {@inheritDoc} + * + * <p>The default implementation does nothing.</p> + */ + @Override public void exitDotOp(@NotNull BindingExpressionParser.DotOpContext ctx) { } + + /** + * {@inheritDoc} + * + * <p>The default implementation does nothing.</p> + */ + @Override public void enterDefaults(@NotNull BindingExpressionParser.DefaultsContext ctx) { } + /** + * {@inheritDoc} + * + * <p>The default implementation does nothing.</p> + */ + @Override public void exitDefaults(@NotNull BindingExpressionParser.DefaultsContext ctx) { } + + /** + * {@inheritDoc} + * + * <p>The default implementation does nothing.</p> + */ + @Override public void enterBitShiftOp(@NotNull BindingExpressionParser.BitShiftOpContext ctx) { } + /** + * {@inheritDoc} + * + * <p>The default implementation does nothing.</p> + */ + @Override public void exitBitShiftOp(@NotNull BindingExpressionParser.BitShiftOpContext ctx) { } + + /** + * {@inheritDoc} + * + * <p>The default implementation does nothing.</p> + */ + @Override public void enterInstanceOfOp(@NotNull BindingExpressionParser.InstanceOfOpContext ctx) { } + /** + * {@inheritDoc} + * + * <p>The default implementation does nothing.</p> + */ + @Override public void exitInstanceOfOp(@NotNull BindingExpressionParser.InstanceOfOpContext ctx) { } + + /** + * {@inheritDoc} + * + * <p>The default implementation does nothing.</p> + */ + @Override public void enterBinaryOp(@NotNull BindingExpressionParser.BinaryOpContext ctx) { } + /** + * {@inheritDoc} + * + * <p>The default implementation does nothing.</p> + */ + @Override public void exitBinaryOp(@NotNull BindingExpressionParser.BinaryOpContext ctx) { } + + /** + * {@inheritDoc} + * + * <p>The default implementation does nothing.</p> + */ + @Override public void enterExplicitGenericInvocation(@NotNull BindingExpressionParser.ExplicitGenericInvocationContext ctx) { } + /** + * {@inheritDoc} + * + * <p>The default implementation does nothing.</p> + */ + @Override public void exitExplicitGenericInvocation(@NotNull BindingExpressionParser.ExplicitGenericInvocationContext ctx) { } + + /** + * {@inheritDoc} + * + * <p>The default implementation does nothing.</p> + */ + @Override public void enterResource(@NotNull BindingExpressionParser.ResourceContext ctx) { } + /** + * {@inheritDoc} + * + * <p>The default implementation does nothing.</p> + */ + @Override public void exitResource(@NotNull BindingExpressionParser.ResourceContext ctx) { } + + /** + * {@inheritDoc} + * + * <p>The default implementation does nothing.</p> + */ + @Override public void enterTypeArguments(@NotNull BindingExpressionParser.TypeArgumentsContext ctx) { } + /** + * {@inheritDoc} + * + * <p>The default implementation does nothing.</p> + */ + @Override public void exitTypeArguments(@NotNull BindingExpressionParser.TypeArgumentsContext ctx) { } + + /** + * {@inheritDoc} + * + * <p>The default implementation does nothing.</p> + */ + @Override public void enterGrouping(@NotNull BindingExpressionParser.GroupingContext ctx) { } + /** + * {@inheritDoc} + * + * <p>The default implementation does nothing.</p> + */ + @Override public void exitGrouping(@NotNull BindingExpressionParser.GroupingContext ctx) { } + + /** + * {@inheritDoc} + * + * <p>The default implementation does nothing.</p> + */ + @Override public void enterMathOp(@NotNull BindingExpressionParser.MathOpContext ctx) { } + /** + * {@inheritDoc} + * + * <p>The default implementation does nothing.</p> + */ + @Override public void exitMathOp(@NotNull BindingExpressionParser.MathOpContext ctx) { } + + /** + * {@inheritDoc} + * + * <p>The default implementation does nothing.</p> + */ + @Override public void enterClassExtraction(@NotNull BindingExpressionParser.ClassExtractionContext ctx) { } + /** + * {@inheritDoc} + * + * <p>The default implementation does nothing.</p> + */ + @Override public void exitClassExtraction(@NotNull BindingExpressionParser.ClassExtractionContext ctx) { } + + /** + * {@inheritDoc} + * + * <p>The default implementation does nothing.</p> + */ + @Override public void enterArguments(@NotNull BindingExpressionParser.ArgumentsContext ctx) { } + /** + * {@inheritDoc} + * + * <p>The default implementation does nothing.</p> + */ + @Override public void exitArguments(@NotNull BindingExpressionParser.ArgumentsContext ctx) { } + + /** + * {@inheritDoc} + * + * <p>The default implementation does nothing.</p> + */ + @Override public void enterPrimitiveType(@NotNull BindingExpressionParser.PrimitiveTypeContext ctx) { } + /** + * {@inheritDoc} + * + * <p>The default implementation does nothing.</p> + */ + @Override public void exitPrimitiveType(@NotNull BindingExpressionParser.PrimitiveTypeContext ctx) { } + + /** + * {@inheritDoc} + * + * <p>The default implementation does nothing.</p> + */ + @Override public void enterQuestionQuestionOp(@NotNull BindingExpressionParser.QuestionQuestionOpContext ctx) { } + /** + * {@inheritDoc} + * + * <p>The default implementation does nothing.</p> + */ + @Override public void exitQuestionQuestionOp(@NotNull BindingExpressionParser.QuestionQuestionOpContext ctx) { } + + /** + * {@inheritDoc} + * + * <p>The default implementation does nothing.</p> + */ + @Override public void enterJavaLiteral(@NotNull BindingExpressionParser.JavaLiteralContext ctx) { } + /** + * {@inheritDoc} + * + * <p>The default implementation does nothing.</p> + */ + @Override public void exitJavaLiteral(@NotNull BindingExpressionParser.JavaLiteralContext ctx) { } + + /** + * {@inheritDoc} + * + * <p>The default implementation does nothing.</p> + */ + @Override public void enterExplicitGenericInvocationSuffix(@NotNull BindingExpressionParser.ExplicitGenericInvocationSuffixContext ctx) { } + /** + * {@inheritDoc} + * + * <p>The default implementation does nothing.</p> + */ + @Override public void exitExplicitGenericInvocationSuffix(@NotNull BindingExpressionParser.ExplicitGenericInvocationSuffixContext ctx) { } + + /** + * {@inheritDoc} + * + * <p>The default implementation does nothing.</p> + */ + @Override public void enterIdentifier(@NotNull BindingExpressionParser.IdentifierContext ctx) { } + /** + * {@inheritDoc} + * + * <p>The default implementation does nothing.</p> + */ + @Override public void exitIdentifier(@NotNull BindingExpressionParser.IdentifierContext ctx) { } + + /** + * {@inheritDoc} + * + * <p>The default implementation does nothing.</p> + */ + @Override public void enterLiteral(@NotNull BindingExpressionParser.LiteralContext ctx) { } + /** + * {@inheritDoc} + * + * <p>The default implementation does nothing.</p> + */ + @Override public void exitLiteral(@NotNull BindingExpressionParser.LiteralContext ctx) { } + + /** + * {@inheritDoc} + * + * <p>The default implementation does nothing.</p> + */ + @Override public void enterEveryRule(@NotNull ParserRuleContext ctx) { } + /** + * {@inheritDoc} + * + * <p>The default implementation does nothing.</p> + */ + @Override public void exitEveryRule(@NotNull ParserRuleContext ctx) { } + /** + * {@inheritDoc} + * + * <p>The default implementation does nothing.</p> + */ + @Override public void visitTerminal(@NotNull TerminalNode node) { } + /** + * {@inheritDoc} + * + * <p>The default implementation does nothing.</p> + */ + @Override public void visitErrorNode(@NotNull ErrorNode node) { } +}
\ No newline at end of file diff --git a/tools/data-binding/grammarBuilder/src/main/java-gen/android/databinding/parser/BindingExpressionBaseVisitor.java b/tools/data-binding/grammarBuilder/src/main/java-gen/android/databinding/parser/BindingExpressionBaseVisitor.java new file mode 100644 index 0000000..d7d426e --- /dev/null +++ b/tools/data-binding/grammarBuilder/src/main/java-gen/android/databinding/parser/BindingExpressionBaseVisitor.java @@ -0,0 +1,295 @@ +// Generated from BindingExpression.g4 by ANTLR 4.4 +package android.databinding.parser; +import org.antlr.v4.runtime.Token; +import org.antlr.v4.runtime.misc.NotNull; +import org.antlr.v4.runtime.tree.AbstractParseTreeVisitor; + +/** + * This class provides an empty implementation of {@link BindingExpressionVisitor}, + * which can be extended to create a visitor which only needs to handle a subset + * of the available methods. + * + * @param <Result> The return type of the visit operation. Use {@link Void} for + * operations with no return type. + */ +public class BindingExpressionBaseVisitor<Result> extends AbstractParseTreeVisitor<Result> implements BindingExpressionVisitor<Result> { + /** + * {@inheritDoc} + * + * <p>The default implementation returns the result of calling + * {@link #visitChildren} on {@code ctx}.</p> + */ + @Override public Result visitExpression(@NotNull BindingExpressionParser.ExpressionContext ctx) { return visitChildren(ctx); } + + /** + * {@inheritDoc} + * + * <p>The default implementation returns the result of calling + * {@link #visitChildren} on {@code ctx}.</p> + */ + @Override public Result visitResources(@NotNull BindingExpressionParser.ResourcesContext ctx) { return visitChildren(ctx); } + + /** + * {@inheritDoc} + * + * <p>The default implementation returns the result of calling + * {@link #visitChildren} on {@code ctx}.</p> + */ + @Override public Result visitBracketOp(@NotNull BindingExpressionParser.BracketOpContext ctx) { return visitChildren(ctx); } + + /** + * {@inheritDoc} + * + * <p>The default implementation returns the result of calling + * {@link #visitChildren} on {@code ctx}.</p> + */ + @Override public Result visitUnaryOp(@NotNull BindingExpressionParser.UnaryOpContext ctx) { return visitChildren(ctx); } + + /** + * {@inheritDoc} + * + * <p>The default implementation returns the result of calling + * {@link #visitChildren} on {@code ctx}.</p> + */ + @Override public Result visitCastOp(@NotNull BindingExpressionParser.CastOpContext ctx) { return visitChildren(ctx); } + + /** + * {@inheritDoc} + * + * <p>The default implementation returns the result of calling + * {@link #visitChildren} on {@code ctx}.</p> + */ + @Override public Result visitResourceParameters(@NotNull BindingExpressionParser.ResourceParametersContext ctx) { return visitChildren(ctx); } + + /** + * {@inheritDoc} + * + * <p>The default implementation returns the result of calling + * {@link #visitChildren} on {@code ctx}.</p> + */ + @Override public Result visitAndOrOp(@NotNull BindingExpressionParser.AndOrOpContext ctx) { return visitChildren(ctx); } + + /** + * {@inheritDoc} + * + * <p>The default implementation returns the result of calling + * {@link #visitChildren} on {@code ctx}.</p> + */ + @Override public Result visitMethodInvocation(@NotNull BindingExpressionParser.MethodInvocationContext ctx) { return visitChildren(ctx); } + + /** + * {@inheritDoc} + * + * <p>The default implementation returns the result of calling + * {@link #visitChildren} on {@code ctx}.</p> + */ + @Override public Result visitExpressionList(@NotNull BindingExpressionParser.ExpressionListContext ctx) { return visitChildren(ctx); } + + /** + * {@inheritDoc} + * + * <p>The default implementation returns the result of calling + * {@link #visitChildren} on {@code ctx}.</p> + */ + @Override public Result visitClassOrInterfaceType(@NotNull BindingExpressionParser.ClassOrInterfaceTypeContext ctx) { return visitChildren(ctx); } + + /** + * {@inheritDoc} + * + * <p>The default implementation returns the result of calling + * {@link #visitChildren} on {@code ctx}.</p> + */ + @Override public Result visitStringLiteral(@NotNull BindingExpressionParser.StringLiteralContext ctx) { return visitChildren(ctx); } + + /** + * {@inheritDoc} + * + * <p>The default implementation returns the result of calling + * {@link #visitChildren} on {@code ctx}.</p> + */ + @Override public Result visitPrimary(@NotNull BindingExpressionParser.PrimaryContext ctx) { return visitChildren(ctx); } + + /** + * {@inheritDoc} + * + * <p>The default implementation returns the result of calling + * {@link #visitChildren} on {@code ctx}.</p> + */ + @Override public Result visitType(@NotNull BindingExpressionParser.TypeContext ctx) { return visitChildren(ctx); } + + /** + * {@inheritDoc} + * + * <p>The default implementation returns the result of calling + * {@link #visitChildren} on {@code ctx}.</p> + */ + @Override public Result visitBindingSyntax(@NotNull BindingExpressionParser.BindingSyntaxContext ctx) { return visitChildren(ctx); } + + /** + * {@inheritDoc} + * + * <p>The default implementation returns the result of calling + * {@link #visitChildren} on {@code ctx}.</p> + */ + @Override public Result visitComparisonOp(@NotNull BindingExpressionParser.ComparisonOpContext ctx) { return visitChildren(ctx); } + + /** + * {@inheritDoc} + * + * <p>The default implementation returns the result of calling + * {@link #visitChildren} on {@code ctx}.</p> + */ + @Override public Result visitTernaryOp(@NotNull BindingExpressionParser.TernaryOpContext ctx) { return visitChildren(ctx); } + + /** + * {@inheritDoc} + * + * <p>The default implementation returns the result of calling + * {@link #visitChildren} on {@code ctx}.</p> + */ + @Override public Result visitConstantValue(@NotNull BindingExpressionParser.ConstantValueContext ctx) { return visitChildren(ctx); } + + /** + * {@inheritDoc} + * + * <p>The default implementation returns the result of calling + * {@link #visitChildren} on {@code ctx}.</p> + */ + @Override public Result visitDotOp(@NotNull BindingExpressionParser.DotOpContext ctx) { return visitChildren(ctx); } + + /** + * {@inheritDoc} + * + * <p>The default implementation returns the result of calling + * {@link #visitChildren} on {@code ctx}.</p> + */ + @Override public Result visitDefaults(@NotNull BindingExpressionParser.DefaultsContext ctx) { return visitChildren(ctx); } + + /** + * {@inheritDoc} + * + * <p>The default implementation returns the result of calling + * {@link #visitChildren} on {@code ctx}.</p> + */ + @Override public Result visitBitShiftOp(@NotNull BindingExpressionParser.BitShiftOpContext ctx) { return visitChildren(ctx); } + + /** + * {@inheritDoc} + * + * <p>The default implementation returns the result of calling + * {@link #visitChildren} on {@code ctx}.</p> + */ + @Override public Result visitInstanceOfOp(@NotNull BindingExpressionParser.InstanceOfOpContext ctx) { return visitChildren(ctx); } + + /** + * {@inheritDoc} + * + * <p>The default implementation returns the result of calling + * {@link #visitChildren} on {@code ctx}.</p> + */ + @Override public Result visitBinaryOp(@NotNull BindingExpressionParser.BinaryOpContext ctx) { return visitChildren(ctx); } + + /** + * {@inheritDoc} + * + * <p>The default implementation returns the result of calling + * {@link #visitChildren} on {@code ctx}.</p> + */ + @Override public Result visitExplicitGenericInvocation(@NotNull BindingExpressionParser.ExplicitGenericInvocationContext ctx) { return visitChildren(ctx); } + + /** + * {@inheritDoc} + * + * <p>The default implementation returns the result of calling + * {@link #visitChildren} on {@code ctx}.</p> + */ + @Override public Result visitResource(@NotNull BindingExpressionParser.ResourceContext ctx) { return visitChildren(ctx); } + + /** + * {@inheritDoc} + * + * <p>The default implementation returns the result of calling + * {@link #visitChildren} on {@code ctx}.</p> + */ + @Override public Result visitTypeArguments(@NotNull BindingExpressionParser.TypeArgumentsContext ctx) { return visitChildren(ctx); } + + /** + * {@inheritDoc} + * + * <p>The default implementation returns the result of calling + * {@link #visitChildren} on {@code ctx}.</p> + */ + @Override public Result visitGrouping(@NotNull BindingExpressionParser.GroupingContext ctx) { return visitChildren(ctx); } + + /** + * {@inheritDoc} + * + * <p>The default implementation returns the result of calling + * {@link #visitChildren} on {@code ctx}.</p> + */ + @Override public Result visitMathOp(@NotNull BindingExpressionParser.MathOpContext ctx) { return visitChildren(ctx); } + + /** + * {@inheritDoc} + * + * <p>The default implementation returns the result of calling + * {@link #visitChildren} on {@code ctx}.</p> + */ + @Override public Result visitClassExtraction(@NotNull BindingExpressionParser.ClassExtractionContext ctx) { return visitChildren(ctx); } + + /** + * {@inheritDoc} + * + * <p>The default implementation returns the result of calling + * {@link #visitChildren} on {@code ctx}.</p> + */ + @Override public Result visitArguments(@NotNull BindingExpressionParser.ArgumentsContext ctx) { return visitChildren(ctx); } + + /** + * {@inheritDoc} + * + * <p>The default implementation returns the result of calling + * {@link #visitChildren} on {@code ctx}.</p> + */ + @Override public Result visitPrimitiveType(@NotNull BindingExpressionParser.PrimitiveTypeContext ctx) { return visitChildren(ctx); } + + /** + * {@inheritDoc} + * + * <p>The default implementation returns the result of calling + * {@link #visitChildren} on {@code ctx}.</p> + */ + @Override public Result visitQuestionQuestionOp(@NotNull BindingExpressionParser.QuestionQuestionOpContext ctx) { return visitChildren(ctx); } + + /** + * {@inheritDoc} + * + * <p>The default implementation returns the result of calling + * {@link #visitChildren} on {@code ctx}.</p> + */ + @Override public Result visitJavaLiteral(@NotNull BindingExpressionParser.JavaLiteralContext ctx) { return visitChildren(ctx); } + + /** + * {@inheritDoc} + * + * <p>The default implementation returns the result of calling + * {@link #visitChildren} on {@code ctx}.</p> + */ + @Override public Result visitExplicitGenericInvocationSuffix(@NotNull BindingExpressionParser.ExplicitGenericInvocationSuffixContext ctx) { return visitChildren(ctx); } + + /** + * {@inheritDoc} + * + * <p>The default implementation returns the result of calling + * {@link #visitChildren} on {@code ctx}.</p> + */ + @Override public Result visitIdentifier(@NotNull BindingExpressionParser.IdentifierContext ctx) { return visitChildren(ctx); } + + /** + * {@inheritDoc} + * + * <p>The default implementation returns the result of calling + * {@link #visitChildren} on {@code ctx}.</p> + */ + @Override public Result visitLiteral(@NotNull BindingExpressionParser.LiteralContext ctx) { return visitChildren(ctx); } +}
\ No newline at end of file diff --git a/tools/data-binding/grammarBuilder/src/main/java-gen/android/databinding/parser/BindingExpressionLexer.java b/tools/data-binding/grammarBuilder/src/main/java-gen/android/databinding/parser/BindingExpressionLexer.java new file mode 100644 index 0000000..47eb769 --- /dev/null +++ b/tools/data-binding/grammarBuilder/src/main/java-gen/android/databinding/parser/BindingExpressionLexer.java @@ -0,0 +1,413 @@ +// Generated from BindingExpression.g4 by ANTLR 4.4 +package android.databinding.parser; +import org.antlr.v4.runtime.Lexer; +import org.antlr.v4.runtime.CharStream; +import org.antlr.v4.runtime.Token; +import org.antlr.v4.runtime.TokenStream; +import org.antlr.v4.runtime.*; +import org.antlr.v4.runtime.atn.*; +import org.antlr.v4.runtime.dfa.DFA; +import org.antlr.v4.runtime.misc.*; + +public class BindingExpressionLexer extends Lexer { + public static final int + T__42=1, T__41=2, T__40=3, T__39=4, T__38=5, T__37=6, T__36=7, T__35=8, + T__34=9, T__33=10, T__32=11, T__31=12, T__30=13, T__29=14, T__28=15, T__27=16, + T__26=17, T__25=18, T__24=19, T__23=20, T__22=21, T__21=22, T__20=23, + T__19=24, T__18=25, T__17=26, T__16=27, T__15=28, T__14=29, T__13=30, + T__12=31, T__11=32, T__10=33, T__9=34, T__8=35, T__7=36, T__6=37, T__5=38, + T__4=39, T__3=40, T__2=41, T__1=42, T__0=43, THIS=44, IntegerLiteral=45, + FloatingPointLiteral=46, BooleanLiteral=47, CharacterLiteral=48, SingleQuoteString=49, + DoubleQuoteString=50, NullLiteral=51, Identifier=52, WS=53, ResourceReference=54, + PackageName=55, ResourceType=56; + public static String[] modeNames = { + "DEFAULT_MODE" + }; + + public static final String[] tokenNames = { + "'\\u0000'", "'\\u0001'", "'\\u0002'", "'\\u0003'", "'\\u0004'", "'\\u0005'", + "'\\u0006'", "'\\u0007'", "'\b'", "'\t'", "'\n'", "'\\u000B'", "'\f'", + "'\r'", "'\\u000E'", "'\\u000F'", "'\\u0010'", "'\\u0011'", "'\\u0012'", + "'\\u0013'", "'\\u0014'", "'\\u0015'", "'\\u0016'", "'\\u0017'", "'\\u0018'", + "'\\u0019'", "'\\u001A'", "'\\u001B'", "'\\u001C'", "'\\u001D'", "'\\u001E'", + "'\\u001F'", "' '", "'!'", "'\"'", "'#'", "'$'", "'%'", "'&'", "'''", + "'('", "')'", "'*'", "'+'", "','", "'-'", "'.'", "'/'", "'0'", "'1'", + "'2'", "'3'", "'4'", "'5'", "'6'", "'7'", "'8'" + }; + public static final String[] ruleNames = { + "T__42", "T__41", "T__40", "T__39", "T__38", "T__37", "T__36", "T__35", + "T__34", "T__33", "T__32", "T__31", "T__30", "T__29", "T__28", "T__27", + "T__26", "T__25", "T__24", "T__23", "T__22", "T__21", "T__20", "T__19", + "T__18", "T__17", "T__16", "T__15", "T__14", "T__13", "T__12", "T__11", + "T__10", "T__9", "T__8", "T__7", "T__6", "T__5", "T__4", "T__3", "T__2", + "T__1", "T__0", "THIS", "IntegerLiteral", "DecimalIntegerLiteral", "HexIntegerLiteral", + "OctalIntegerLiteral", "BinaryIntegerLiteral", "IntegerTypeSuffix", "DecimalNumeral", + "Digits", "Digit", "NonZeroDigit", "DigitOrUnderscore", "Underscores", + "HexNumeral", "HexDigits", "HexDigit", "HexDigitOrUnderscore", "OctalNumeral", + "OctalDigits", "OctalDigit", "OctalDigitOrUnderscore", "BinaryNumeral", + "BinaryDigits", "BinaryDigit", "BinaryDigitOrUnderscore", "FloatingPointLiteral", + "DecimalFloatingPointLiteral", "ExponentPart", "ExponentIndicator", "SignedInteger", + "Sign", "FloatTypeSuffix", "HexadecimalFloatingPointLiteral", "HexSignificand", + "BinaryExponent", "BinaryExponentIndicator", "BooleanLiteral", "CharacterLiteral", + "SingleCharacter", "SingleQuoteString", "DoubleQuoteString", "StringCharacters", + "StringCharacter", "SingleQuoteStringCharacter", "EscapeSequence", "OctalEscape", + "UnicodeEscape", "ZeroToThree", "NullLiteral", "Identifier", "JavaLetter", + "JavaLetterOrDigit", "WS", "ResourceReference", "PackageName", "ResourceType" + }; + + + public BindingExpressionLexer(CharStream input) { + super(input); + _interp = new LexerATNSimulator(this,_ATN); + } + + @Override + public String getGrammarFileName() { return "BindingExpression.g4"; } + + @Override + public String[] getTokenNames() { return tokenNames; } + + @Override + public String[] getRuleNames() { return ruleNames; } + + @Override + public String getSerializedATN() { return _serializedATN; } + + @Override + public String[] getModeNames() { return modeNames; } + + @Override + public boolean sempred(RuleContext _localctx, int ruleIndex, int predIndex) { + switch (ruleIndex) { + case 93 : return JavaLetter_sempred(_localctx, predIndex); + + case 94 : return JavaLetterOrDigit_sempred(_localctx, predIndex); + } + return true; + } + private boolean JavaLetterOrDigit_sempred(RuleContext _localctx, int predIndex) { + switch (predIndex) { + case 2: return Character.isJavaIdentifierPart(_input.LA(-1)); + + case 3: return Character.isJavaIdentifierPart(Character.toCodePoint((char)_input.LA(-2), (char)_input.LA(-1))); + } + return true; + } + private boolean JavaLetter_sempred(RuleContext _localctx, int predIndex) { + switch (predIndex) { + case 0: return Character.isJavaIdentifierStart(_input.LA(-1)); + + case 1: return Character.isJavaIdentifierStart(Character.toCodePoint((char)_input.LA(-2), (char)_input.LA(-1))); + } + return true; + } + + public static final String _serializedATN = + "\3\uaf6f\u8320\u479d\ub75c\u4880\u1605\u191c\uab37\2:\u0358\b\1\4\2\t"+ + "\2\4\3\t\3\4\4\t\4\4\5\t\5\4\6\t\6\4\7\t\7\4\b\t\b\4\t\t\t\4\n\t\n\4\13"+ + "\t\13\4\f\t\f\4\r\t\r\4\16\t\16\4\17\t\17\4\20\t\20\4\21\t\21\4\22\t\22"+ + "\4\23\t\23\4\24\t\24\4\25\t\25\4\26\t\26\4\27\t\27\4\30\t\30\4\31\t\31"+ + "\4\32\t\32\4\33\t\33\4\34\t\34\4\35\t\35\4\36\t\36\4\37\t\37\4 \t \4!"+ + "\t!\4\"\t\"\4#\t#\4$\t$\4%\t%\4&\t&\4\'\t\'\4(\t(\4)\t)\4*\t*\4+\t+\4"+ + ",\t,\4-\t-\4.\t.\4/\t/\4\60\t\60\4\61\t\61\4\62\t\62\4\63\t\63\4\64\t"+ + "\64\4\65\t\65\4\66\t\66\4\67\t\67\48\t8\49\t9\4:\t:\4;\t;\4<\t<\4=\t="+ + "\4>\t>\4?\t?\4@\t@\4A\tA\4B\tB\4C\tC\4D\tD\4E\tE\4F\tF\4G\tG\4H\tH\4I"+ + "\tI\4J\tJ\4K\tK\4L\tL\4M\tM\4N\tN\4O\tO\4P\tP\4Q\tQ\4R\tR\4S\tS\4T\tT"+ + "\4U\tU\4V\tV\4W\tW\4X\tX\4Y\tY\4Z\tZ\4[\t[\4\\\t\\\4]\t]\4^\t^\4_\t_\4"+ + "`\t`\4a\ta\4b\tb\4c\tc\4d\td\3\2\3\2\3\2\3\2\3\2\3\3\3\3\3\3\3\3\3\4\3"+ + "\4\3\5\3\5\3\5\3\5\3\5\3\5\3\6\3\6\3\7\3\7\3\7\3\7\3\7\3\7\3\7\3\7\3\b"+ + "\3\b\3\t\3\t\3\n\3\n\3\13\3\13\3\f\3\f\3\r\3\r\3\16\3\16\3\17\3\17\3\17"+ + "\3\17\3\20\3\20\3\20\3\21\3\21\3\21\3\22\3\22\3\23\3\23\3\23\3\24\3\24"+ + "\3\24\3\24\3\24\3\25\3\25\3\25\3\25\3\25\3\25\3\25\3\26\3\26\3\26\3\26"+ + "\3\26\3\26\3\26\3\26\3\27\3\27\3\27\3\27\3\27\3\27\3\30\3\30\3\30\3\31"+ + "\3\31\3\31\3\31\3\31\3\32\3\32\3\33\3\33\3\34\3\34\3\34\3\34\3\34\3\35"+ + "\3\35\3\36\3\36\3\37\3\37\3 \3 \3!\3!\3!\3\"\3\"\3\"\3#\3#\3$\3$\3$\3"+ + "%\3%\3%\3&\3&\3\'\3\'\3(\3(\3(\3)\3)\3)\3)\3)\3)\3*\3*\3+\3+\3+\3+\3+"+ + "\3+\3+\3+\3+\3+\3+\3,\3,\3-\3-\3-\3-\3-\3.\3.\3.\3.\5.\u0168\n.\3/\3/"+ + "\5/\u016c\n/\3\60\3\60\5\60\u0170\n\60\3\61\3\61\5\61\u0174\n\61\3\62"+ + "\3\62\5\62\u0178\n\62\3\63\3\63\3\64\3\64\3\64\5\64\u017f\n\64\3\64\3"+ + "\64\3\64\5\64\u0184\n\64\5\64\u0186\n\64\3\65\3\65\7\65\u018a\n\65\f\65"+ + "\16\65\u018d\13\65\3\65\5\65\u0190\n\65\3\66\3\66\5\66\u0194\n\66\3\67"+ + "\3\67\38\38\58\u019a\n8\39\69\u019d\n9\r9\169\u019e\3:\3:\3:\3:\3;\3;"+ + "\7;\u01a7\n;\f;\16;\u01aa\13;\3;\5;\u01ad\n;\3<\3<\3=\3=\5=\u01b3\n=\3"+ + ">\3>\5>\u01b7\n>\3>\3>\3?\3?\7?\u01bd\n?\f?\16?\u01c0\13?\3?\5?\u01c3"+ + "\n?\3@\3@\3A\3A\5A\u01c9\nA\3B\3B\3B\3B\3C\3C\7C\u01d1\nC\fC\16C\u01d4"+ + "\13C\3C\5C\u01d7\nC\3D\3D\3E\3E\5E\u01dd\nE\3F\3F\5F\u01e1\nF\3G\3G\3"+ + "G\5G\u01e6\nG\3G\5G\u01e9\nG\3G\5G\u01ec\nG\3G\3G\3G\5G\u01f1\nG\3G\5"+ + "G\u01f4\nG\3G\3G\3G\5G\u01f9\nG\3G\3G\3G\5G\u01fe\nG\3H\3H\3H\3I\3I\3"+ + "J\5J\u0206\nJ\3J\3J\3K\3K\3L\3L\3M\3M\3M\5M\u0211\nM\3N\3N\5N\u0215\n"+ + "N\3N\3N\3N\5N\u021a\nN\3N\3N\5N\u021e\nN\3O\3O\3O\3P\3P\3Q\3Q\3Q\3Q\3"+ + "Q\3Q\3Q\3Q\3Q\5Q\u022e\nQ\3R\3R\3R\3R\3R\3R\3R\3R\5R\u0238\nR\3S\3S\3"+ + "T\3T\7T\u023e\nT\fT\16T\u0241\13T\3T\3T\3U\3U\5U\u0247\nU\3U\3U\3V\6V"+ + "\u024c\nV\rV\16V\u024d\3W\3W\5W\u0252\nW\3X\3X\5X\u0256\nX\3Y\3Y\3Y\3"+ + "Y\5Y\u025c\nY\3Z\3Z\3Z\3Z\3Z\3Z\3Z\3Z\3Z\3Z\3Z\5Z\u0269\nZ\3[\3[\3[\3"+ + "[\3[\3[\3[\3\\\3\\\3]\3]\3]\3]\3]\3^\3^\7^\u027b\n^\f^\16^\u027e\13^\3"+ + "_\3_\3_\3_\3_\3_\5_\u0286\n_\3`\3`\3`\3`\3`\3`\5`\u028e\n`\3a\6a\u0291"+ + "\na\ra\16a\u0292\3a\3a\3b\3b\3b\3b\5b\u029b\nb\3b\3b\3b\3b\3c\3c\3c\3"+ + "c\3c\3c\3c\3c\5c\u02a9\nc\3d\3d\3d\3d\3d\3d\3d\3d\3d\3d\3d\3d\3d\3d\3"+ + "d\3d\3d\3d\3d\3d\3d\3d\3d\3d\3d\3d\3d\3d\3d\3d\3d\3d\3d\3d\3d\3d\3d\3"+ + "d\3d\3d\3d\3d\3d\3d\3d\3d\3d\3d\3d\3d\3d\3d\3d\3d\3d\3d\3d\3d\3d\3d\3"+ + "d\3d\3d\3d\3d\3d\3d\3d\3d\3d\3d\3d\3d\3d\3d\3d\3d\3d\3d\3d\3d\3d\3d\3"+ + "d\3d\3d\3d\3d\3d\3d\3d\3d\3d\3d\3d\3d\3d\3d\3d\3d\3d\3d\3d\3d\3d\3d\3"+ + "d\3d\3d\3d\3d\3d\3d\3d\3d\3d\3d\3d\3d\3d\3d\3d\3d\3d\3d\3d\3d\3d\3d\3"+ + "d\3d\3d\3d\3d\3d\3d\3d\3d\3d\3d\3d\3d\3d\3d\3d\3d\3d\3d\3d\3d\3d\3d\3"+ + "d\3d\3d\3d\3d\3d\3d\3d\3d\3d\3d\3d\3d\3d\3d\3d\3d\3d\3d\3d\5d\u0357\n"+ + "d\2\2\2e\3\2\3\5\2\4\7\2\5\t\2\6\13\2\7\r\2\b\17\2\t\21\2\n\23\2\13\25"+ + "\2\f\27\2\r\31\2\16\33\2\17\35\2\20\37\2\21!\2\22#\2\23%\2\24\'\2\25)"+ + "\2\26+\2\27-\2\30/\2\31\61\2\32\63\2\33\65\2\34\67\2\359\2\36;\2\37=\2"+ + " ?\2!A\2\"C\2#E\2$G\2%I\2&K\2\'M\2(O\2)Q\2*S\2+U\2,W\2-Y\2.[\2/]\2\2_"+ + "\2\2a\2\2c\2\2e\2\2g\2\2i\2\2k\2\2m\2\2o\2\2q\2\2s\2\2u\2\2w\2\2y\2\2"+ + "{\2\2}\2\2\177\2\2\u0081\2\2\u0083\2\2\u0085\2\2\u0087\2\2\u0089\2\2\u008b"+ + "\2\60\u008d\2\2\u008f\2\2\u0091\2\2\u0093\2\2\u0095\2\2\u0097\2\2\u0099"+ + "\2\2\u009b\2\2\u009d\2\2\u009f\2\2\u00a1\2\61\u00a3\2\62\u00a5\2\2\u00a7"+ + "\2\63\u00a9\2\64\u00ab\2\2\u00ad\2\2\u00af\2\2\u00b1\2\2\u00b3\2\2\u00b5"+ + "\2\2\u00b7\2\2\u00b9\2\65\u00bb\2\66\u00bd\2\2\u00bf\2\2\u00c1\2\67\u00c3"+ + "\28\u00c5\29\u00c7\2:\3\2\30\4\2NNnn\3\2\63;\4\2ZZzz\5\2\62;CHch\3\2\62"+ + "9\4\2DDdd\3\2\62\63\4\2GGgg\4\2--//\6\2FFHHffhh\4\2RRrr\4\2))^^\4\2$$"+ + "^^\4\2^^bb\13\2$$))^^bbddhhppttvv\3\2\62\65\6\2&&C\\aac|\4\2\2\u0101\ud802"+ + "\udc01\3\2\ud802\udc01\3\2\udc02\ue001\7\2&&\62;C\\aac|\5\2\13\f\16\17"+ + "\"\"\u037b\2\3\3\2\2\2\2\5\3\2\2\2\2\7\3\2\2\2\2\t\3\2\2\2\2\13\3\2\2"+ + "\2\2\r\3\2\2\2\2\17\3\2\2\2\2\21\3\2\2\2\2\23\3\2\2\2\2\25\3\2\2\2\2\27"+ + "\3\2\2\2\2\31\3\2\2\2\2\33\3\2\2\2\2\35\3\2\2\2\2\37\3\2\2\2\2!\3\2\2"+ + "\2\2#\3\2\2\2\2%\3\2\2\2\2\'\3\2\2\2\2)\3\2\2\2\2+\3\2\2\2\2-\3\2\2\2"+ + "\2/\3\2\2\2\2\61\3\2\2\2\2\63\3\2\2\2\2\65\3\2\2\2\2\67\3\2\2\2\29\3\2"+ + "\2\2\2;\3\2\2\2\2=\3\2\2\2\2?\3\2\2\2\2A\3\2\2\2\2C\3\2\2\2\2E\3\2\2\2"+ + "\2G\3\2\2\2\2I\3\2\2\2\2K\3\2\2\2\2M\3\2\2\2\2O\3\2\2\2\2Q\3\2\2\2\2S"+ + "\3\2\2\2\2U\3\2\2\2\2W\3\2\2\2\2Y\3\2\2\2\2[\3\2\2\2\2\u008b\3\2\2\2\2"+ + "\u00a1\3\2\2\2\2\u00a3\3\2\2\2\2\u00a7\3\2\2\2\2\u00a9\3\2\2\2\2\u00b9"+ + "\3\2\2\2\2\u00bb\3\2\2\2\2\u00c1\3\2\2\2\2\u00c3\3\2\2\2\2\u00c5\3\2\2"+ + "\2\2\u00c7\3\2\2\2\3\u00c9\3\2\2\2\5\u00ce\3\2\2\2\7\u00d2\3\2\2\2\t\u00d4"+ + "\3\2\2\2\13\u00da\3\2\2\2\r\u00dc\3\2\2\2\17\u00e4\3\2\2\2\21\u00e6\3"+ + "\2\2\2\23\u00e8\3\2\2\2\25\u00ea\3\2\2\2\27\u00ec\3\2\2\2\31\u00ee\3\2"+ + "\2\2\33\u00f0\3\2\2\2\35\u00f2\3\2\2\2\37\u00f6\3\2\2\2!\u00f9\3\2\2\2"+ + "#\u00fc\3\2\2\2%\u00fe\3\2\2\2\'\u0101\3\2\2\2)\u0106\3\2\2\2+\u010d\3"+ + "\2\2\2-\u0115\3\2\2\2/\u011b\3\2\2\2\61\u011e\3\2\2\2\63\u0123\3\2\2\2"+ + "\65\u0125\3\2\2\2\67\u0127\3\2\2\29\u012c\3\2\2\2;\u012e\3\2\2\2=\u0130"+ + "\3\2\2\2?\u0132\3\2\2\2A\u0134\3\2\2\2C\u0137\3\2\2\2E\u013a\3\2\2\2G"+ + "\u013c\3\2\2\2I\u013f\3\2\2\2K\u0142\3\2\2\2M\u0144\3\2\2\2O\u0146\3\2"+ + "\2\2Q\u0149\3\2\2\2S\u014f\3\2\2\2U\u0151\3\2\2\2W\u015c\3\2\2\2Y\u015e"+ + "\3\2\2\2[\u0167\3\2\2\2]\u0169\3\2\2\2_\u016d\3\2\2\2a\u0171\3\2\2\2c"+ + "\u0175\3\2\2\2e\u0179\3\2\2\2g\u0185\3\2\2\2i\u0187\3\2\2\2k\u0193\3\2"+ + "\2\2m\u0195\3\2\2\2o\u0199\3\2\2\2q\u019c\3\2\2\2s\u01a0\3\2\2\2u\u01a4"+ + "\3\2\2\2w\u01ae\3\2\2\2y\u01b2\3\2\2\2{\u01b4\3\2\2\2}\u01ba\3\2\2\2\177"+ + "\u01c4\3\2\2\2\u0081\u01c8\3\2\2\2\u0083\u01ca\3\2\2\2\u0085\u01ce\3\2"+ + "\2\2\u0087\u01d8\3\2\2\2\u0089\u01dc\3\2\2\2\u008b\u01e0\3\2\2\2\u008d"+ + "\u01fd\3\2\2\2\u008f\u01ff\3\2\2\2\u0091\u0202\3\2\2\2\u0093\u0205\3\2"+ + "\2\2\u0095\u0209\3\2\2\2\u0097\u020b\3\2\2\2\u0099\u020d\3\2\2\2\u009b"+ + "\u021d\3\2\2\2\u009d\u021f\3\2\2\2\u009f\u0222\3\2\2\2\u00a1\u022d\3\2"+ + "\2\2\u00a3\u0237\3\2\2\2\u00a5\u0239\3\2\2\2\u00a7\u023b\3\2\2\2\u00a9"+ + "\u0244\3\2\2\2\u00ab\u024b\3\2\2\2\u00ad\u0251\3\2\2\2\u00af\u0255\3\2"+ + "\2\2\u00b1\u025b\3\2\2\2\u00b3\u0268\3\2\2\2\u00b5\u026a\3\2\2\2\u00b7"+ + "\u0271\3\2\2\2\u00b9\u0273\3\2\2\2\u00bb\u0278\3\2\2\2\u00bd\u0285\3\2"+ + "\2\2\u00bf\u028d\3\2\2\2\u00c1\u0290\3\2\2\2\u00c3\u0296\3\2\2\2\u00c5"+ + "\u02a8\3\2\2\2\u00c7\u0356\3\2\2\2\u00c9\u00ca\7n\2\2\u00ca\u00cb\7q\2"+ + "\2\u00cb\u00cc\7p\2\2\u00cc\u00cd\7i\2\2\u00cd\4\3\2\2\2\u00ce\u00cf\7"+ + "@\2\2\u00cf\u00d0\7@\2\2\u00d0\u00d1\7@\2\2\u00d1\6\3\2\2\2\u00d2\u00d3"+ + "\7_\2\2\u00d3\b\3\2\2\2\u00d4\u00d5\7u\2\2\u00d5\u00d6\7j\2\2\u00d6\u00d7"+ + "\7q\2\2\u00d7\u00d8\7t\2\2\u00d8\u00d9\7v\2\2\u00d9\n\3\2\2\2\u00da\u00db"+ + "\7(\2\2\u00db\f\3\2\2\2\u00dc\u00dd\7f\2\2\u00dd\u00de\7g\2\2\u00de\u00df"+ + "\7h\2\2\u00df\u00e0\7c\2\2\u00e0\u00e1\7w\2\2\u00e1\u00e2\7n\2\2\u00e2"+ + "\u00e3\7v\2\2\u00e3\16\3\2\2\2\u00e4\u00e5\7.\2\2\u00e5\20\3\2\2\2\u00e6"+ + "\u00e7\7,\2\2\u00e7\22\3\2\2\2\u00e8\u00e9\7]\2\2\u00e9\24\3\2\2\2\u00ea"+ + "\u00eb\7/\2\2\u00eb\26\3\2\2\2\u00ec\u00ed\7*\2\2\u00ed\30\3\2\2\2\u00ee"+ + "\u00ef\7<\2\2\u00ef\32\3\2\2\2\u00f0\u00f1\7>\2\2\u00f1\34\3\2\2\2\u00f2"+ + "\u00f3\7k\2\2\u00f3\u00f4\7p\2\2\u00f4\u00f5\7v\2\2\u00f5\36\3\2\2\2\u00f6"+ + "\u00f7\7#\2\2\u00f7\u00f8\7?\2\2\u00f8 \3\2\2\2\u00f9\u00fa\7>\2\2\u00fa"+ + "\u00fb\7?\2\2\u00fb\"\3\2\2\2\u00fc\u00fd\7A\2\2\u00fd$\3\2\2\2\u00fe"+ + "\u00ff\7>\2\2\u00ff\u0100\7>\2\2\u0100&\3\2\2\2\u0101\u0102\7x\2\2\u0102"+ + "\u0103\7q\2\2\u0103\u0104\7k\2\2\u0104\u0105\7f\2\2\u0105(\3\2\2\2\u0106"+ + "\u0107\7f\2\2\u0107\u0108\7q\2\2\u0108\u0109\7w\2\2\u0109\u010a\7d\2\2"+ + "\u010a\u010b\7n\2\2\u010b\u010c\7g\2\2\u010c*\3\2\2\2\u010d\u010e\7d\2"+ + "\2\u010e\u010f\7q\2\2\u010f\u0110\7q\2\2\u0110\u0111\7n\2\2\u0111\u0112"+ + "\7g\2\2\u0112\u0113\7c\2\2\u0113\u0114\7p\2\2\u0114,\3\2\2\2\u0115\u0116"+ + "\7h\2\2\u0116\u0117\7n\2\2\u0117\u0118\7q\2\2\u0118\u0119\7c\2\2\u0119"+ + "\u011a\7v\2\2\u011a.\3\2\2\2\u011b\u011c\7@\2\2\u011c\u011d\7@\2\2\u011d"+ + "\60\3\2\2\2\u011e\u011f\7e\2\2\u011f\u0120\7j\2\2\u0120\u0121\7c\2\2\u0121"+ + "\u0122\7t\2\2\u0122\62\3\2\2\2\u0123\u0124\7\'\2\2\u0124\64\3\2\2\2\u0125"+ + "\u0126\7`\2\2\u0126\66\3\2\2\2\u0127\u0128\7d\2\2\u0128\u0129\7{\2\2\u0129"+ + "\u012a\7v\2\2\u012a\u012b\7g\2\2\u012b8\3\2\2\2\u012c\u012d\7+\2\2\u012d"+ + ":\3\2\2\2\u012e\u012f\7\60\2\2\u012f<\3\2\2\2\u0130\u0131\7-\2\2\u0131"+ + ">\3\2\2\2\u0132\u0133\7?\2\2\u0133@\3\2\2\2\u0134\u0135\7(\2\2\u0135\u0136"+ + "\7(\2\2\u0136B\3\2\2\2\u0137\u0138\7~\2\2\u0138\u0139\7~\2\2\u0139D\3"+ + "\2\2\2\u013a\u013b\7@\2\2\u013bF\3\2\2\2\u013c\u013d\7A\2\2\u013d\u013e"+ + "\7A\2\2\u013eH\3\2\2\2\u013f\u0140\7?\2\2\u0140\u0141\7?\2\2\u0141J\3"+ + "\2\2\2\u0142\u0143\7\61\2\2\u0143L\3\2\2\2\u0144\u0145\7\u0080\2\2\u0145"+ + "N\3\2\2\2\u0146\u0147\7@\2\2\u0147\u0148\7?\2\2\u0148P\3\2\2\2\u0149\u014a"+ + "\7e\2\2\u014a\u014b\7n\2\2\u014b\u014c\7c\2\2\u014c\u014d\7u\2\2\u014d"+ + "\u014e\7u\2\2\u014eR\3\2\2\2\u014f\u0150\7~\2\2\u0150T\3\2\2\2\u0151\u0152"+ + "\7k\2\2\u0152\u0153\7p\2\2\u0153\u0154\7u\2\2\u0154\u0155\7v\2\2\u0155"+ + "\u0156\7c\2\2\u0156\u0157\7p\2\2\u0157\u0158\7e\2\2\u0158\u0159\7g\2\2"+ + "\u0159\u015a\7q\2\2\u015a\u015b\7h\2\2\u015bV\3\2\2\2\u015c\u015d\7#\2"+ + "\2\u015dX\3\2\2\2\u015e\u015f\7v\2\2\u015f\u0160\7j\2\2\u0160\u0161\7"+ + "k\2\2\u0161\u0162\7u\2\2\u0162Z\3\2\2\2\u0163\u0168\5]/\2\u0164\u0168"+ + "\5_\60\2\u0165\u0168\5a\61\2\u0166\u0168\5c\62\2\u0167\u0163\3\2\2\2\u0167"+ + "\u0164\3\2\2\2\u0167\u0165\3\2\2\2\u0167\u0166\3\2\2\2\u0168\\\3\2\2\2"+ + "\u0169\u016b\5g\64\2\u016a\u016c\5e\63\2\u016b\u016a\3\2\2\2\u016b\u016c"+ + "\3\2\2\2\u016c^\3\2\2\2\u016d\u016f\5s:\2\u016e\u0170\5e\63\2\u016f\u016e"+ + "\3\2\2\2\u016f\u0170\3\2\2\2\u0170`\3\2\2\2\u0171\u0173\5{>\2\u0172\u0174"+ + "\5e\63\2\u0173\u0172\3\2\2\2\u0173\u0174\3\2\2\2\u0174b\3\2\2\2\u0175"+ + "\u0177\5\u0083B\2\u0176\u0178\5e\63\2\u0177\u0176\3\2\2\2\u0177\u0178"+ + "\3\2\2\2\u0178d\3\2\2\2\u0179\u017a\t\2\2\2\u017af\3\2\2\2\u017b\u0186"+ + "\7\62\2\2\u017c\u0183\5m\67\2\u017d\u017f\5i\65\2\u017e\u017d\3\2\2\2"+ + "\u017e\u017f\3\2\2\2\u017f\u0184\3\2\2\2\u0180\u0181\5q9\2\u0181\u0182"+ + "\5i\65\2\u0182\u0184\3\2\2\2\u0183\u017e\3\2\2\2\u0183\u0180\3\2\2\2\u0184"+ + "\u0186\3\2\2\2\u0185\u017b\3\2\2\2\u0185\u017c\3\2\2\2\u0186h\3\2\2\2"+ + "\u0187\u018f\5k\66\2\u0188\u018a\5o8\2\u0189\u0188\3\2\2\2\u018a\u018d"+ + "\3\2\2\2\u018b\u0189\3\2\2\2\u018b\u018c\3\2\2\2\u018c\u018e\3\2\2\2\u018d"+ + "\u018b\3\2\2\2\u018e\u0190\5k\66\2\u018f\u018b\3\2\2\2\u018f\u0190\3\2"+ + "\2\2\u0190j\3\2\2\2\u0191\u0194\7\62\2\2\u0192\u0194\5m\67\2\u0193\u0191"+ + "\3\2\2\2\u0193\u0192\3\2\2\2\u0194l\3\2\2\2\u0195\u0196\t\3\2\2\u0196"+ + "n\3\2\2\2\u0197\u019a\5k\66\2\u0198\u019a\7a\2\2\u0199\u0197\3\2\2\2\u0199"+ + "\u0198\3\2\2\2\u019ap\3\2\2\2\u019b\u019d\7a\2\2\u019c\u019b\3\2\2\2\u019d"+ + "\u019e\3\2\2\2\u019e\u019c\3\2\2\2\u019e\u019f\3\2\2\2\u019fr\3\2\2\2"+ + "\u01a0\u01a1\7\62\2\2\u01a1\u01a2\t\4\2\2\u01a2\u01a3\5u;\2\u01a3t\3\2"+ + "\2\2\u01a4\u01ac\5w<\2\u01a5\u01a7\5y=\2\u01a6\u01a5\3\2\2\2\u01a7\u01aa"+ + "\3\2\2\2\u01a8\u01a6\3\2\2\2\u01a8\u01a9\3\2\2\2\u01a9\u01ab\3\2\2\2\u01aa"+ + "\u01a8\3\2\2\2\u01ab\u01ad\5w<\2\u01ac\u01a8\3\2\2\2\u01ac\u01ad\3\2\2"+ + "\2\u01adv\3\2\2\2\u01ae\u01af\t\5\2\2\u01afx\3\2\2\2\u01b0\u01b3\5w<\2"+ + "\u01b1\u01b3\7a\2\2\u01b2\u01b0\3\2\2\2\u01b2\u01b1\3\2\2\2\u01b3z\3\2"+ + "\2\2\u01b4\u01b6\7\62\2\2\u01b5\u01b7\5q9\2\u01b6\u01b5\3\2\2\2\u01b6"+ + "\u01b7\3\2\2\2\u01b7\u01b8\3\2\2\2\u01b8\u01b9\5}?\2\u01b9|\3\2\2\2\u01ba"+ + "\u01c2\5\177@\2\u01bb\u01bd\5\u0081A\2\u01bc\u01bb\3\2\2\2\u01bd\u01c0"+ + "\3\2\2\2\u01be\u01bc\3\2\2\2\u01be\u01bf\3\2\2\2\u01bf\u01c1\3\2\2\2\u01c0"+ + "\u01be\3\2\2\2\u01c1\u01c3\5\177@\2\u01c2\u01be\3\2\2\2\u01c2\u01c3\3"+ + "\2\2\2\u01c3~\3\2\2\2\u01c4\u01c5\t\6\2\2\u01c5\u0080\3\2\2\2\u01c6\u01c9"+ + "\5\177@\2\u01c7\u01c9\7a\2\2\u01c8\u01c6\3\2\2\2\u01c8\u01c7\3\2\2\2\u01c9"+ + "\u0082\3\2\2\2\u01ca\u01cb\7\62\2\2\u01cb\u01cc\t\7\2\2\u01cc\u01cd\5"+ + "\u0085C\2\u01cd\u0084\3\2\2\2\u01ce\u01d6\5\u0087D\2\u01cf\u01d1\5\u0089"+ + "E\2\u01d0\u01cf\3\2\2\2\u01d1\u01d4\3\2\2\2\u01d2\u01d0\3\2\2\2\u01d2"+ + "\u01d3\3\2\2\2\u01d3\u01d5\3\2\2\2\u01d4\u01d2\3\2\2\2\u01d5\u01d7\5\u0087"+ + "D\2\u01d6\u01d2\3\2\2\2\u01d6\u01d7\3\2\2\2\u01d7\u0086\3\2\2\2\u01d8"+ + "\u01d9\t\b\2\2\u01d9\u0088\3\2\2\2\u01da\u01dd\5\u0087D\2\u01db\u01dd"+ + "\7a\2\2\u01dc\u01da\3\2\2\2\u01dc\u01db\3\2\2\2\u01dd\u008a\3\2\2\2\u01de"+ + "\u01e1\5\u008dG\2\u01df\u01e1\5\u0099M\2\u01e0\u01de\3\2\2\2\u01e0\u01df"+ + "\3\2\2\2\u01e1\u008c\3\2\2\2\u01e2\u01e3\5i\65\2\u01e3\u01e5\7\60\2\2"+ + "\u01e4\u01e6\5i\65\2\u01e5\u01e4\3\2\2\2\u01e5\u01e6\3\2\2\2\u01e6\u01e8"+ + "\3\2\2\2\u01e7\u01e9\5\u008fH\2\u01e8\u01e7\3\2\2\2\u01e8\u01e9\3\2\2"+ + "\2\u01e9\u01eb\3\2\2\2\u01ea\u01ec\5\u0097L\2\u01eb\u01ea\3\2\2\2\u01eb"+ + "\u01ec\3\2\2\2\u01ec\u01fe\3\2\2\2\u01ed\u01ee\7\60\2\2\u01ee\u01f0\5"+ + "i\65\2\u01ef\u01f1\5\u008fH\2\u01f0\u01ef\3\2\2\2\u01f0\u01f1\3\2\2\2"+ + "\u01f1\u01f3\3\2\2\2\u01f2\u01f4\5\u0097L\2\u01f3\u01f2\3\2\2\2\u01f3"+ + "\u01f4\3\2\2\2\u01f4\u01fe\3\2\2\2\u01f5\u01f6\5i\65\2\u01f6\u01f8\5\u008f"+ + "H\2\u01f7\u01f9\5\u0097L\2\u01f8\u01f7\3\2\2\2\u01f8\u01f9\3\2\2\2\u01f9"+ + "\u01fe\3\2\2\2\u01fa\u01fb\5i\65\2\u01fb\u01fc\5\u0097L\2\u01fc\u01fe"+ + "\3\2\2\2\u01fd\u01e2\3\2\2\2\u01fd\u01ed\3\2\2\2\u01fd\u01f5\3\2\2\2\u01fd"+ + "\u01fa\3\2\2\2\u01fe\u008e\3\2\2\2\u01ff\u0200\5\u0091I\2\u0200\u0201"+ + "\5\u0093J\2\u0201\u0090\3\2\2\2\u0202\u0203\t\t\2\2\u0203\u0092\3\2\2"+ + "\2\u0204\u0206\5\u0095K\2\u0205\u0204\3\2\2\2\u0205\u0206\3\2\2\2\u0206"+ + "\u0207\3\2\2\2\u0207\u0208\5i\65\2\u0208\u0094\3\2\2\2\u0209\u020a\t\n"+ + "\2\2\u020a\u0096\3\2\2\2\u020b\u020c\t\13\2\2\u020c\u0098\3\2\2\2\u020d"+ + "\u020e\5\u009bN\2\u020e\u0210\5\u009dO\2\u020f\u0211\5\u0097L\2\u0210"+ + "\u020f\3\2\2\2\u0210\u0211\3\2\2\2\u0211\u009a\3\2\2\2\u0212\u0214\5s"+ + ":\2\u0213\u0215\7\60\2\2\u0214\u0213\3\2\2\2\u0214\u0215\3\2\2\2\u0215"+ + "\u021e\3\2\2\2\u0216\u0217\7\62\2\2\u0217\u0219\t\4\2\2\u0218\u021a\5"+ + "u;\2\u0219\u0218\3\2\2\2\u0219\u021a\3\2\2\2\u021a\u021b\3\2\2\2\u021b"+ + "\u021c\7\60\2\2\u021c\u021e\5u;\2\u021d\u0212\3\2\2\2\u021d\u0216\3\2"+ + "\2\2\u021e\u009c\3\2\2\2\u021f\u0220\5\u009fP\2\u0220\u0221\5\u0093J\2"+ + "\u0221\u009e\3\2\2\2\u0222\u0223\t\f\2\2\u0223\u00a0\3\2\2\2\u0224\u0225"+ + "\7v\2\2\u0225\u0226\7t\2\2\u0226\u0227\7w\2\2\u0227\u022e\7g\2\2\u0228"+ + "\u0229\7h\2\2\u0229\u022a\7c\2\2\u022a\u022b\7n\2\2\u022b\u022c\7u\2\2"+ + "\u022c\u022e\7g\2\2\u022d\u0224\3\2\2\2\u022d\u0228\3\2\2\2\u022e\u00a2"+ + "\3\2\2\2\u022f\u0230\7)\2\2\u0230\u0231\5\u00a5S\2\u0231\u0232\7)\2\2"+ + "\u0232\u0238\3\2\2\2\u0233\u0234\7)\2\2\u0234\u0235\5\u00b1Y\2\u0235\u0236"+ + "\7)\2\2\u0236\u0238\3\2\2\2\u0237\u022f\3\2\2\2\u0237\u0233\3\2\2\2\u0238"+ + "\u00a4\3\2\2\2\u0239\u023a\n\r\2\2\u023a\u00a6\3\2\2\2\u023b\u023f\7b"+ + "\2\2\u023c\u023e\5\u00afX\2\u023d\u023c\3\2\2\2\u023e\u0241\3\2\2\2\u023f"+ + "\u023d\3\2\2\2\u023f\u0240\3\2\2\2\u0240\u0242\3\2\2\2\u0241\u023f\3\2"+ + "\2\2\u0242\u0243\7b\2\2\u0243\u00a8\3\2\2\2\u0244\u0246\7$\2\2\u0245\u0247"+ + "\5\u00abV\2\u0246\u0245\3\2\2\2\u0246\u0247\3\2\2\2\u0247\u0248\3\2\2"+ + "\2\u0248\u0249\7$\2\2\u0249\u00aa\3\2\2\2\u024a\u024c\5\u00adW\2\u024b"+ + "\u024a\3\2\2\2\u024c\u024d\3\2\2\2\u024d\u024b\3\2\2\2\u024d\u024e\3\2"+ + "\2\2\u024e\u00ac\3\2\2\2\u024f\u0252\n\16\2\2\u0250\u0252\5\u00b1Y\2\u0251"+ + "\u024f\3\2\2\2\u0251\u0250\3\2\2\2\u0252\u00ae\3\2\2\2\u0253\u0256\n\17"+ + "\2\2\u0254\u0256\5\u00b1Y\2\u0255\u0253\3\2\2\2\u0255\u0254\3\2\2\2\u0256"+ + "\u00b0\3\2\2\2\u0257\u0258\7^\2\2\u0258\u025c\t\20\2\2\u0259\u025c\5\u00b3"+ + "Z\2\u025a\u025c\5\u00b5[\2\u025b\u0257\3\2\2\2\u025b\u0259\3\2\2\2\u025b"+ + "\u025a\3\2\2\2\u025c\u00b2\3\2\2\2\u025d\u025e\7^\2\2\u025e\u0269\5\177"+ + "@\2\u025f\u0260\7^\2\2\u0260\u0261\5\177@\2\u0261\u0262\5\177@\2\u0262"+ + "\u0269\3\2\2\2\u0263\u0264\7^\2\2\u0264\u0265\5\u00b7\\\2\u0265\u0266"+ + "\5\177@\2\u0266\u0267\5\177@\2\u0267\u0269\3\2\2\2\u0268\u025d\3\2\2\2"+ + "\u0268\u025f\3\2\2\2\u0268\u0263\3\2\2\2\u0269\u00b4\3\2\2\2\u026a\u026b"+ + "\7^\2\2\u026b\u026c\7w\2\2\u026c\u026d\5w<\2\u026d\u026e\5w<\2\u026e\u026f"+ + "\5w<\2\u026f\u0270\5w<\2\u0270\u00b6\3\2\2\2\u0271\u0272\t\21\2\2\u0272"+ + "\u00b8\3\2\2\2\u0273\u0274\7p\2\2\u0274\u0275\7w\2\2\u0275\u0276\7n\2"+ + "\2\u0276\u0277\7n\2\2\u0277\u00ba\3\2\2\2\u0278\u027c\5\u00bd_\2\u0279"+ + "\u027b\5\u00bf`\2\u027a\u0279\3\2\2\2\u027b\u027e\3\2\2\2\u027c\u027a"+ + "\3\2\2\2\u027c\u027d\3\2\2\2\u027d\u00bc\3\2\2\2\u027e\u027c\3\2\2\2\u027f"+ + "\u0286\t\22\2\2\u0280\u0281\n\23\2\2\u0281\u0286\6_\2\2\u0282\u0283\t"+ + "\24\2\2\u0283\u0284\t\25\2\2\u0284\u0286\6_\3\2\u0285\u027f\3\2\2\2\u0285"+ + "\u0280\3\2\2\2\u0285\u0282\3\2\2\2\u0286\u00be\3\2\2\2\u0287\u028e\t\26"+ + "\2\2\u0288\u0289\n\23\2\2\u0289\u028e\6`\4\2\u028a\u028b\t\24\2\2\u028b"+ + "\u028c\t\25\2\2\u028c\u028e\6`\5\2\u028d\u0287\3\2\2\2\u028d\u0288\3\2"+ + "\2\2\u028d\u028a\3\2\2\2\u028e\u00c0\3\2\2\2\u028f\u0291\t\27\2\2\u0290"+ + "\u028f\3\2\2\2\u0291\u0292\3\2\2\2\u0292\u0290\3\2\2\2\u0292\u0293\3\2"+ + "\2\2\u0293\u0294\3\2\2\2\u0294\u0295\ba\2\2\u0295\u00c2\3\2\2\2\u0296"+ + "\u029a\7B\2\2\u0297\u0298\5\u00c5c\2\u0298\u0299\7<\2\2\u0299\u029b\3"+ + "\2\2\2\u029a\u0297\3\2\2\2\u029a\u029b\3\2\2\2\u029b\u029c\3\2\2\2\u029c"+ + "\u029d\5\u00c7d\2\u029d\u029e\7\61\2\2\u029e\u029f\5\u00bb^\2\u029f\u00c4"+ + "\3\2\2\2\u02a0\u02a1\7c\2\2\u02a1\u02a2\7p\2\2\u02a2\u02a3\7f\2\2\u02a3"+ + "\u02a4\7t\2\2\u02a4\u02a5\7q\2\2\u02a5\u02a6\7k\2\2\u02a6\u02a9\7f\2\2"+ + "\u02a7\u02a9\5\u00bb^\2\u02a8\u02a0\3\2\2\2\u02a8\u02a7\3\2\2\2\u02a9"+ + "\u00c6\3\2\2\2\u02aa\u02ab\7c\2\2\u02ab\u02ac\7p\2\2\u02ac\u02ad\7k\2"+ + "\2\u02ad\u0357\7o\2\2\u02ae\u02af\7c\2\2\u02af\u02b0\7p\2\2\u02b0\u02b1"+ + "\7k\2\2\u02b1\u02b2\7o\2\2\u02b2\u02b3\7c\2\2\u02b3\u02b4\7v\2\2\u02b4"+ + "\u02b5\7q\2\2\u02b5\u0357\7t\2\2\u02b6\u02b7\7d\2\2\u02b7\u02b8\7q\2\2"+ + "\u02b8\u02b9\7q\2\2\u02b9\u0357\7n\2\2\u02ba\u02bb\7e\2\2\u02bb\u02bc"+ + "\7q\2\2\u02bc\u02bd\7n\2\2\u02bd\u02be\7q\2\2\u02be\u0357\7t\2\2\u02bf"+ + "\u02c0\7e\2\2\u02c0\u02c1\7q\2\2\u02c1\u02c2\7n\2\2\u02c2\u02c3\7q\2\2"+ + "\u02c3\u02c4\7t\2\2\u02c4\u02c5\7U\2\2\u02c5\u02c6\7v\2\2\u02c6\u02c7"+ + "\7c\2\2\u02c7\u02c8\7v\2\2\u02c8\u02c9\7g\2\2\u02c9\u02ca\7N\2\2\u02ca"+ + "\u02cb\7k\2\2\u02cb\u02cc\7u\2\2\u02cc\u0357\7v\2\2\u02cd\u02ce\7f\2\2"+ + "\u02ce\u02cf\7k\2\2\u02cf\u02d0\7o\2\2\u02d0\u02d1\7g\2\2\u02d1\u0357"+ + "\7p\2\2\u02d2\u02d3\7f\2\2\u02d3\u02d4\7k\2\2\u02d4\u02d5\7o\2\2\u02d5"+ + "\u02d6\7g\2\2\u02d6\u02d7\7p\2\2\u02d7\u02d8\7Q\2\2\u02d8\u02d9\7h\2\2"+ + "\u02d9\u02da\7h\2\2\u02da\u02db\7u\2\2\u02db\u02dc\7g\2\2\u02dc\u0357"+ + "\7v\2\2\u02dd\u02de\7f\2\2\u02de\u02df\7k\2\2\u02df\u02e0\7o\2\2\u02e0"+ + "\u02e1\7g\2\2\u02e1\u02e2\7p\2\2\u02e2\u02e3\7U\2\2\u02e3\u02e4\7k\2\2"+ + "\u02e4\u02e5\7|\2\2\u02e5\u0357\7g\2\2\u02e6\u02e7\7f\2\2\u02e7\u02e8"+ + "\7t\2\2\u02e8\u02e9\7c\2\2\u02e9\u02ea\7y\2\2\u02ea\u02eb\7c\2\2\u02eb"+ + "\u02ec\7d\2\2\u02ec\u02ed\7n\2\2\u02ed\u0357\7g\2\2\u02ee\u02ef\7h\2\2"+ + "\u02ef\u02f0\7t\2\2\u02f0\u02f1\7c\2\2\u02f1\u02f2\7e\2\2\u02f2\u02f3"+ + "\7v\2\2\u02f3\u02f4\7k\2\2\u02f4\u02f5\7q\2\2\u02f5\u0357\7p\2\2\u02f6"+ + "\u02f7\7k\2\2\u02f7\u0357\7f\2\2\u02f8\u02f9\7k\2\2\u02f9\u02fa\7p\2\2"+ + "\u02fa\u02fb\7v\2\2\u02fb\u02fc\7g\2\2\u02fc\u02fd\7i\2\2\u02fd\u02fe"+ + "\7g\2\2\u02fe\u0357\7t\2\2\u02ff\u0300\7k\2\2\u0300\u0301\7p\2\2\u0301"+ + "\u0302\7v\2\2\u0302\u0303\7C\2\2\u0303\u0304\7t\2\2\u0304\u0305\7t\2\2"+ + "\u0305\u0306\7c\2\2\u0306\u0357\7{\2\2\u0307\u0308\7k\2\2\u0308\u0309"+ + "\7p\2\2\u0309\u030a\7v\2\2\u030a\u030b\7g\2\2\u030b\u030c\7t\2\2\u030c"+ + "\u030d\7r\2\2\u030d\u030e\7q\2\2\u030e\u030f\7n\2\2\u030f\u0310\7c\2\2"+ + "\u0310\u0311\7v\2\2\u0311\u0312\7q\2\2\u0312\u0357\7t\2\2\u0313\u0314"+ + "\7n\2\2\u0314\u0315\7c\2\2\u0315\u0316\7{\2\2\u0316\u0317\7q\2\2\u0317"+ + "\u0318\7w\2\2\u0318\u0357\7v\2\2\u0319\u031a\7r\2\2\u031a\u031b\7n\2\2"+ + "\u031b\u031c\7w\2\2\u031c\u031d\7t\2\2\u031d\u031e\7c\2\2\u031e\u031f"+ + "\7n\2\2\u031f\u0357\7u\2\2\u0320\u0321\7u\2\2\u0321\u0322\7v\2\2\u0322"+ + "\u0323\7c\2\2\u0323\u0324\7v\2\2\u0324\u0325\7g\2\2\u0325\u0326\7N\2\2"+ + "\u0326\u0327\7k\2\2\u0327\u0328\7u\2\2\u0328\u0329\7v\2\2\u0329\u032a"+ + "\7C\2\2\u032a\u032b\7p\2\2\u032b\u032c\7k\2\2\u032c\u032d\7o\2\2\u032d"+ + "\u032e\7c\2\2\u032e\u032f\7v\2\2\u032f\u0330\7q\2\2\u0330\u0357\7t\2\2"+ + "\u0331\u0332\7u\2\2\u0332\u0333\7v\2\2\u0333\u0334\7t\2\2\u0334\u0335"+ + "\7k\2\2\u0335\u0336\7p\2\2\u0336\u0357\7i\2\2\u0337\u0338\7u\2\2\u0338"+ + "\u0339\7v\2\2\u0339\u033a\7t\2\2\u033a\u033b\7k\2\2\u033b\u033c\7p\2\2"+ + "\u033c\u033d\7i\2\2\u033d\u033e\7C\2\2\u033e\u033f\7t\2\2\u033f\u0340"+ + "\7t\2\2\u0340\u0341\7c\2\2\u0341\u0357\7{\2\2\u0342\u0343\7v\2\2\u0343"+ + "\u0344\7t\2\2\u0344\u0345\7c\2\2\u0345\u0346\7p\2\2\u0346\u0347\7u\2\2"+ + "\u0347\u0348\7k\2\2\u0348\u0349\7v\2\2\u0349\u034a\7k\2\2\u034a\u034b"+ + "\7q\2\2\u034b\u0357\7p\2\2\u034c\u034d\7v\2\2\u034d\u034e\7{\2\2\u034e"+ + "\u034f\7r\2\2\u034f\u0350\7g\2\2\u0350\u0351\7f\2\2\u0351\u0352\7C\2\2"+ + "\u0352\u0353\7t\2\2\u0353\u0354\7t\2\2\u0354\u0355\7c\2\2\u0355\u0357"+ + "\7{\2\2\u0356\u02aa\3\2\2\2\u0356\u02ae\3\2\2\2\u0356\u02b6\3\2\2\2\u0356"+ + "\u02ba\3\2\2\2\u0356\u02bf\3\2\2\2\u0356\u02cd\3\2\2\2\u0356\u02d2\3\2"+ + "\2\2\u0356\u02dd\3\2\2\2\u0356\u02e6\3\2\2\2\u0356\u02ee\3\2\2\2\u0356"+ + "\u02f6\3\2\2\2\u0356\u02f8\3\2\2\2\u0356\u02ff\3\2\2\2\u0356\u0307\3\2"+ + "\2\2\u0356\u0313\3\2\2\2\u0356\u0319\3\2\2\2\u0356\u0320\3\2\2\2\u0356"+ + "\u0331\3\2\2\2\u0356\u0337\3\2\2\2\u0356\u0342\3\2\2\2\u0356\u034c\3\2"+ + "\2\2\u0357\u00c8\3\2\2\2\67\2\u0167\u016b\u016f\u0173\u0177\u017e\u0183"+ + "\u0185\u018b\u018f\u0193\u0199\u019e\u01a8\u01ac\u01b2\u01b6\u01be\u01c2"+ + "\u01c8\u01d2\u01d6\u01dc\u01e0\u01e5\u01e8\u01eb\u01f0\u01f3\u01f8\u01fd"+ + "\u0205\u0210\u0214\u0219\u021d\u022d\u0237\u023f\u0246\u024d\u0251\u0255"+ + "\u025b\u0268\u027c\u0285\u028d\u0292\u029a\u02a8\u0356\3\b\2\2"; + public static final ATN _ATN = + new ATNDeserializer().deserialize(_serializedATN.toCharArray()); + static { + } +}
\ No newline at end of file diff --git a/tools/data-binding/grammarBuilder/src/main/java-gen/android/databinding/parser/BindingExpressionLexer.tokens b/tools/data-binding/grammarBuilder/src/main/java-gen/android/databinding/parser/BindingExpressionLexer.tokens new file mode 100644 index 0000000..d379280 --- /dev/null +++ b/tools/data-binding/grammarBuilder/src/main/java-gen/android/databinding/parser/BindingExpressionLexer.tokens @@ -0,0 +1,101 @@ +NullLiteral=51 +T__29=14 +T__28=15 +T__27=16 +T__26=17 +T__25=18 +T__24=19 +T__23=20 +T__22=21 +CharacterLiteral=48 +T__21=22 +T__20=23 +SingleQuoteString=49 +T__9=34 +T__8=35 +Identifier=52 +T__7=36 +T__6=37 +T__5=38 +T__4=39 +T__19=24 +T__16=27 +T__15=28 +T__18=25 +T__17=26 +T__12=31 +T__11=32 +T__14=29 +T__13=30 +T__10=33 +THIS=44 +PackageName=55 +DoubleQuoteString=50 +T__42=1 +T__40=3 +T__41=2 +ResourceType=56 +T__30=13 +T__31=12 +T__32=11 +WS=53 +T__33=10 +T__34=9 +T__35=8 +T__36=7 +T__37=6 +T__38=5 +T__39=4 +T__1=42 +T__0=43 +FloatingPointLiteral=46 +T__3=40 +T__2=41 +IntegerLiteral=45 +ResourceReference=54 +BooleanLiteral=47 +'!'=43 +'instanceof'=42 +'|'=41 +'class'=40 +'>='=39 +'~'=38 +'/'=37 +'=='=36 +'??'=35 +'null'=51 +'>'=34 +'||'=33 +'this'=44 +'&&'=32 +'='=31 +'+'=30 +'.'=29 +')'=28 +'byte'=27 +'^'=26 +'%'=25 +'>>'=23 +'char'=24 +'float'=22 +'boolean'=21 +'double'=20 +'<<'=18 +'void'=19 +'?'=17 +'<='=16 +'!='=15 +'<'=13 +'int'=14 +':'=12 +'('=11 +'-'=10 +'['=9 +'*'=8 +','=7 +'default'=6 +'&'=5 +'short'=4 +']'=3 +'>>>'=2 +'long'=1 diff --git a/tools/data-binding/grammarBuilder/src/main/java-gen/android/databinding/parser/BindingExpressionListener.java b/tools/data-binding/grammarBuilder/src/main/java-gen/android/databinding/parser/BindingExpressionListener.java new file mode 100644 index 0000000..020be83 --- /dev/null +++ b/tools/data-binding/grammarBuilder/src/main/java-gen/android/databinding/parser/BindingExpressionListener.java @@ -0,0 +1,428 @@ +// Generated from BindingExpression.g4 by ANTLR 4.4 +package android.databinding.parser; +import org.antlr.v4.runtime.Token; +import org.antlr.v4.runtime.misc.NotNull; +import org.antlr.v4.runtime.tree.ParseTreeListener; + +/** + * This interface defines a complete listener for a parse tree produced by + * {@link BindingExpressionParser}. + */ +public interface BindingExpressionListener extends ParseTreeListener { + /** + * Enter a parse tree produced by {@link BindingExpressionParser#expression}. + * @param ctx the parse tree + */ + void enterExpression(@NotNull BindingExpressionParser.ExpressionContext ctx); + /** + * Exit a parse tree produced by {@link BindingExpressionParser#expression}. + * @param ctx the parse tree + */ + void exitExpression(@NotNull BindingExpressionParser.ExpressionContext ctx); + + /** + * Enter a parse tree produced by {@link BindingExpressionParser#resources}. + * @param ctx the parse tree + */ + void enterResources(@NotNull BindingExpressionParser.ResourcesContext ctx); + /** + * Exit a parse tree produced by {@link BindingExpressionParser#resources}. + * @param ctx the parse tree + */ + void exitResources(@NotNull BindingExpressionParser.ResourcesContext ctx); + + /** + * Enter a parse tree produced by the {@code BracketOp} + * labeled alternative in {@link BindingExpressionParser#expression}. + * @param ctx the parse tree + */ + void enterBracketOp(@NotNull BindingExpressionParser.BracketOpContext ctx); + /** + * Exit a parse tree produced by the {@code BracketOp} + * labeled alternative in {@link BindingExpressionParser#expression}. + * @param ctx the parse tree + */ + void exitBracketOp(@NotNull BindingExpressionParser.BracketOpContext ctx); + + /** + * Enter a parse tree produced by the {@code UnaryOp} + * labeled alternative in {@link BindingExpressionParser#expression}. + * @param ctx the parse tree + */ + void enterUnaryOp(@NotNull BindingExpressionParser.UnaryOpContext ctx); + /** + * Exit a parse tree produced by the {@code UnaryOp} + * labeled alternative in {@link BindingExpressionParser#expression}. + * @param ctx the parse tree + */ + void exitUnaryOp(@NotNull BindingExpressionParser.UnaryOpContext ctx); + + /** + * Enter a parse tree produced by the {@code CastOp} + * labeled alternative in {@link BindingExpressionParser#expression}. + * @param ctx the parse tree + */ + void enterCastOp(@NotNull BindingExpressionParser.CastOpContext ctx); + /** + * Exit a parse tree produced by the {@code CastOp} + * labeled alternative in {@link BindingExpressionParser#expression}. + * @param ctx the parse tree + */ + void exitCastOp(@NotNull BindingExpressionParser.CastOpContext ctx); + + /** + * Enter a parse tree produced by {@link BindingExpressionParser#resourceParameters}. + * @param ctx the parse tree + */ + void enterResourceParameters(@NotNull BindingExpressionParser.ResourceParametersContext ctx); + /** + * Exit a parse tree produced by {@link BindingExpressionParser#resourceParameters}. + * @param ctx the parse tree + */ + void exitResourceParameters(@NotNull BindingExpressionParser.ResourceParametersContext ctx); + + /** + * Enter a parse tree produced by the {@code AndOrOp} + * labeled alternative in {@link BindingExpressionParser#expression}. + * @param ctx the parse tree + */ + void enterAndOrOp(@NotNull BindingExpressionParser.AndOrOpContext ctx); + /** + * Exit a parse tree produced by the {@code AndOrOp} + * labeled alternative in {@link BindingExpressionParser#expression}. + * @param ctx the parse tree + */ + void exitAndOrOp(@NotNull BindingExpressionParser.AndOrOpContext ctx); + + /** + * Enter a parse tree produced by the {@code MethodInvocation} + * labeled alternative in {@link BindingExpressionParser#expression}. + * @param ctx the parse tree + */ + void enterMethodInvocation(@NotNull BindingExpressionParser.MethodInvocationContext ctx); + /** + * Exit a parse tree produced by the {@code MethodInvocation} + * labeled alternative in {@link BindingExpressionParser#expression}. + * @param ctx the parse tree + */ + void exitMethodInvocation(@NotNull BindingExpressionParser.MethodInvocationContext ctx); + + /** + * Enter a parse tree produced by {@link BindingExpressionParser#expressionList}. + * @param ctx the parse tree + */ + void enterExpressionList(@NotNull BindingExpressionParser.ExpressionListContext ctx); + /** + * Exit a parse tree produced by {@link BindingExpressionParser#expressionList}. + * @param ctx the parse tree + */ + void exitExpressionList(@NotNull BindingExpressionParser.ExpressionListContext ctx); + + /** + * Enter a parse tree produced by {@link BindingExpressionParser#classOrInterfaceType}. + * @param ctx the parse tree + */ + void enterClassOrInterfaceType(@NotNull BindingExpressionParser.ClassOrInterfaceTypeContext ctx); + /** + * Exit a parse tree produced by {@link BindingExpressionParser#classOrInterfaceType}. + * @param ctx the parse tree + */ + void exitClassOrInterfaceType(@NotNull BindingExpressionParser.ClassOrInterfaceTypeContext ctx); + + /** + * Enter a parse tree produced by {@link BindingExpressionParser#stringLiteral}. + * @param ctx the parse tree + */ + void enterStringLiteral(@NotNull BindingExpressionParser.StringLiteralContext ctx); + /** + * Exit a parse tree produced by {@link BindingExpressionParser#stringLiteral}. + * @param ctx the parse tree + */ + void exitStringLiteral(@NotNull BindingExpressionParser.StringLiteralContext ctx); + + /** + * Enter a parse tree produced by the {@code Primary} + * labeled alternative in {@link BindingExpressionParser#expression}. + * @param ctx the parse tree + */ + void enterPrimary(@NotNull BindingExpressionParser.PrimaryContext ctx); + /** + * Exit a parse tree produced by the {@code Primary} + * labeled alternative in {@link BindingExpressionParser#expression}. + * @param ctx the parse tree + */ + void exitPrimary(@NotNull BindingExpressionParser.PrimaryContext ctx); + + /** + * Enter a parse tree produced by {@link BindingExpressionParser#type}. + * @param ctx the parse tree + */ + void enterType(@NotNull BindingExpressionParser.TypeContext ctx); + /** + * Exit a parse tree produced by {@link BindingExpressionParser#type}. + * @param ctx the parse tree + */ + void exitType(@NotNull BindingExpressionParser.TypeContext ctx); + + /** + * Enter a parse tree produced by {@link BindingExpressionParser#bindingSyntax}. + * @param ctx the parse tree + */ + void enterBindingSyntax(@NotNull BindingExpressionParser.BindingSyntaxContext ctx); + /** + * Exit a parse tree produced by {@link BindingExpressionParser#bindingSyntax}. + * @param ctx the parse tree + */ + void exitBindingSyntax(@NotNull BindingExpressionParser.BindingSyntaxContext ctx); + + /** + * Enter a parse tree produced by the {@code ComparisonOp} + * labeled alternative in {@link BindingExpressionParser#expression}. + * @param ctx the parse tree + */ + void enterComparisonOp(@NotNull BindingExpressionParser.ComparisonOpContext ctx); + /** + * Exit a parse tree produced by the {@code ComparisonOp} + * labeled alternative in {@link BindingExpressionParser#expression}. + * @param ctx the parse tree + */ + void exitComparisonOp(@NotNull BindingExpressionParser.ComparisonOpContext ctx); + + /** + * Enter a parse tree produced by the {@code TernaryOp} + * labeled alternative in {@link BindingExpressionParser#expression}. + * @param ctx the parse tree + */ + void enterTernaryOp(@NotNull BindingExpressionParser.TernaryOpContext ctx); + /** + * Exit a parse tree produced by the {@code TernaryOp} + * labeled alternative in {@link BindingExpressionParser#expression}. + * @param ctx the parse tree + */ + void exitTernaryOp(@NotNull BindingExpressionParser.TernaryOpContext ctx); + + /** + * Enter a parse tree produced by {@link BindingExpressionParser#constantValue}. + * @param ctx the parse tree + */ + void enterConstantValue(@NotNull BindingExpressionParser.ConstantValueContext ctx); + /** + * Exit a parse tree produced by {@link BindingExpressionParser#constantValue}. + * @param ctx the parse tree + */ + void exitConstantValue(@NotNull BindingExpressionParser.ConstantValueContext ctx); + + /** + * Enter a parse tree produced by the {@code DotOp} + * labeled alternative in {@link BindingExpressionParser#expression}. + * @param ctx the parse tree + */ + void enterDotOp(@NotNull BindingExpressionParser.DotOpContext ctx); + /** + * Exit a parse tree produced by the {@code DotOp} + * labeled alternative in {@link BindingExpressionParser#expression}. + * @param ctx the parse tree + */ + void exitDotOp(@NotNull BindingExpressionParser.DotOpContext ctx); + + /** + * Enter a parse tree produced by {@link BindingExpressionParser#defaults}. + * @param ctx the parse tree + */ + void enterDefaults(@NotNull BindingExpressionParser.DefaultsContext ctx); + /** + * Exit a parse tree produced by {@link BindingExpressionParser#defaults}. + * @param ctx the parse tree + */ + void exitDefaults(@NotNull BindingExpressionParser.DefaultsContext ctx); + + /** + * Enter a parse tree produced by the {@code BitShiftOp} + * labeled alternative in {@link BindingExpressionParser#expression}. + * @param ctx the parse tree + */ + void enterBitShiftOp(@NotNull BindingExpressionParser.BitShiftOpContext ctx); + /** + * Exit a parse tree produced by the {@code BitShiftOp} + * labeled alternative in {@link BindingExpressionParser#expression}. + * @param ctx the parse tree + */ + void exitBitShiftOp(@NotNull BindingExpressionParser.BitShiftOpContext ctx); + + /** + * Enter a parse tree produced by the {@code InstanceOfOp} + * labeled alternative in {@link BindingExpressionParser#expression}. + * @param ctx the parse tree + */ + void enterInstanceOfOp(@NotNull BindingExpressionParser.InstanceOfOpContext ctx); + /** + * Exit a parse tree produced by the {@code InstanceOfOp} + * labeled alternative in {@link BindingExpressionParser#expression}. + * @param ctx the parse tree + */ + void exitInstanceOfOp(@NotNull BindingExpressionParser.InstanceOfOpContext ctx); + + /** + * Enter a parse tree produced by the {@code BinaryOp} + * labeled alternative in {@link BindingExpressionParser#expression}. + * @param ctx the parse tree + */ + void enterBinaryOp(@NotNull BindingExpressionParser.BinaryOpContext ctx); + /** + * Exit a parse tree produced by the {@code BinaryOp} + * labeled alternative in {@link BindingExpressionParser#expression}. + * @param ctx the parse tree + */ + void exitBinaryOp(@NotNull BindingExpressionParser.BinaryOpContext ctx); + + /** + * Enter a parse tree produced by {@link BindingExpressionParser#explicitGenericInvocation}. + * @param ctx the parse tree + */ + void enterExplicitGenericInvocation(@NotNull BindingExpressionParser.ExplicitGenericInvocationContext ctx); + /** + * Exit a parse tree produced by {@link BindingExpressionParser#explicitGenericInvocation}. + * @param ctx the parse tree + */ + void exitExplicitGenericInvocation(@NotNull BindingExpressionParser.ExplicitGenericInvocationContext ctx); + + /** + * Enter a parse tree produced by the {@code Resource} + * labeled alternative in {@link BindingExpressionParser#expression}. + * @param ctx the parse tree + */ + void enterResource(@NotNull BindingExpressionParser.ResourceContext ctx); + /** + * Exit a parse tree produced by the {@code Resource} + * labeled alternative in {@link BindingExpressionParser#expression}. + * @param ctx the parse tree + */ + void exitResource(@NotNull BindingExpressionParser.ResourceContext ctx); + + /** + * Enter a parse tree produced by {@link BindingExpressionParser#typeArguments}. + * @param ctx the parse tree + */ + void enterTypeArguments(@NotNull BindingExpressionParser.TypeArgumentsContext ctx); + /** + * Exit a parse tree produced by {@link BindingExpressionParser#typeArguments}. + * @param ctx the parse tree + */ + void exitTypeArguments(@NotNull BindingExpressionParser.TypeArgumentsContext ctx); + + /** + * Enter a parse tree produced by the {@code Grouping} + * labeled alternative in {@link BindingExpressionParser#expression}. + * @param ctx the parse tree + */ + void enterGrouping(@NotNull BindingExpressionParser.GroupingContext ctx); + /** + * Exit a parse tree produced by the {@code Grouping} + * labeled alternative in {@link BindingExpressionParser#expression}. + * @param ctx the parse tree + */ + void exitGrouping(@NotNull BindingExpressionParser.GroupingContext ctx); + + /** + * Enter a parse tree produced by the {@code MathOp} + * labeled alternative in {@link BindingExpressionParser#expression}. + * @param ctx the parse tree + */ + void enterMathOp(@NotNull BindingExpressionParser.MathOpContext ctx); + /** + * Exit a parse tree produced by the {@code MathOp} + * labeled alternative in {@link BindingExpressionParser#expression}. + * @param ctx the parse tree + */ + void exitMathOp(@NotNull BindingExpressionParser.MathOpContext ctx); + + /** + * Enter a parse tree produced by {@link BindingExpressionParser#classExtraction}. + * @param ctx the parse tree + */ + void enterClassExtraction(@NotNull BindingExpressionParser.ClassExtractionContext ctx); + /** + * Exit a parse tree produced by {@link BindingExpressionParser#classExtraction}. + * @param ctx the parse tree + */ + void exitClassExtraction(@NotNull BindingExpressionParser.ClassExtractionContext ctx); + + /** + * Enter a parse tree produced by {@link BindingExpressionParser#arguments}. + * @param ctx the parse tree + */ + void enterArguments(@NotNull BindingExpressionParser.ArgumentsContext ctx); + /** + * Exit a parse tree produced by {@link BindingExpressionParser#arguments}. + * @param ctx the parse tree + */ + void exitArguments(@NotNull BindingExpressionParser.ArgumentsContext ctx); + + /** + * Enter a parse tree produced by {@link BindingExpressionParser#primitiveType}. + * @param ctx the parse tree + */ + void enterPrimitiveType(@NotNull BindingExpressionParser.PrimitiveTypeContext ctx); + /** + * Exit a parse tree produced by {@link BindingExpressionParser#primitiveType}. + * @param ctx the parse tree + */ + void exitPrimitiveType(@NotNull BindingExpressionParser.PrimitiveTypeContext ctx); + + /** + * Enter a parse tree produced by the {@code QuestionQuestionOp} + * labeled alternative in {@link BindingExpressionParser#expression}. + * @param ctx the parse tree + */ + void enterQuestionQuestionOp(@NotNull BindingExpressionParser.QuestionQuestionOpContext ctx); + /** + * Exit a parse tree produced by the {@code QuestionQuestionOp} + * labeled alternative in {@link BindingExpressionParser#expression}. + * @param ctx the parse tree + */ + void exitQuestionQuestionOp(@NotNull BindingExpressionParser.QuestionQuestionOpContext ctx); + + /** + * Enter a parse tree produced by {@link BindingExpressionParser#javaLiteral}. + * @param ctx the parse tree + */ + void enterJavaLiteral(@NotNull BindingExpressionParser.JavaLiteralContext ctx); + /** + * Exit a parse tree produced by {@link BindingExpressionParser#javaLiteral}. + * @param ctx the parse tree + */ + void exitJavaLiteral(@NotNull BindingExpressionParser.JavaLiteralContext ctx); + + /** + * Enter a parse tree produced by {@link BindingExpressionParser#explicitGenericInvocationSuffix}. + * @param ctx the parse tree + */ + void enterExplicitGenericInvocationSuffix(@NotNull BindingExpressionParser.ExplicitGenericInvocationSuffixContext ctx); + /** + * Exit a parse tree produced by {@link BindingExpressionParser#explicitGenericInvocationSuffix}. + * @param ctx the parse tree + */ + void exitExplicitGenericInvocationSuffix(@NotNull BindingExpressionParser.ExplicitGenericInvocationSuffixContext ctx); + + /** + * Enter a parse tree produced by {@link BindingExpressionParser#identifier}. + * @param ctx the parse tree + */ + void enterIdentifier(@NotNull BindingExpressionParser.IdentifierContext ctx); + /** + * Exit a parse tree produced by {@link BindingExpressionParser#identifier}. + * @param ctx the parse tree + */ + void exitIdentifier(@NotNull BindingExpressionParser.IdentifierContext ctx); + + /** + * Enter a parse tree produced by {@link BindingExpressionParser#literal}. + * @param ctx the parse tree + */ + void enterLiteral(@NotNull BindingExpressionParser.LiteralContext ctx); + /** + * Exit a parse tree produced by {@link BindingExpressionParser#literal}. + * @param ctx the parse tree + */ + void exitLiteral(@NotNull BindingExpressionParser.LiteralContext ctx); +}
\ No newline at end of file diff --git a/tools/data-binding/grammarBuilder/src/main/java-gen/android/databinding/parser/BindingExpressionParser.java b/tools/data-binding/grammarBuilder/src/main/java-gen/android/databinding/parser/BindingExpressionParser.java new file mode 100644 index 0000000..0d41591 --- /dev/null +++ b/tools/data-binding/grammarBuilder/src/main/java-gen/android/databinding/parser/BindingExpressionParser.java @@ -0,0 +1,2005 @@ +// Generated from BindingExpression.g4 by ANTLR 4.4 +package android.databinding.parser; +import org.antlr.v4.runtime.atn.*; +import org.antlr.v4.runtime.dfa.DFA; +import org.antlr.v4.runtime.*; +import org.antlr.v4.runtime.misc.*; +import org.antlr.v4.runtime.tree.*; +import java.util.List; +import java.util.Iterator; +import java.util.ArrayList; + +public class BindingExpressionParser extends Parser { + public static final int + T__42=1, T__41=2, T__40=3, T__39=4, T__38=5, T__37=6, T__36=7, T__35=8, + T__34=9, T__33=10, T__32=11, T__31=12, T__30=13, T__29=14, T__28=15, T__27=16, + T__26=17, T__25=18, T__24=19, T__23=20, T__22=21, T__21=22, T__20=23, + T__19=24, T__18=25, T__17=26, T__16=27, T__15=28, T__14=29, T__13=30, + T__12=31, T__11=32, T__10=33, T__9=34, T__8=35, T__7=36, T__6=37, T__5=38, + T__4=39, T__3=40, T__2=41, T__1=42, T__0=43, THIS=44, IntegerLiteral=45, + FloatingPointLiteral=46, BooleanLiteral=47, CharacterLiteral=48, SingleQuoteString=49, + DoubleQuoteString=50, NullLiteral=51, Identifier=52, WS=53, ResourceReference=54, + PackageName=55, ResourceType=56; + public static final String[] tokenNames = { + "<INVALID>", "'long'", "'>>>'", "']'", "'short'", "'&'", "'default'", + "','", "'*'", "'['", "'-'", "'('", "':'", "'<'", "'int'", "'!='", "'<='", + "'?'", "'<<'", "'void'", "'double'", "'boolean'", "'float'", "'>>'", "'char'", + "'%'", "'^'", "'byte'", "')'", "'.'", "'+'", "'='", "'&&'", "'||'", "'>'", + "'??'", "'=='", "'/'", "'~'", "'>='", "'class'", "'|'", "'instanceof'", + "'!'", "'this'", "IntegerLiteral", "FloatingPointLiteral", "BooleanLiteral", + "CharacterLiteral", "SingleQuoteString", "DoubleQuoteString", "'null'", + "Identifier", "WS", "ResourceReference", "PackageName", "ResourceType" + }; + public static final int + RULE_bindingSyntax = 0, RULE_defaults = 1, RULE_constantValue = 2, RULE_expression = 3, + RULE_classExtraction = 4, RULE_expressionList = 5, RULE_literal = 6, RULE_identifier = 7, + RULE_javaLiteral = 8, RULE_stringLiteral = 9, RULE_explicitGenericInvocation = 10, + RULE_typeArguments = 11, RULE_type = 12, RULE_explicitGenericInvocationSuffix = 13, + RULE_arguments = 14, RULE_classOrInterfaceType = 15, RULE_primitiveType = 16, + RULE_resources = 17, RULE_resourceParameters = 18; + public static final String[] ruleNames = { + "bindingSyntax", "defaults", "constantValue", "expression", "classExtraction", + "expressionList", "literal", "identifier", "javaLiteral", "stringLiteral", + "explicitGenericInvocation", "typeArguments", "type", "explicitGenericInvocationSuffix", + "arguments", "classOrInterfaceType", "primitiveType", "resources", "resourceParameters" + }; + + @Override + public String getGrammarFileName() { return "BindingExpression.g4"; } + + @Override + public String[] getTokenNames() { return tokenNames; } + + @Override + public String[] getRuleNames() { return ruleNames; } + + @Override + public String getSerializedATN() { return _serializedATN; } + + public BindingExpressionParser(TokenStream input) { + super(input); + _interp = new ParserATNSimulator(this,_ATN); + } + public static class BindingSyntaxContext extends ParserRuleContext { + public DefaultsContext defaults() { + return getRuleContext(DefaultsContext.class,0); + } + public ExpressionContext expression() { + return getRuleContext(ExpressionContext.class,0); + } + public BindingSyntaxContext(ParserRuleContext parent, int invokingState) { + super(parent, invokingState); + } + @Override public int getRuleIndex() { return RULE_bindingSyntax; } + @Override + public void enterRule(ParseTreeListener listener) { + if ( listener instanceof BindingExpressionListener ) ((BindingExpressionListener)listener).enterBindingSyntax(this); + } + @Override + public void exitRule(ParseTreeListener listener) { + if ( listener instanceof BindingExpressionListener ) ((BindingExpressionListener)listener).exitBindingSyntax(this); + } + @Override + public <Result> Result accept(ParseTreeVisitor<? extends Result> visitor) { + if ( visitor instanceof BindingExpressionVisitor<?> ) return ((BindingExpressionVisitor<? extends Result>)visitor).visitBindingSyntax(this); + else return visitor.visitChildren(this); + } + } + + @RuleVersion(0) + public final BindingSyntaxContext bindingSyntax() throws RecognitionException { + BindingSyntaxContext _localctx = new BindingSyntaxContext(_ctx, getState()); + enterRule(_localctx, 0, RULE_bindingSyntax); + int _la; + try { + enterOuterAlt(_localctx, 1); + { + setState(38); expression(0); + setState(40); + _la = _input.LA(1); + if (_la==T__36) { + { + setState(39); defaults(); + } + } + + } + } + catch (RecognitionException re) { + _localctx.exception = re; + _errHandler.reportError(this, re); + _errHandler.recover(this, re); + } + finally { + exitRule(); + } + return _localctx; + } + + public static class DefaultsContext extends ParserRuleContext { + public ConstantValueContext constantValue() { + return getRuleContext(ConstantValueContext.class,0); + } + public DefaultsContext(ParserRuleContext parent, int invokingState) { + super(parent, invokingState); + } + @Override public int getRuleIndex() { return RULE_defaults; } + @Override + public void enterRule(ParseTreeListener listener) { + if ( listener instanceof BindingExpressionListener ) ((BindingExpressionListener)listener).enterDefaults(this); + } + @Override + public void exitRule(ParseTreeListener listener) { + if ( listener instanceof BindingExpressionListener ) ((BindingExpressionListener)listener).exitDefaults(this); + } + @Override + public <Result> Result accept(ParseTreeVisitor<? extends Result> visitor) { + if ( visitor instanceof BindingExpressionVisitor<?> ) return ((BindingExpressionVisitor<? extends Result>)visitor).visitDefaults(this); + else return visitor.visitChildren(this); + } + } + + @RuleVersion(0) + public final DefaultsContext defaults() throws RecognitionException { + DefaultsContext _localctx = new DefaultsContext(_ctx, getState()); + enterRule(_localctx, 2, RULE_defaults); + try { + enterOuterAlt(_localctx, 1); + { + setState(42); match(T__36); + setState(43); match(T__37); + setState(44); match(T__12); + setState(45); constantValue(); + } + } + catch (RecognitionException re) { + _localctx.exception = re; + _errHandler.reportError(this, re); + _errHandler.recover(this, re); + } + finally { + exitRule(); + } + return _localctx; + } + + public static class ConstantValueContext extends ParserRuleContext { + public LiteralContext literal() { + return getRuleContext(LiteralContext.class,0); + } + public IdentifierContext identifier() { + return getRuleContext(IdentifierContext.class,0); + } + public TerminalNode ResourceReference() { return getToken(BindingExpressionParser.ResourceReference, 0); } + public ConstantValueContext(ParserRuleContext parent, int invokingState) { + super(parent, invokingState); + } + @Override public int getRuleIndex() { return RULE_constantValue; } + @Override + public void enterRule(ParseTreeListener listener) { + if ( listener instanceof BindingExpressionListener ) ((BindingExpressionListener)listener).enterConstantValue(this); + } + @Override + public void exitRule(ParseTreeListener listener) { + if ( listener instanceof BindingExpressionListener ) ((BindingExpressionListener)listener).exitConstantValue(this); + } + @Override + public <Result> Result accept(ParseTreeVisitor<? extends Result> visitor) { + if ( visitor instanceof BindingExpressionVisitor<?> ) return ((BindingExpressionVisitor<? extends Result>)visitor).visitConstantValue(this); + else return visitor.visitChildren(this); + } + } + + @RuleVersion(0) + public final ConstantValueContext constantValue() throws RecognitionException { + ConstantValueContext _localctx = new ConstantValueContext(_ctx, getState()); + enterRule(_localctx, 4, RULE_constantValue); + try { + setState(50); + switch (_input.LA(1)) { + case IntegerLiteral: + case FloatingPointLiteral: + case BooleanLiteral: + case CharacterLiteral: + case SingleQuoteString: + case DoubleQuoteString: + case NullLiteral: + enterOuterAlt(_localctx, 1); + { + setState(47); literal(); + } + break; + case ResourceReference: + enterOuterAlt(_localctx, 2); + { + setState(48); match(ResourceReference); + } + break; + case Identifier: + enterOuterAlt(_localctx, 3); + { + setState(49); identifier(); + } + break; + default: + throw new NoViableAltException(this); + } + } + catch (RecognitionException re) { + _localctx.exception = re; + _errHandler.reportError(this, re); + _errHandler.recover(this, re); + } + finally { + exitRule(); + } + return _localctx; + } + + public static class ExpressionContext extends ParserRuleContext { + public ExpressionContext(ParserRuleContext parent, int invokingState) { + super(parent, invokingState); + } + @Override public int getRuleIndex() { return RULE_expression; } + + public ExpressionContext() { } + public void copyFrom(ExpressionContext ctx) { + super.copyFrom(ctx); + } + } + public static class BracketOpContext extends ExpressionContext { + public ExpressionContext expression(int i) { + return getRuleContext(ExpressionContext.class,i); + } + public List<? extends ExpressionContext> expression() { + return getRuleContexts(ExpressionContext.class); + } + public BracketOpContext(ExpressionContext ctx) { copyFrom(ctx); } + @Override + public void enterRule(ParseTreeListener listener) { + if ( listener instanceof BindingExpressionListener ) ((BindingExpressionListener)listener).enterBracketOp(this); + } + @Override + public void exitRule(ParseTreeListener listener) { + if ( listener instanceof BindingExpressionListener ) ((BindingExpressionListener)listener).exitBracketOp(this); + } + @Override + public <Result> Result accept(ParseTreeVisitor<? extends Result> visitor) { + if ( visitor instanceof BindingExpressionVisitor<?> ) return ((BindingExpressionVisitor<? extends Result>)visitor).visitBracketOp(this); + else return visitor.visitChildren(this); + } + } + public static class ResourceContext extends ExpressionContext { + public ResourcesContext resources() { + return getRuleContext(ResourcesContext.class,0); + } + public ResourceContext(ExpressionContext ctx) { copyFrom(ctx); } + @Override + public void enterRule(ParseTreeListener listener) { + if ( listener instanceof BindingExpressionListener ) ((BindingExpressionListener)listener).enterResource(this); + } + @Override + public void exitRule(ParseTreeListener listener) { + if ( listener instanceof BindingExpressionListener ) ((BindingExpressionListener)listener).exitResource(this); + } + @Override + public <Result> Result accept(ParseTreeVisitor<? extends Result> visitor) { + if ( visitor instanceof BindingExpressionVisitor<?> ) return ((BindingExpressionVisitor<? extends Result>)visitor).visitResource(this); + else return visitor.visitChildren(this); + } + } + public static class CastOpContext extends ExpressionContext { + public TypeContext type() { + return getRuleContext(TypeContext.class,0); + } + public ExpressionContext expression() { + return getRuleContext(ExpressionContext.class,0); + } + public CastOpContext(ExpressionContext ctx) { copyFrom(ctx); } + @Override + public void enterRule(ParseTreeListener listener) { + if ( listener instanceof BindingExpressionListener ) ((BindingExpressionListener)listener).enterCastOp(this); + } + @Override + public void exitRule(ParseTreeListener listener) { + if ( listener instanceof BindingExpressionListener ) ((BindingExpressionListener)listener).exitCastOp(this); + } + @Override + public <Result> Result accept(ParseTreeVisitor<? extends Result> visitor) { + if ( visitor instanceof BindingExpressionVisitor<?> ) return ((BindingExpressionVisitor<? extends Result>)visitor).visitCastOp(this); + else return visitor.visitChildren(this); + } + } + public static class UnaryOpContext extends ExpressionContext { + public Token op; + public ExpressionContext expression() { + return getRuleContext(ExpressionContext.class,0); + } + public UnaryOpContext(ExpressionContext ctx) { copyFrom(ctx); } + @Override + public void enterRule(ParseTreeListener listener) { + if ( listener instanceof BindingExpressionListener ) ((BindingExpressionListener)listener).enterUnaryOp(this); + } + @Override + public void exitRule(ParseTreeListener listener) { + if ( listener instanceof BindingExpressionListener ) ((BindingExpressionListener)listener).exitUnaryOp(this); + } + @Override + public <Result> Result accept(ParseTreeVisitor<? extends Result> visitor) { + if ( visitor instanceof BindingExpressionVisitor<?> ) return ((BindingExpressionVisitor<? extends Result>)visitor).visitUnaryOp(this); + else return visitor.visitChildren(this); + } + } + public static class AndOrOpContext extends ExpressionContext { + public ExpressionContext left; + public Token op; + public ExpressionContext right; + public ExpressionContext expression(int i) { + return getRuleContext(ExpressionContext.class,i); + } + public List<? extends ExpressionContext> expression() { + return getRuleContexts(ExpressionContext.class); + } + public AndOrOpContext(ExpressionContext ctx) { copyFrom(ctx); } + @Override + public void enterRule(ParseTreeListener listener) { + if ( listener instanceof BindingExpressionListener ) ((BindingExpressionListener)listener).enterAndOrOp(this); + } + @Override + public void exitRule(ParseTreeListener listener) { + if ( listener instanceof BindingExpressionListener ) ((BindingExpressionListener)listener).exitAndOrOp(this); + } + @Override + public <Result> Result accept(ParseTreeVisitor<? extends Result> visitor) { + if ( visitor instanceof BindingExpressionVisitor<?> ) return ((BindingExpressionVisitor<? extends Result>)visitor).visitAndOrOp(this); + else return visitor.visitChildren(this); + } + } + public static class MethodInvocationContext extends ExpressionContext { + public ExpressionContext target; + public Token methodName; + public ExpressionListContext args; + public ExpressionListContext expressionList() { + return getRuleContext(ExpressionListContext.class,0); + } + public TerminalNode Identifier() { return getToken(BindingExpressionParser.Identifier, 0); } + public ExpressionContext expression() { + return getRuleContext(ExpressionContext.class,0); + } + public MethodInvocationContext(ExpressionContext ctx) { copyFrom(ctx); } + @Override + public void enterRule(ParseTreeListener listener) { + if ( listener instanceof BindingExpressionListener ) ((BindingExpressionListener)listener).enterMethodInvocation(this); + } + @Override + public void exitRule(ParseTreeListener listener) { + if ( listener instanceof BindingExpressionListener ) ((BindingExpressionListener)listener).exitMethodInvocation(this); + } + @Override + public <Result> Result accept(ParseTreeVisitor<? extends Result> visitor) { + if ( visitor instanceof BindingExpressionVisitor<?> ) return ((BindingExpressionVisitor<? extends Result>)visitor).visitMethodInvocation(this); + else return visitor.visitChildren(this); + } + } + public static class PrimaryContext extends ExpressionContext { + public ClassExtractionContext classExtraction() { + return getRuleContext(ClassExtractionContext.class,0); + } + public LiteralContext literal() { + return getRuleContext(LiteralContext.class,0); + } + public IdentifierContext identifier() { + return getRuleContext(IdentifierContext.class,0); + } + public PrimaryContext(ExpressionContext ctx) { copyFrom(ctx); } + @Override + public void enterRule(ParseTreeListener listener) { + if ( listener instanceof BindingExpressionListener ) ((BindingExpressionListener)listener).enterPrimary(this); + } + @Override + public void exitRule(ParseTreeListener listener) { + if ( listener instanceof BindingExpressionListener ) ((BindingExpressionListener)listener).exitPrimary(this); + } + @Override + public <Result> Result accept(ParseTreeVisitor<? extends Result> visitor) { + if ( visitor instanceof BindingExpressionVisitor<?> ) return ((BindingExpressionVisitor<? extends Result>)visitor).visitPrimary(this); + else return visitor.visitChildren(this); + } + } + public static class GroupingContext extends ExpressionContext { + public ExpressionContext expression() { + return getRuleContext(ExpressionContext.class,0); + } + public GroupingContext(ExpressionContext ctx) { copyFrom(ctx); } + @Override + public void enterRule(ParseTreeListener listener) { + if ( listener instanceof BindingExpressionListener ) ((BindingExpressionListener)listener).enterGrouping(this); + } + @Override + public void exitRule(ParseTreeListener listener) { + if ( listener instanceof BindingExpressionListener ) ((BindingExpressionListener)listener).exitGrouping(this); + } + @Override + public <Result> Result accept(ParseTreeVisitor<? extends Result> visitor) { + if ( visitor instanceof BindingExpressionVisitor<?> ) return ((BindingExpressionVisitor<? extends Result>)visitor).visitGrouping(this); + else return visitor.visitChildren(this); + } + } + public static class TernaryOpContext extends ExpressionContext { + public ExpressionContext left; + public Token op; + public ExpressionContext iftrue; + public ExpressionContext iffalse; + public ExpressionContext expression(int i) { + return getRuleContext(ExpressionContext.class,i); + } + public List<? extends ExpressionContext> expression() { + return getRuleContexts(ExpressionContext.class); + } + public TernaryOpContext(ExpressionContext ctx) { copyFrom(ctx); } + @Override + public void enterRule(ParseTreeListener listener) { + if ( listener instanceof BindingExpressionListener ) ((BindingExpressionListener)listener).enterTernaryOp(this); + } + @Override + public void exitRule(ParseTreeListener listener) { + if ( listener instanceof BindingExpressionListener ) ((BindingExpressionListener)listener).exitTernaryOp(this); + } + @Override + public <Result> Result accept(ParseTreeVisitor<? extends Result> visitor) { + if ( visitor instanceof BindingExpressionVisitor<?> ) return ((BindingExpressionVisitor<? extends Result>)visitor).visitTernaryOp(this); + else return visitor.visitChildren(this); + } + } + public static class ComparisonOpContext extends ExpressionContext { + public ExpressionContext left; + public Token op; + public ExpressionContext right; + public ExpressionContext expression(int i) { + return getRuleContext(ExpressionContext.class,i); + } + public List<? extends ExpressionContext> expression() { + return getRuleContexts(ExpressionContext.class); + } + public ComparisonOpContext(ExpressionContext ctx) { copyFrom(ctx); } + @Override + public void enterRule(ParseTreeListener listener) { + if ( listener instanceof BindingExpressionListener ) ((BindingExpressionListener)listener).enterComparisonOp(this); + } + @Override + public void exitRule(ParseTreeListener listener) { + if ( listener instanceof BindingExpressionListener ) ((BindingExpressionListener)listener).exitComparisonOp(this); + } + @Override + public <Result> Result accept(ParseTreeVisitor<? extends Result> visitor) { + if ( visitor instanceof BindingExpressionVisitor<?> ) return ((BindingExpressionVisitor<? extends Result>)visitor).visitComparisonOp(this); + else return visitor.visitChildren(this); + } + } + public static class DotOpContext extends ExpressionContext { + public TerminalNode Identifier() { return getToken(BindingExpressionParser.Identifier, 0); } + public ExpressionContext expression() { + return getRuleContext(ExpressionContext.class,0); + } + public DotOpContext(ExpressionContext ctx) { copyFrom(ctx); } + @Override + public void enterRule(ParseTreeListener listener) { + if ( listener instanceof BindingExpressionListener ) ((BindingExpressionListener)listener).enterDotOp(this); + } + @Override + public void exitRule(ParseTreeListener listener) { + if ( listener instanceof BindingExpressionListener ) ((BindingExpressionListener)listener).exitDotOp(this); + } + @Override + public <Result> Result accept(ParseTreeVisitor<? extends Result> visitor) { + if ( visitor instanceof BindingExpressionVisitor<?> ) return ((BindingExpressionVisitor<? extends Result>)visitor).visitDotOp(this); + else return visitor.visitChildren(this); + } + } + public static class MathOpContext extends ExpressionContext { + public ExpressionContext left; + public Token op; + public ExpressionContext right; + public ExpressionContext expression(int i) { + return getRuleContext(ExpressionContext.class,i); + } + public List<? extends ExpressionContext> expression() { + return getRuleContexts(ExpressionContext.class); + } + public MathOpContext(ExpressionContext ctx) { copyFrom(ctx); } + @Override + public void enterRule(ParseTreeListener listener) { + if ( listener instanceof BindingExpressionListener ) ((BindingExpressionListener)listener).enterMathOp(this); + } + @Override + public void exitRule(ParseTreeListener listener) { + if ( listener instanceof BindingExpressionListener ) ((BindingExpressionListener)listener).exitMathOp(this); + } + @Override + public <Result> Result accept(ParseTreeVisitor<? extends Result> visitor) { + if ( visitor instanceof BindingExpressionVisitor<?> ) return ((BindingExpressionVisitor<? extends Result>)visitor).visitMathOp(this); + else return visitor.visitChildren(this); + } + } + public static class BitShiftOpContext extends ExpressionContext { + public ExpressionContext left; + public Token op; + public ExpressionContext right; + public ExpressionContext expression(int i) { + return getRuleContext(ExpressionContext.class,i); + } + public List<? extends ExpressionContext> expression() { + return getRuleContexts(ExpressionContext.class); + } + public BitShiftOpContext(ExpressionContext ctx) { copyFrom(ctx); } + @Override + public void enterRule(ParseTreeListener listener) { + if ( listener instanceof BindingExpressionListener ) ((BindingExpressionListener)listener).enterBitShiftOp(this); + } + @Override + public void exitRule(ParseTreeListener listener) { + if ( listener instanceof BindingExpressionListener ) ((BindingExpressionListener)listener).exitBitShiftOp(this); + } + @Override + public <Result> Result accept(ParseTreeVisitor<? extends Result> visitor) { + if ( visitor instanceof BindingExpressionVisitor<?> ) return ((BindingExpressionVisitor<? extends Result>)visitor).visitBitShiftOp(this); + else return visitor.visitChildren(this); + } + } + public static class QuestionQuestionOpContext extends ExpressionContext { + public ExpressionContext left; + public Token op; + public ExpressionContext right; + public ExpressionContext expression(int i) { + return getRuleContext(ExpressionContext.class,i); + } + public List<? extends ExpressionContext> expression() { + return getRuleContexts(ExpressionContext.class); + } + public QuestionQuestionOpContext(ExpressionContext ctx) { copyFrom(ctx); } + @Override + public void enterRule(ParseTreeListener listener) { + if ( listener instanceof BindingExpressionListener ) ((BindingExpressionListener)listener).enterQuestionQuestionOp(this); + } + @Override + public void exitRule(ParseTreeListener listener) { + if ( listener instanceof BindingExpressionListener ) ((BindingExpressionListener)listener).exitQuestionQuestionOp(this); + } + @Override + public <Result> Result accept(ParseTreeVisitor<? extends Result> visitor) { + if ( visitor instanceof BindingExpressionVisitor<?> ) return ((BindingExpressionVisitor<? extends Result>)visitor).visitQuestionQuestionOp(this); + else return visitor.visitChildren(this); + } + } + public static class InstanceOfOpContext extends ExpressionContext { + public TypeContext type() { + return getRuleContext(TypeContext.class,0); + } + public ExpressionContext expression() { + return getRuleContext(ExpressionContext.class,0); + } + public InstanceOfOpContext(ExpressionContext ctx) { copyFrom(ctx); } + @Override + public void enterRule(ParseTreeListener listener) { + if ( listener instanceof BindingExpressionListener ) ((BindingExpressionListener)listener).enterInstanceOfOp(this); + } + @Override + public void exitRule(ParseTreeListener listener) { + if ( listener instanceof BindingExpressionListener ) ((BindingExpressionListener)listener).exitInstanceOfOp(this); + } + @Override + public <Result> Result accept(ParseTreeVisitor<? extends Result> visitor) { + if ( visitor instanceof BindingExpressionVisitor<?> ) return ((BindingExpressionVisitor<? extends Result>)visitor).visitInstanceOfOp(this); + else return visitor.visitChildren(this); + } + } + public static class BinaryOpContext extends ExpressionContext { + public ExpressionContext left; + public Token op; + public ExpressionContext right; + public ExpressionContext expression(int i) { + return getRuleContext(ExpressionContext.class,i); + } + public List<? extends ExpressionContext> expression() { + return getRuleContexts(ExpressionContext.class); + } + public BinaryOpContext(ExpressionContext ctx) { copyFrom(ctx); } + @Override + public void enterRule(ParseTreeListener listener) { + if ( listener instanceof BindingExpressionListener ) ((BindingExpressionListener)listener).enterBinaryOp(this); + } + @Override + public void exitRule(ParseTreeListener listener) { + if ( listener instanceof BindingExpressionListener ) ((BindingExpressionListener)listener).exitBinaryOp(this); + } + @Override + public <Result> Result accept(ParseTreeVisitor<? extends Result> visitor) { + if ( visitor instanceof BindingExpressionVisitor<?> ) return ((BindingExpressionVisitor<? extends Result>)visitor).visitBinaryOp(this); + else return visitor.visitChildren(this); + } + } + + @RuleVersion(0) + public final ExpressionContext expression() throws RecognitionException { + return expression(0); + } + + private ExpressionContext expression(int _p) throws RecognitionException { + ParserRuleContext _parentctx = _ctx; + int _parentState = getState(); + ExpressionContext _localctx = new ExpressionContext(_ctx, _parentState); + ExpressionContext _prevctx = _localctx; + int _startState = 6; + enterRecursionRule(_localctx, 6, RULE_expression, _p); + int _la; + try { + int _alt; + enterOuterAlt(_localctx, 1); + { + setState(70); + switch ( getInterpreter().adaptivePredict(_input,2,_ctx) ) { + case 1: + { + _localctx = new CastOpContext(_localctx); + _ctx = _localctx; + _prevctx = _localctx; + + setState(53); match(T__32); + setState(54); type(); + setState(55); match(T__15); + setState(56); expression(16); + } + break; + + case 2: + { + _localctx = new UnaryOpContext(_localctx); + _ctx = _localctx; + _prevctx = _localctx; + setState(58); + ((UnaryOpContext)_localctx).op = _input.LT(1); + _la = _input.LA(1); + if ( !(_la==T__33 || _la==T__13) ) { + ((UnaryOpContext)_localctx).op = _errHandler.recoverInline(this); + } + consume(); + setState(59); expression(15); + } + break; + + case 3: + { + _localctx = new UnaryOpContext(_localctx); + _ctx = _localctx; + _prevctx = _localctx; + setState(60); + ((UnaryOpContext)_localctx).op = _input.LT(1); + _la = _input.LA(1); + if ( !(_la==T__5 || _la==T__0) ) { + ((UnaryOpContext)_localctx).op = _errHandler.recoverInline(this); + } + consume(); + setState(61); expression(14); + } + break; + + case 4: + { + _localctx = new GroupingContext(_localctx); + _ctx = _localctx; + _prevctx = _localctx; + setState(62); match(T__32); + setState(63); expression(0); + setState(64); match(T__15); + } + break; + + case 5: + { + _localctx = new PrimaryContext(_localctx); + _ctx = _localctx; + _prevctx = _localctx; + setState(66); literal(); + } + break; + + case 6: + { + _localctx = new PrimaryContext(_localctx); + _ctx = _localctx; + _prevctx = _localctx; + setState(67); identifier(); + } + break; + + case 7: + { + _localctx = new PrimaryContext(_localctx); + _ctx = _localctx; + _prevctx = _localctx; + setState(68); classExtraction(); + } + break; + + case 8: + { + _localctx = new ResourceContext(_localctx); + _ctx = _localctx; + _prevctx = _localctx; + setState(69); resources(); + } + break; + } + _ctx.stop = _input.LT(-1); + setState(132); + _errHandler.sync(this); + _alt = getInterpreter().adaptivePredict(_input,5,_ctx); + while ( _alt!=2 && _alt!=org.antlr.v4.runtime.atn.ATN.INVALID_ALT_NUMBER ) { + if ( _alt==1 ) { + if ( _parseListeners!=null ) triggerExitRuleEvent(); + _prevctx = _localctx; + { + setState(130); + switch ( getInterpreter().adaptivePredict(_input,4,_ctx) ) { + case 1: + { + _localctx = new MathOpContext(new ExpressionContext(_parentctx, _parentState)); + ((MathOpContext)_localctx).left = _prevctx; + pushNewRecursionContext(_localctx, _startState, RULE_expression); + setState(72); + if (!(precpred(_ctx, 13))) throw new FailedPredicateException(this, "precpred(_ctx, 13)"); + setState(73); + ((MathOpContext)_localctx).op = _input.LT(1); + _la = _input.LA(1); + if ( !((((_la) & ~0x3f) == 0 && ((1L << _la) & ((1L << T__35) | (1L << T__18) | (1L << T__6))) != 0)) ) { + ((MathOpContext)_localctx).op = _errHandler.recoverInline(this); + } + consume(); + setState(74); ((MathOpContext)_localctx).right = expression(14); + } + break; + + case 2: + { + _localctx = new MathOpContext(new ExpressionContext(_parentctx, _parentState)); + ((MathOpContext)_localctx).left = _prevctx; + pushNewRecursionContext(_localctx, _startState, RULE_expression); + setState(75); + if (!(precpred(_ctx, 12))) throw new FailedPredicateException(this, "precpred(_ctx, 12)"); + setState(76); + ((MathOpContext)_localctx).op = _input.LT(1); + _la = _input.LA(1); + if ( !(_la==T__33 || _la==T__13) ) { + ((MathOpContext)_localctx).op = _errHandler.recoverInline(this); + } + consume(); + setState(77); ((MathOpContext)_localctx).right = expression(13); + } + break; + + case 3: + { + _localctx = new BitShiftOpContext(new ExpressionContext(_parentctx, _parentState)); + ((BitShiftOpContext)_localctx).left = _prevctx; + pushNewRecursionContext(_localctx, _startState, RULE_expression); + setState(78); + if (!(precpred(_ctx, 11))) throw new FailedPredicateException(this, "precpred(_ctx, 11)"); + setState(79); + ((BitShiftOpContext)_localctx).op = _input.LT(1); + _la = _input.LA(1); + if ( !((((_la) & ~0x3f) == 0 && ((1L << _la) & ((1L << T__41) | (1L << T__25) | (1L << T__20))) != 0)) ) { + ((BitShiftOpContext)_localctx).op = _errHandler.recoverInline(this); + } + consume(); + setState(80); ((BitShiftOpContext)_localctx).right = expression(12); + } + break; + + case 4: + { + _localctx = new ComparisonOpContext(new ExpressionContext(_parentctx, _parentState)); + ((ComparisonOpContext)_localctx).left = _prevctx; + pushNewRecursionContext(_localctx, _startState, RULE_expression); + setState(81); + if (!(precpred(_ctx, 10))) throw new FailedPredicateException(this, "precpred(_ctx, 10)"); + setState(82); + ((ComparisonOpContext)_localctx).op = _input.LT(1); + _la = _input.LA(1); + if ( !((((_la) & ~0x3f) == 0 && ((1L << _la) & ((1L << T__30) | (1L << T__27) | (1L << T__9) | (1L << T__4))) != 0)) ) { + ((ComparisonOpContext)_localctx).op = _errHandler.recoverInline(this); + } + consume(); + setState(83); ((ComparisonOpContext)_localctx).right = expression(11); + } + break; + + case 5: + { + _localctx = new ComparisonOpContext(new ExpressionContext(_parentctx, _parentState)); + ((ComparisonOpContext)_localctx).left = _prevctx; + pushNewRecursionContext(_localctx, _startState, RULE_expression); + setState(84); + if (!(precpred(_ctx, 8))) throw new FailedPredicateException(this, "precpred(_ctx, 8)"); + setState(85); + ((ComparisonOpContext)_localctx).op = _input.LT(1); + _la = _input.LA(1); + if ( !(_la==T__28 || _la==T__7) ) { + ((ComparisonOpContext)_localctx).op = _errHandler.recoverInline(this); + } + consume(); + setState(86); ((ComparisonOpContext)_localctx).right = expression(9); + } + break; + + case 6: + { + _localctx = new BinaryOpContext(new ExpressionContext(_parentctx, _parentState)); + ((BinaryOpContext)_localctx).left = _prevctx; + pushNewRecursionContext(_localctx, _startState, RULE_expression); + setState(87); + if (!(precpred(_ctx, 7))) throw new FailedPredicateException(this, "precpred(_ctx, 7)"); + setState(88); ((BinaryOpContext)_localctx).op = match(T__38); + setState(89); ((BinaryOpContext)_localctx).right = expression(8); + } + break; + + case 7: + { + _localctx = new BinaryOpContext(new ExpressionContext(_parentctx, _parentState)); + ((BinaryOpContext)_localctx).left = _prevctx; + pushNewRecursionContext(_localctx, _startState, RULE_expression); + setState(90); + if (!(precpred(_ctx, 6))) throw new FailedPredicateException(this, "precpred(_ctx, 6)"); + setState(91); ((BinaryOpContext)_localctx).op = match(T__17); + setState(92); ((BinaryOpContext)_localctx).right = expression(7); + } + break; + + case 8: + { + _localctx = new BinaryOpContext(new ExpressionContext(_parentctx, _parentState)); + ((BinaryOpContext)_localctx).left = _prevctx; + pushNewRecursionContext(_localctx, _startState, RULE_expression); + setState(93); + if (!(precpred(_ctx, 5))) throw new FailedPredicateException(this, "precpred(_ctx, 5)"); + setState(94); ((BinaryOpContext)_localctx).op = match(T__2); + setState(95); ((BinaryOpContext)_localctx).right = expression(6); + } + break; + + case 9: + { + _localctx = new AndOrOpContext(new ExpressionContext(_parentctx, _parentState)); + ((AndOrOpContext)_localctx).left = _prevctx; + pushNewRecursionContext(_localctx, _startState, RULE_expression); + setState(96); + if (!(precpred(_ctx, 4))) throw new FailedPredicateException(this, "precpred(_ctx, 4)"); + setState(97); ((AndOrOpContext)_localctx).op = match(T__11); + setState(98); ((AndOrOpContext)_localctx).right = expression(5); + } + break; + + case 10: + { + _localctx = new AndOrOpContext(new ExpressionContext(_parentctx, _parentState)); + ((AndOrOpContext)_localctx).left = _prevctx; + pushNewRecursionContext(_localctx, _startState, RULE_expression); + setState(99); + if (!(precpred(_ctx, 3))) throw new FailedPredicateException(this, "precpred(_ctx, 3)"); + setState(100); ((AndOrOpContext)_localctx).op = match(T__10); + setState(101); ((AndOrOpContext)_localctx).right = expression(4); + } + break; + + case 11: + { + _localctx = new TernaryOpContext(new ExpressionContext(_parentctx, _parentState)); + ((TernaryOpContext)_localctx).left = _prevctx; + pushNewRecursionContext(_localctx, _startState, RULE_expression); + setState(102); + if (!(precpred(_ctx, 2))) throw new FailedPredicateException(this, "precpred(_ctx, 2)"); + setState(103); ((TernaryOpContext)_localctx).op = match(T__26); + setState(104); ((TernaryOpContext)_localctx).iftrue = expression(0); + setState(105); match(T__31); + setState(106); ((TernaryOpContext)_localctx).iffalse = expression(3); + } + break; + + case 12: + { + _localctx = new QuestionQuestionOpContext(new ExpressionContext(_parentctx, _parentState)); + ((QuestionQuestionOpContext)_localctx).left = _prevctx; + pushNewRecursionContext(_localctx, _startState, RULE_expression); + setState(108); + if (!(precpred(_ctx, 1))) throw new FailedPredicateException(this, "precpred(_ctx, 1)"); + setState(109); ((QuestionQuestionOpContext)_localctx).op = match(T__8); + setState(110); ((QuestionQuestionOpContext)_localctx).right = expression(2); + } + break; + + case 13: + { + _localctx = new DotOpContext(new ExpressionContext(_parentctx, _parentState)); + pushNewRecursionContext(_localctx, _startState, RULE_expression); + setState(111); + if (!(precpred(_ctx, 19))) throw new FailedPredicateException(this, "precpred(_ctx, 19)"); + setState(112); match(T__14); + setState(113); match(Identifier); + } + break; + + case 14: + { + _localctx = new BracketOpContext(new ExpressionContext(_parentctx, _parentState)); + pushNewRecursionContext(_localctx, _startState, RULE_expression); + setState(114); + if (!(precpred(_ctx, 18))) throw new FailedPredicateException(this, "precpred(_ctx, 18)"); + setState(115); match(T__34); + setState(116); expression(0); + setState(117); match(T__40); + } + break; + + case 15: + { + _localctx = new MethodInvocationContext(new ExpressionContext(_parentctx, _parentState)); + ((MethodInvocationContext)_localctx).target = _prevctx; + pushNewRecursionContext(_localctx, _startState, RULE_expression); + setState(119); + if (!(precpred(_ctx, 17))) throw new FailedPredicateException(this, "precpred(_ctx, 17)"); + setState(120); match(T__14); + setState(121); ((MethodInvocationContext)_localctx).methodName = match(Identifier); + setState(122); match(T__32); + setState(124); + _la = _input.LA(1); + if ((((_la) & ~0x3f) == 0 && ((1L << _la) & ((1L << T__42) | (1L << T__39) | (1L << T__33) | (1L << T__32) | (1L << T__29) | (1L << T__24) | (1L << T__23) | (1L << T__22) | (1L << T__21) | (1L << T__19) | (1L << T__16) | (1L << T__13) | (1L << T__5) | (1L << T__0) | (1L << IntegerLiteral) | (1L << FloatingPointLiteral) | (1L << BooleanLiteral) | (1L << CharacterLiteral) | (1L << SingleQuoteString) | (1L << DoubleQuoteString) | (1L << NullLiteral) | (1L << Identifier) | (1L << ResourceReference))) != 0)) { + { + setState(123); ((MethodInvocationContext)_localctx).args = expressionList(); + } + } + + setState(126); match(T__15); + } + break; + + case 16: + { + _localctx = new InstanceOfOpContext(new ExpressionContext(_parentctx, _parentState)); + pushNewRecursionContext(_localctx, _startState, RULE_expression); + setState(127); + if (!(precpred(_ctx, 9))) throw new FailedPredicateException(this, "precpred(_ctx, 9)"); + setState(128); match(T__1); + setState(129); type(); + } + break; + } + } + } + setState(134); + _errHandler.sync(this); + _alt = getInterpreter().adaptivePredict(_input,5,_ctx); + } + } + } + catch (RecognitionException re) { + _localctx.exception = re; + _errHandler.reportError(this, re); + _errHandler.recover(this, re); + } + finally { + unrollRecursionContexts(_parentctx); + } + return _localctx; + } + + public static class ClassExtractionContext extends ParserRuleContext { + public TypeContext type() { + return getRuleContext(TypeContext.class,0); + } + public ClassExtractionContext(ParserRuleContext parent, int invokingState) { + super(parent, invokingState); + } + @Override public int getRuleIndex() { return RULE_classExtraction; } + @Override + public void enterRule(ParseTreeListener listener) { + if ( listener instanceof BindingExpressionListener ) ((BindingExpressionListener)listener).enterClassExtraction(this); + } + @Override + public void exitRule(ParseTreeListener listener) { + if ( listener instanceof BindingExpressionListener ) ((BindingExpressionListener)listener).exitClassExtraction(this); + } + @Override + public <Result> Result accept(ParseTreeVisitor<? extends Result> visitor) { + if ( visitor instanceof BindingExpressionVisitor<?> ) return ((BindingExpressionVisitor<? extends Result>)visitor).visitClassExtraction(this); + else return visitor.visitChildren(this); + } + } + + @RuleVersion(0) + public final ClassExtractionContext classExtraction() throws RecognitionException { + ClassExtractionContext _localctx = new ClassExtractionContext(_ctx, getState()); + enterRule(_localctx, 8, RULE_classExtraction); + try { + setState(142); + switch (_input.LA(1)) { + case T__42: + case T__39: + case T__29: + case T__23: + case T__22: + case T__21: + case T__19: + case T__16: + case Identifier: + enterOuterAlt(_localctx, 1); + { + setState(135); type(); + setState(136); match(T__14); + setState(137); match(T__3); + } + break; + case T__24: + enterOuterAlt(_localctx, 2); + { + setState(139); match(T__24); + setState(140); match(T__14); + setState(141); match(T__3); + } + break; + default: + throw new NoViableAltException(this); + } + } + catch (RecognitionException re) { + _localctx.exception = re; + _errHandler.reportError(this, re); + _errHandler.recover(this, re); + } + finally { + exitRule(); + } + return _localctx; + } + + public static class ExpressionListContext extends ParserRuleContext { + public ExpressionContext expression(int i) { + return getRuleContext(ExpressionContext.class,i); + } + public List<? extends ExpressionContext> expression() { + return getRuleContexts(ExpressionContext.class); + } + public ExpressionListContext(ParserRuleContext parent, int invokingState) { + super(parent, invokingState); + } + @Override public int getRuleIndex() { return RULE_expressionList; } + @Override + public void enterRule(ParseTreeListener listener) { + if ( listener instanceof BindingExpressionListener ) ((BindingExpressionListener)listener).enterExpressionList(this); + } + @Override + public void exitRule(ParseTreeListener listener) { + if ( listener instanceof BindingExpressionListener ) ((BindingExpressionListener)listener).exitExpressionList(this); + } + @Override + public <Result> Result accept(ParseTreeVisitor<? extends Result> visitor) { + if ( visitor instanceof BindingExpressionVisitor<?> ) return ((BindingExpressionVisitor<? extends Result>)visitor).visitExpressionList(this); + else return visitor.visitChildren(this); + } + } + + @RuleVersion(0) + public final ExpressionListContext expressionList() throws RecognitionException { + ExpressionListContext _localctx = new ExpressionListContext(_ctx, getState()); + enterRule(_localctx, 10, RULE_expressionList); + int _la; + try { + enterOuterAlt(_localctx, 1); + { + setState(144); expression(0); + setState(149); + _errHandler.sync(this); + _la = _input.LA(1); + while (_la==T__36) { + { + { + setState(145); match(T__36); + setState(146); expression(0); + } + } + setState(151); + _errHandler.sync(this); + _la = _input.LA(1); + } + } + } + catch (RecognitionException re) { + _localctx.exception = re; + _errHandler.reportError(this, re); + _errHandler.recover(this, re); + } + finally { + exitRule(); + } + return _localctx; + } + + public static class LiteralContext extends ParserRuleContext { + public StringLiteralContext stringLiteral() { + return getRuleContext(StringLiteralContext.class,0); + } + public JavaLiteralContext javaLiteral() { + return getRuleContext(JavaLiteralContext.class,0); + } + public LiteralContext(ParserRuleContext parent, int invokingState) { + super(parent, invokingState); + } + @Override public int getRuleIndex() { return RULE_literal; } + @Override + public void enterRule(ParseTreeListener listener) { + if ( listener instanceof BindingExpressionListener ) ((BindingExpressionListener)listener).enterLiteral(this); + } + @Override + public void exitRule(ParseTreeListener listener) { + if ( listener instanceof BindingExpressionListener ) ((BindingExpressionListener)listener).exitLiteral(this); + } + @Override + public <Result> Result accept(ParseTreeVisitor<? extends Result> visitor) { + if ( visitor instanceof BindingExpressionVisitor<?> ) return ((BindingExpressionVisitor<? extends Result>)visitor).visitLiteral(this); + else return visitor.visitChildren(this); + } + } + + @RuleVersion(0) + public final LiteralContext literal() throws RecognitionException { + LiteralContext _localctx = new LiteralContext(_ctx, getState()); + enterRule(_localctx, 12, RULE_literal); + try { + setState(154); + switch (_input.LA(1)) { + case IntegerLiteral: + case FloatingPointLiteral: + case BooleanLiteral: + case CharacterLiteral: + case NullLiteral: + enterOuterAlt(_localctx, 1); + { + setState(152); javaLiteral(); + } + break; + case SingleQuoteString: + case DoubleQuoteString: + enterOuterAlt(_localctx, 2); + { + setState(153); stringLiteral(); + } + break; + default: + throw new NoViableAltException(this); + } + } + catch (RecognitionException re) { + _localctx.exception = re; + _errHandler.reportError(this, re); + _errHandler.recover(this, re); + } + finally { + exitRule(); + } + return _localctx; + } + + public static class IdentifierContext extends ParserRuleContext { + public TerminalNode Identifier() { return getToken(BindingExpressionParser.Identifier, 0); } + public IdentifierContext(ParserRuleContext parent, int invokingState) { + super(parent, invokingState); + } + @Override public int getRuleIndex() { return RULE_identifier; } + @Override + public void enterRule(ParseTreeListener listener) { + if ( listener instanceof BindingExpressionListener ) ((BindingExpressionListener)listener).enterIdentifier(this); + } + @Override + public void exitRule(ParseTreeListener listener) { + if ( listener instanceof BindingExpressionListener ) ((BindingExpressionListener)listener).exitIdentifier(this); + } + @Override + public <Result> Result accept(ParseTreeVisitor<? extends Result> visitor) { + if ( visitor instanceof BindingExpressionVisitor<?> ) return ((BindingExpressionVisitor<? extends Result>)visitor).visitIdentifier(this); + else return visitor.visitChildren(this); + } + } + + @RuleVersion(0) + public final IdentifierContext identifier() throws RecognitionException { + IdentifierContext _localctx = new IdentifierContext(_ctx, getState()); + enterRule(_localctx, 14, RULE_identifier); + try { + enterOuterAlt(_localctx, 1); + { + setState(156); match(Identifier); + } + } + catch (RecognitionException re) { + _localctx.exception = re; + _errHandler.reportError(this, re); + _errHandler.recover(this, re); + } + finally { + exitRule(); + } + return _localctx; + } + + public static class JavaLiteralContext extends ParserRuleContext { + public TerminalNode NullLiteral() { return getToken(BindingExpressionParser.NullLiteral, 0); } + public TerminalNode CharacterLiteral() { return getToken(BindingExpressionParser.CharacterLiteral, 0); } + public TerminalNode IntegerLiteral() { return getToken(BindingExpressionParser.IntegerLiteral, 0); } + public TerminalNode FloatingPointLiteral() { return getToken(BindingExpressionParser.FloatingPointLiteral, 0); } + public TerminalNode BooleanLiteral() { return getToken(BindingExpressionParser.BooleanLiteral, 0); } + public JavaLiteralContext(ParserRuleContext parent, int invokingState) { + super(parent, invokingState); + } + @Override public int getRuleIndex() { return RULE_javaLiteral; } + @Override + public void enterRule(ParseTreeListener listener) { + if ( listener instanceof BindingExpressionListener ) ((BindingExpressionListener)listener).enterJavaLiteral(this); + } + @Override + public void exitRule(ParseTreeListener listener) { + if ( listener instanceof BindingExpressionListener ) ((BindingExpressionListener)listener).exitJavaLiteral(this); + } + @Override + public <Result> Result accept(ParseTreeVisitor<? extends Result> visitor) { + if ( visitor instanceof BindingExpressionVisitor<?> ) return ((BindingExpressionVisitor<? extends Result>)visitor).visitJavaLiteral(this); + else return visitor.visitChildren(this); + } + } + + @RuleVersion(0) + public final JavaLiteralContext javaLiteral() throws RecognitionException { + JavaLiteralContext _localctx = new JavaLiteralContext(_ctx, getState()); + enterRule(_localctx, 16, RULE_javaLiteral); + int _la; + try { + enterOuterAlt(_localctx, 1); + { + setState(158); + _la = _input.LA(1); + if ( !((((_la) & ~0x3f) == 0 && ((1L << _la) & ((1L << IntegerLiteral) | (1L << FloatingPointLiteral) | (1L << BooleanLiteral) | (1L << CharacterLiteral) | (1L << NullLiteral))) != 0)) ) { + _errHandler.recoverInline(this); + } + consume(); + } + } + catch (RecognitionException re) { + _localctx.exception = re; + _errHandler.reportError(this, re); + _errHandler.recover(this, re); + } + finally { + exitRule(); + } + return _localctx; + } + + public static class StringLiteralContext extends ParserRuleContext { + public TerminalNode SingleQuoteString() { return getToken(BindingExpressionParser.SingleQuoteString, 0); } + public TerminalNode DoubleQuoteString() { return getToken(BindingExpressionParser.DoubleQuoteString, 0); } + public StringLiteralContext(ParserRuleContext parent, int invokingState) { + super(parent, invokingState); + } + @Override public int getRuleIndex() { return RULE_stringLiteral; } + @Override + public void enterRule(ParseTreeListener listener) { + if ( listener instanceof BindingExpressionListener ) ((BindingExpressionListener)listener).enterStringLiteral(this); + } + @Override + public void exitRule(ParseTreeListener listener) { + if ( listener instanceof BindingExpressionListener ) ((BindingExpressionListener)listener).exitStringLiteral(this); + } + @Override + public <Result> Result accept(ParseTreeVisitor<? extends Result> visitor) { + if ( visitor instanceof BindingExpressionVisitor<?> ) return ((BindingExpressionVisitor<? extends Result>)visitor).visitStringLiteral(this); + else return visitor.visitChildren(this); + } + } + + @RuleVersion(0) + public final StringLiteralContext stringLiteral() throws RecognitionException { + StringLiteralContext _localctx = new StringLiteralContext(_ctx, getState()); + enterRule(_localctx, 18, RULE_stringLiteral); + int _la; + try { + enterOuterAlt(_localctx, 1); + { + setState(160); + _la = _input.LA(1); + if ( !(_la==SingleQuoteString || _la==DoubleQuoteString) ) { + _errHandler.recoverInline(this); + } + consume(); + } + } + catch (RecognitionException re) { + _localctx.exception = re; + _errHandler.reportError(this, re); + _errHandler.recover(this, re); + } + finally { + exitRule(); + } + return _localctx; + } + + public static class ExplicitGenericInvocationContext extends ParserRuleContext { + public ExplicitGenericInvocationSuffixContext explicitGenericInvocationSuffix() { + return getRuleContext(ExplicitGenericInvocationSuffixContext.class,0); + } + public TypeArgumentsContext typeArguments() { + return getRuleContext(TypeArgumentsContext.class,0); + } + public ExplicitGenericInvocationContext(ParserRuleContext parent, int invokingState) { + super(parent, invokingState); + } + @Override public int getRuleIndex() { return RULE_explicitGenericInvocation; } + @Override + public void enterRule(ParseTreeListener listener) { + if ( listener instanceof BindingExpressionListener ) ((BindingExpressionListener)listener).enterExplicitGenericInvocation(this); + } + @Override + public void exitRule(ParseTreeListener listener) { + if ( listener instanceof BindingExpressionListener ) ((BindingExpressionListener)listener).exitExplicitGenericInvocation(this); + } + @Override + public <Result> Result accept(ParseTreeVisitor<? extends Result> visitor) { + if ( visitor instanceof BindingExpressionVisitor<?> ) return ((BindingExpressionVisitor<? extends Result>)visitor).visitExplicitGenericInvocation(this); + else return visitor.visitChildren(this); + } + } + + @RuleVersion(0) + public final ExplicitGenericInvocationContext explicitGenericInvocation() throws RecognitionException { + ExplicitGenericInvocationContext _localctx = new ExplicitGenericInvocationContext(_ctx, getState()); + enterRule(_localctx, 20, RULE_explicitGenericInvocation); + try { + enterOuterAlt(_localctx, 1); + { + setState(162); typeArguments(); + setState(163); explicitGenericInvocationSuffix(); + } + } + catch (RecognitionException re) { + _localctx.exception = re; + _errHandler.reportError(this, re); + _errHandler.recover(this, re); + } + finally { + exitRule(); + } + return _localctx; + } + + public static class TypeArgumentsContext extends ParserRuleContext { + public TypeContext type(int i) { + return getRuleContext(TypeContext.class,i); + } + public List<? extends TypeContext> type() { + return getRuleContexts(TypeContext.class); + } + public TypeArgumentsContext(ParserRuleContext parent, int invokingState) { + super(parent, invokingState); + } + @Override public int getRuleIndex() { return RULE_typeArguments; } + @Override + public void enterRule(ParseTreeListener listener) { + if ( listener instanceof BindingExpressionListener ) ((BindingExpressionListener)listener).enterTypeArguments(this); + } + @Override + public void exitRule(ParseTreeListener listener) { + if ( listener instanceof BindingExpressionListener ) ((BindingExpressionListener)listener).exitTypeArguments(this); + } + @Override + public <Result> Result accept(ParseTreeVisitor<? extends Result> visitor) { + if ( visitor instanceof BindingExpressionVisitor<?> ) return ((BindingExpressionVisitor<? extends Result>)visitor).visitTypeArguments(this); + else return visitor.visitChildren(this); + } + } + + @RuleVersion(0) + public final TypeArgumentsContext typeArguments() throws RecognitionException { + TypeArgumentsContext _localctx = new TypeArgumentsContext(_ctx, getState()); + enterRule(_localctx, 22, RULE_typeArguments); + int _la; + try { + enterOuterAlt(_localctx, 1); + { + setState(165); match(T__30); + setState(166); type(); + setState(171); + _errHandler.sync(this); + _la = _input.LA(1); + while (_la==T__36) { + { + { + setState(167); match(T__36); + setState(168); type(); + } + } + setState(173); + _errHandler.sync(this); + _la = _input.LA(1); + } + setState(174); match(T__9); + } + } + catch (RecognitionException re) { + _localctx.exception = re; + _errHandler.reportError(this, re); + _errHandler.recover(this, re); + } + finally { + exitRule(); + } + return _localctx; + } + + public static class TypeContext extends ParserRuleContext { + public PrimitiveTypeContext primitiveType() { + return getRuleContext(PrimitiveTypeContext.class,0); + } + public ClassOrInterfaceTypeContext classOrInterfaceType() { + return getRuleContext(ClassOrInterfaceTypeContext.class,0); + } + public TypeContext(ParserRuleContext parent, int invokingState) { + super(parent, invokingState); + } + @Override public int getRuleIndex() { return RULE_type; } + @Override + public void enterRule(ParseTreeListener listener) { + if ( listener instanceof BindingExpressionListener ) ((BindingExpressionListener)listener).enterType(this); + } + @Override + public void exitRule(ParseTreeListener listener) { + if ( listener instanceof BindingExpressionListener ) ((BindingExpressionListener)listener).exitType(this); + } + @Override + public <Result> Result accept(ParseTreeVisitor<? extends Result> visitor) { + if ( visitor instanceof BindingExpressionVisitor<?> ) return ((BindingExpressionVisitor<? extends Result>)visitor).visitType(this); + else return visitor.visitChildren(this); + } + } + + @RuleVersion(0) + public final TypeContext type() throws RecognitionException { + TypeContext _localctx = new TypeContext(_ctx, getState()); + enterRule(_localctx, 24, RULE_type); + try { + int _alt; + setState(192); + switch (_input.LA(1)) { + case Identifier: + enterOuterAlt(_localctx, 1); + { + setState(176); classOrInterfaceType(); + setState(181); + _errHandler.sync(this); + _alt = getInterpreter().adaptivePredict(_input,10,_ctx); + while ( _alt!=2 && _alt!=org.antlr.v4.runtime.atn.ATN.INVALID_ALT_NUMBER ) { + if ( _alt==1 ) { + { + { + setState(177); match(T__34); + setState(178); match(T__40); + } + } + } + setState(183); + _errHandler.sync(this); + _alt = getInterpreter().adaptivePredict(_input,10,_ctx); + } + } + break; + case T__42: + case T__39: + case T__29: + case T__23: + case T__22: + case T__21: + case T__19: + case T__16: + enterOuterAlt(_localctx, 2); + { + setState(184); primitiveType(); + setState(189); + _errHandler.sync(this); + _alt = getInterpreter().adaptivePredict(_input,11,_ctx); + while ( _alt!=2 && _alt!=org.antlr.v4.runtime.atn.ATN.INVALID_ALT_NUMBER ) { + if ( _alt==1 ) { + { + { + setState(185); match(T__34); + setState(186); match(T__40); + } + } + } + setState(191); + _errHandler.sync(this); + _alt = getInterpreter().adaptivePredict(_input,11,_ctx); + } + } + break; + default: + throw new NoViableAltException(this); + } + } + catch (RecognitionException re) { + _localctx.exception = re; + _errHandler.reportError(this, re); + _errHandler.recover(this, re); + } + finally { + exitRule(); + } + return _localctx; + } + + public static class ExplicitGenericInvocationSuffixContext extends ParserRuleContext { + public TerminalNode Identifier() { return getToken(BindingExpressionParser.Identifier, 0); } + public ArgumentsContext arguments() { + return getRuleContext(ArgumentsContext.class,0); + } + public ExplicitGenericInvocationSuffixContext(ParserRuleContext parent, int invokingState) { + super(parent, invokingState); + } + @Override public int getRuleIndex() { return RULE_explicitGenericInvocationSuffix; } + @Override + public void enterRule(ParseTreeListener listener) { + if ( listener instanceof BindingExpressionListener ) ((BindingExpressionListener)listener).enterExplicitGenericInvocationSuffix(this); + } + @Override + public void exitRule(ParseTreeListener listener) { + if ( listener instanceof BindingExpressionListener ) ((BindingExpressionListener)listener).exitExplicitGenericInvocationSuffix(this); + } + @Override + public <Result> Result accept(ParseTreeVisitor<? extends Result> visitor) { + if ( visitor instanceof BindingExpressionVisitor<?> ) return ((BindingExpressionVisitor<? extends Result>)visitor).visitExplicitGenericInvocationSuffix(this); + else return visitor.visitChildren(this); + } + } + + @RuleVersion(0) + public final ExplicitGenericInvocationSuffixContext explicitGenericInvocationSuffix() throws RecognitionException { + ExplicitGenericInvocationSuffixContext _localctx = new ExplicitGenericInvocationSuffixContext(_ctx, getState()); + enterRule(_localctx, 26, RULE_explicitGenericInvocationSuffix); + try { + enterOuterAlt(_localctx, 1); + { + setState(194); match(Identifier); + setState(195); arguments(); + } + } + catch (RecognitionException re) { + _localctx.exception = re; + _errHandler.reportError(this, re); + _errHandler.recover(this, re); + } + finally { + exitRule(); + } + return _localctx; + } + + public static class ArgumentsContext extends ParserRuleContext { + public ExpressionListContext expressionList() { + return getRuleContext(ExpressionListContext.class,0); + } + public ArgumentsContext(ParserRuleContext parent, int invokingState) { + super(parent, invokingState); + } + @Override public int getRuleIndex() { return RULE_arguments; } + @Override + public void enterRule(ParseTreeListener listener) { + if ( listener instanceof BindingExpressionListener ) ((BindingExpressionListener)listener).enterArguments(this); + } + @Override + public void exitRule(ParseTreeListener listener) { + if ( listener instanceof BindingExpressionListener ) ((BindingExpressionListener)listener).exitArguments(this); + } + @Override + public <Result> Result accept(ParseTreeVisitor<? extends Result> visitor) { + if ( visitor instanceof BindingExpressionVisitor<?> ) return ((BindingExpressionVisitor<? extends Result>)visitor).visitArguments(this); + else return visitor.visitChildren(this); + } + } + + @RuleVersion(0) + public final ArgumentsContext arguments() throws RecognitionException { + ArgumentsContext _localctx = new ArgumentsContext(_ctx, getState()); + enterRule(_localctx, 28, RULE_arguments); + int _la; + try { + enterOuterAlt(_localctx, 1); + { + setState(197); match(T__32); + setState(199); + _la = _input.LA(1); + if ((((_la) & ~0x3f) == 0 && ((1L << _la) & ((1L << T__42) | (1L << T__39) | (1L << T__33) | (1L << T__32) | (1L << T__29) | (1L << T__24) | (1L << T__23) | (1L << T__22) | (1L << T__21) | (1L << T__19) | (1L << T__16) | (1L << T__13) | (1L << T__5) | (1L << T__0) | (1L << IntegerLiteral) | (1L << FloatingPointLiteral) | (1L << BooleanLiteral) | (1L << CharacterLiteral) | (1L << SingleQuoteString) | (1L << DoubleQuoteString) | (1L << NullLiteral) | (1L << Identifier) | (1L << ResourceReference))) != 0)) { + { + setState(198); expressionList(); + } + } + + setState(201); match(T__15); + } + } + catch (RecognitionException re) { + _localctx.exception = re; + _errHandler.reportError(this, re); + _errHandler.recover(this, re); + } + finally { + exitRule(); + } + return _localctx; + } + + public static class ClassOrInterfaceTypeContext extends ParserRuleContext { + public TypeArgumentsContext typeArguments(int i) { + return getRuleContext(TypeArgumentsContext.class,i); + } + public TerminalNode Identifier(int i) { + return getToken(BindingExpressionParser.Identifier, i); + } + public List<? extends TerminalNode> Identifier() { return getTokens(BindingExpressionParser.Identifier); } + public List<? extends TypeArgumentsContext> typeArguments() { + return getRuleContexts(TypeArgumentsContext.class); + } + public IdentifierContext identifier() { + return getRuleContext(IdentifierContext.class,0); + } + public ClassOrInterfaceTypeContext(ParserRuleContext parent, int invokingState) { + super(parent, invokingState); + } + @Override public int getRuleIndex() { return RULE_classOrInterfaceType; } + @Override + public void enterRule(ParseTreeListener listener) { + if ( listener instanceof BindingExpressionListener ) ((BindingExpressionListener)listener).enterClassOrInterfaceType(this); + } + @Override + public void exitRule(ParseTreeListener listener) { + if ( listener instanceof BindingExpressionListener ) ((BindingExpressionListener)listener).exitClassOrInterfaceType(this); + } + @Override + public <Result> Result accept(ParseTreeVisitor<? extends Result> visitor) { + if ( visitor instanceof BindingExpressionVisitor<?> ) return ((BindingExpressionVisitor<? extends Result>)visitor).visitClassOrInterfaceType(this); + else return visitor.visitChildren(this); + } + } + + @RuleVersion(0) + public final ClassOrInterfaceTypeContext classOrInterfaceType() throws RecognitionException { + ClassOrInterfaceTypeContext _localctx = new ClassOrInterfaceTypeContext(_ctx, getState()); + enterRule(_localctx, 30, RULE_classOrInterfaceType); + try { + int _alt; + enterOuterAlt(_localctx, 1); + { + setState(203); identifier(); + setState(205); + switch ( getInterpreter().adaptivePredict(_input,14,_ctx) ) { + case 1: + { + setState(204); typeArguments(); + } + break; + } + setState(214); + _errHandler.sync(this); + _alt = getInterpreter().adaptivePredict(_input,16,_ctx); + while ( _alt!=2 && _alt!=org.antlr.v4.runtime.atn.ATN.INVALID_ALT_NUMBER ) { + if ( _alt==1 ) { + { + { + setState(207); match(T__14); + setState(208); match(Identifier); + setState(210); + switch ( getInterpreter().adaptivePredict(_input,15,_ctx) ) { + case 1: + { + setState(209); typeArguments(); + } + break; + } + } + } + } + setState(216); + _errHandler.sync(this); + _alt = getInterpreter().adaptivePredict(_input,16,_ctx); + } + } + } + catch (RecognitionException re) { + _localctx.exception = re; + _errHandler.reportError(this, re); + _errHandler.recover(this, re); + } + finally { + exitRule(); + } + return _localctx; + } + + public static class PrimitiveTypeContext extends ParserRuleContext { + public PrimitiveTypeContext(ParserRuleContext parent, int invokingState) { + super(parent, invokingState); + } + @Override public int getRuleIndex() { return RULE_primitiveType; } + @Override + public void enterRule(ParseTreeListener listener) { + if ( listener instanceof BindingExpressionListener ) ((BindingExpressionListener)listener).enterPrimitiveType(this); + } + @Override + public void exitRule(ParseTreeListener listener) { + if ( listener instanceof BindingExpressionListener ) ((BindingExpressionListener)listener).exitPrimitiveType(this); + } + @Override + public <Result> Result accept(ParseTreeVisitor<? extends Result> visitor) { + if ( visitor instanceof BindingExpressionVisitor<?> ) return ((BindingExpressionVisitor<? extends Result>)visitor).visitPrimitiveType(this); + else return visitor.visitChildren(this); + } + } + + @RuleVersion(0) + public final PrimitiveTypeContext primitiveType() throws RecognitionException { + PrimitiveTypeContext _localctx = new PrimitiveTypeContext(_ctx, getState()); + enterRule(_localctx, 32, RULE_primitiveType); + int _la; + try { + enterOuterAlt(_localctx, 1); + { + setState(217); + _la = _input.LA(1); + if ( !((((_la) & ~0x3f) == 0 && ((1L << _la) & ((1L << T__42) | (1L << T__39) | (1L << T__29) | (1L << T__23) | (1L << T__22) | (1L << T__21) | (1L << T__19) | (1L << T__16))) != 0)) ) { + _errHandler.recoverInline(this); + } + consume(); + } + } + catch (RecognitionException re) { + _localctx.exception = re; + _errHandler.reportError(this, re); + _errHandler.recover(this, re); + } + finally { + exitRule(); + } + return _localctx; + } + + public static class ResourcesContext extends ParserRuleContext { + public ResourceParametersContext resourceParameters() { + return getRuleContext(ResourceParametersContext.class,0); + } + public TerminalNode ResourceReference() { return getToken(BindingExpressionParser.ResourceReference, 0); } + public ResourcesContext(ParserRuleContext parent, int invokingState) { + super(parent, invokingState); + } + @Override public int getRuleIndex() { return RULE_resources; } + @Override + public void enterRule(ParseTreeListener listener) { + if ( listener instanceof BindingExpressionListener ) ((BindingExpressionListener)listener).enterResources(this); + } + @Override + public void exitRule(ParseTreeListener listener) { + if ( listener instanceof BindingExpressionListener ) ((BindingExpressionListener)listener).exitResources(this); + } + @Override + public <Result> Result accept(ParseTreeVisitor<? extends Result> visitor) { + if ( visitor instanceof BindingExpressionVisitor<?> ) return ((BindingExpressionVisitor<? extends Result>)visitor).visitResources(this); + else return visitor.visitChildren(this); + } + } + + @RuleVersion(0) + public final ResourcesContext resources() throws RecognitionException { + ResourcesContext _localctx = new ResourcesContext(_ctx, getState()); + enterRule(_localctx, 34, RULE_resources); + try { + enterOuterAlt(_localctx, 1); + { + setState(219); match(ResourceReference); + setState(221); + switch ( getInterpreter().adaptivePredict(_input,17,_ctx) ) { + case 1: + { + setState(220); resourceParameters(); + } + break; + } + } + } + catch (RecognitionException re) { + _localctx.exception = re; + _errHandler.reportError(this, re); + _errHandler.recover(this, re); + } + finally { + exitRule(); + } + return _localctx; + } + + public static class ResourceParametersContext extends ParserRuleContext { + public ExpressionListContext expressionList() { + return getRuleContext(ExpressionListContext.class,0); + } + public ResourceParametersContext(ParserRuleContext parent, int invokingState) { + super(parent, invokingState); + } + @Override public int getRuleIndex() { return RULE_resourceParameters; } + @Override + public void enterRule(ParseTreeListener listener) { + if ( listener instanceof BindingExpressionListener ) ((BindingExpressionListener)listener).enterResourceParameters(this); + } + @Override + public void exitRule(ParseTreeListener listener) { + if ( listener instanceof BindingExpressionListener ) ((BindingExpressionListener)listener).exitResourceParameters(this); + } + @Override + public <Result> Result accept(ParseTreeVisitor<? extends Result> visitor) { + if ( visitor instanceof BindingExpressionVisitor<?> ) return ((BindingExpressionVisitor<? extends Result>)visitor).visitResourceParameters(this); + else return visitor.visitChildren(this); + } + } + + @RuleVersion(0) + public final ResourceParametersContext resourceParameters() throws RecognitionException { + ResourceParametersContext _localctx = new ResourceParametersContext(_ctx, getState()); + enterRule(_localctx, 36, RULE_resourceParameters); + try { + enterOuterAlt(_localctx, 1); + { + setState(223); match(T__32); + setState(224); expressionList(); + setState(225); match(T__15); + } + } + catch (RecognitionException re) { + _localctx.exception = re; + _errHandler.reportError(this, re); + _errHandler.recover(this, re); + } + finally { + exitRule(); + } + return _localctx; + } + + public boolean sempred(RuleContext _localctx, int ruleIndex, int predIndex) { + switch (ruleIndex) { + case 3: return expression_sempred((ExpressionContext)_localctx, predIndex); + } + return true; + } + private boolean expression_sempred(ExpressionContext _localctx, int predIndex) { + switch (predIndex) { + case 0: return precpred(_ctx, 13); + + case 1: return precpred(_ctx, 12); + + case 2: return precpred(_ctx, 11); + + case 3: return precpred(_ctx, 10); + + case 4: return precpred(_ctx, 8); + + case 5: return precpred(_ctx, 7); + + case 6: return precpred(_ctx, 6); + + case 7: return precpred(_ctx, 5); + + case 8: return precpred(_ctx, 4); + + case 9: return precpred(_ctx, 3); + + case 10: return precpred(_ctx, 2); + + case 11: return precpred(_ctx, 1); + + case 12: return precpred(_ctx, 19); + + case 13: return precpred(_ctx, 18); + + case 14: return precpred(_ctx, 17); + + case 15: return precpred(_ctx, 9); + } + return true; + } + + public static final String _serializedATN = + "\3\uaf6f\u8320\u479d\ub75c\u4880\u1605\u191c\uab37\3:\u00e6\4\2\t\2\4"+ + "\3\t\3\4\4\t\4\4\5\t\5\4\6\t\6\4\7\t\7\4\b\t\b\4\t\t\t\4\n\t\n\4\13\t"+ + "\13\4\f\t\f\4\r\t\r\4\16\t\16\4\17\t\17\4\20\t\20\4\21\t\21\4\22\t\22"+ + "\4\23\t\23\4\24\t\24\3\2\3\2\5\2+\n\2\3\3\3\3\3\3\3\3\3\3\3\4\3\4\3\4"+ + "\5\4\65\n\4\3\5\3\5\3\5\3\5\3\5\3\5\3\5\3\5\3\5\3\5\3\5\3\5\3\5\3\5\3"+ + "\5\3\5\3\5\3\5\5\5I\n\5\3\5\3\5\3\5\3\5\3\5\3\5\3\5\3\5\3\5\3\5\3\5\3"+ + "\5\3\5\3\5\3\5\3\5\3\5\3\5\3\5\3\5\3\5\3\5\3\5\3\5\3\5\3\5\3\5\3\5\3\5"+ + "\3\5\3\5\3\5\3\5\3\5\3\5\3\5\3\5\3\5\3\5\3\5\3\5\3\5\3\5\3\5\3\5\3\5\3"+ + "\5\3\5\3\5\3\5\3\5\3\5\5\5\177\n\5\3\5\3\5\3\5\3\5\7\5\u0085\n\5\f\5\16"+ + "\5\u0088\13\5\3\6\3\6\3\6\3\6\3\6\3\6\3\6\5\6\u0091\n\6\3\7\3\7\3\7\7"+ + "\7\u0096\n\7\f\7\16\7\u0099\13\7\3\b\3\b\5\b\u009d\n\b\3\t\3\t\3\n\3\n"+ + "\3\13\3\13\3\f\3\f\3\f\3\r\3\r\3\r\3\r\7\r\u00ac\n\r\f\r\16\r\u00af\13"+ + "\r\3\r\3\r\3\16\3\16\3\16\7\16\u00b6\n\16\f\16\16\16\u00b9\13\16\3\16"+ + "\3\16\3\16\7\16\u00be\n\16\f\16\16\16\u00c1\13\16\5\16\u00c3\n\16\3\17"+ + "\3\17\3\17\3\20\3\20\5\20\u00ca\n\20\3\20\3\20\3\21\3\21\5\21\u00d0\n"+ + "\21\3\21\3\21\3\21\5\21\u00d5\n\21\7\21\u00d7\n\21\f\21\16\21\u00da\13"+ + "\21\3\22\3\22\3\23\3\23\5\23\u00e0\n\23\3\24\3\24\3\24\3\24\3\24\2\2\3"+ + "\b\25\2\2\4\2\6\2\b\2\n\2\f\2\16\2\20\2\22\2\24\2\26\2\30\2\32\2\34\2"+ + "\36\2 \2\"\2$\2&\2\2\13\4\2\f\f \4\2((--\5\2\n\n\33\33\'\'\5\2\4\4\24"+ + "\24\31\31\6\2\17\17\22\22$$))\4\2\21\21&&\4\2/\62\65\65\3\2\63\64\b\2"+ + "\3\3\6\6\20\20\26\30\32\32\35\35\u00f9\2(\3\2\2\2\4,\3\2\2\2\6\64\3\2"+ + "\2\2\bH\3\2\2\2\n\u0090\3\2\2\2\f\u0092\3\2\2\2\16\u009c\3\2\2\2\20\u009e"+ + "\3\2\2\2\22\u00a0\3\2\2\2\24\u00a2\3\2\2\2\26\u00a4\3\2\2\2\30\u00a7\3"+ + "\2\2\2\32\u00c2\3\2\2\2\34\u00c4\3\2\2\2\36\u00c7\3\2\2\2 \u00cd\3\2\2"+ + "\2\"\u00db\3\2\2\2$\u00dd\3\2\2\2&\u00e1\3\2\2\2(*\5\b\5\2)+\5\4\3\2*"+ + ")\3\2\2\2*+\3\2\2\2+\3\3\2\2\2,-\7\t\2\2-.\7\b\2\2./\7!\2\2/\60\5\6\4"+ + "\2\60\5\3\2\2\2\61\65\5\16\b\2\62\65\78\2\2\63\65\5\20\t\2\64\61\3\2\2"+ + "\2\64\62\3\2\2\2\64\63\3\2\2\2\65\7\3\2\2\2\66\67\b\5\1\2\678\7\r\2\2"+ + "89\5\32\16\29:\7\36\2\2:;\5\b\5\22;I\3\2\2\2<=\t\2\2\2=I\5\b\5\21>?\t"+ + "\3\2\2?I\5\b\5\20@A\7\r\2\2AB\5\b\5\2BC\7\36\2\2CI\3\2\2\2DI\5\16\b\2"+ + "EI\5\20\t\2FI\5\n\6\2GI\5$\23\2H\66\3\2\2\2H<\3\2\2\2H>\3\2\2\2H@\3\2"+ + "\2\2HD\3\2\2\2HE\3\2\2\2HF\3\2\2\2HG\3\2\2\2I\u0086\3\2\2\2JK\f\17\2\2"+ + "KL\t\4\2\2L\u0085\5\b\5\20MN\f\16\2\2NO\t\2\2\2O\u0085\5\b\5\17PQ\f\r"+ + "\2\2QR\t\5\2\2R\u0085\5\b\5\16ST\f\f\2\2TU\t\6\2\2U\u0085\5\b\5\rVW\f"+ + "\n\2\2WX\t\7\2\2X\u0085\5\b\5\13YZ\f\t\2\2Z[\7\7\2\2[\u0085\5\b\5\n\\"+ + "]\f\b\2\2]^\7\34\2\2^\u0085\5\b\5\t_`\f\7\2\2`a\7+\2\2a\u0085\5\b\5\b"+ + "bc\f\6\2\2cd\7\"\2\2d\u0085\5\b\5\7ef\f\5\2\2fg\7#\2\2g\u0085\5\b\5\6"+ + "hi\f\4\2\2ij\7\23\2\2jk\5\b\5\2kl\7\16\2\2lm\5\b\5\5m\u0085\3\2\2\2no"+ + "\f\3\2\2op\7%\2\2p\u0085\5\b\5\4qr\f\25\2\2rs\7\37\2\2s\u0085\7\66\2\2"+ + "tu\f\24\2\2uv\7\13\2\2vw\5\b\5\2wx\7\5\2\2x\u0085\3\2\2\2yz\f\23\2\2z"+ + "{\7\37\2\2{|\7\66\2\2|~\7\r\2\2}\177\5\f\7\2~}\3\2\2\2~\177\3\2\2\2\177"+ + "\u0080\3\2\2\2\u0080\u0085\7\36\2\2\u0081\u0082\f\13\2\2\u0082\u0083\7"+ + ",\2\2\u0083\u0085\5\32\16\2\u0084J\3\2\2\2\u0084M\3\2\2\2\u0084P\3\2\2"+ + "\2\u0084S\3\2\2\2\u0084V\3\2\2\2\u0084Y\3\2\2\2\u0084\\\3\2\2\2\u0084"+ + "_\3\2\2\2\u0084b\3\2\2\2\u0084e\3\2\2\2\u0084h\3\2\2\2\u0084n\3\2\2\2"+ + "\u0084q\3\2\2\2\u0084t\3\2\2\2\u0084y\3\2\2\2\u0084\u0081\3\2\2\2\u0085"+ + "\u0088\3\2\2\2\u0086\u0084\3\2\2\2\u0086\u0087\3\2\2\2\u0087\t\3\2\2\2"+ + "\u0088\u0086\3\2\2\2\u0089\u008a\5\32\16\2\u008a\u008b\7\37\2\2\u008b"+ + "\u008c\7*\2\2\u008c\u0091\3\2\2\2\u008d\u008e\7\25\2\2\u008e\u008f\7\37"+ + "\2\2\u008f\u0091\7*\2\2\u0090\u0089\3\2\2\2\u0090\u008d\3\2\2\2\u0091"+ + "\13\3\2\2\2\u0092\u0097\5\b\5\2\u0093\u0094\7\t\2\2\u0094\u0096\5\b\5"+ + "\2\u0095\u0093\3\2\2\2\u0096\u0099\3\2\2\2\u0097\u0095\3\2\2\2\u0097\u0098"+ + "\3\2\2\2\u0098\r\3\2\2\2\u0099\u0097\3\2\2\2\u009a\u009d\5\22\n\2\u009b"+ + "\u009d\5\24\13\2\u009c\u009a\3\2\2\2\u009c\u009b\3\2\2\2\u009d\17\3\2"+ + "\2\2\u009e\u009f\7\66\2\2\u009f\21\3\2\2\2\u00a0\u00a1\t\b\2\2\u00a1\23"+ + "\3\2\2\2\u00a2\u00a3\t\t\2\2\u00a3\25\3\2\2\2\u00a4\u00a5\5\30\r\2\u00a5"+ + "\u00a6\5\34\17\2\u00a6\27\3\2\2\2\u00a7\u00a8\7\17\2\2\u00a8\u00ad\5\32"+ + "\16\2\u00a9\u00aa\7\t\2\2\u00aa\u00ac\5\32\16\2\u00ab\u00a9\3\2\2\2\u00ac"+ + "\u00af\3\2\2\2\u00ad\u00ab\3\2\2\2\u00ad\u00ae\3\2\2\2\u00ae\u00b0\3\2"+ + "\2\2\u00af\u00ad\3\2\2\2\u00b0\u00b1\7$\2\2\u00b1\31\3\2\2\2\u00b2\u00b7"+ + "\5 \21\2\u00b3\u00b4\7\13\2\2\u00b4\u00b6\7\5\2\2\u00b5\u00b3\3\2\2\2"+ + "\u00b6\u00b9\3\2\2\2\u00b7\u00b5\3\2\2\2\u00b7\u00b8\3\2\2\2\u00b8\u00c3"+ + "\3\2\2\2\u00b9\u00b7\3\2\2\2\u00ba\u00bf\5\"\22\2\u00bb\u00bc\7\13\2\2"+ + "\u00bc\u00be\7\5\2\2\u00bd\u00bb\3\2\2\2\u00be\u00c1\3\2\2\2\u00bf\u00bd"+ + "\3\2\2\2\u00bf\u00c0\3\2\2\2\u00c0\u00c3\3\2\2\2\u00c1\u00bf\3\2\2\2\u00c2"+ + "\u00b2\3\2\2\2\u00c2\u00ba\3\2\2\2\u00c3\33\3\2\2\2\u00c4\u00c5\7\66\2"+ + "\2\u00c5\u00c6\5\36\20\2\u00c6\35\3\2\2\2\u00c7\u00c9\7\r\2\2\u00c8\u00ca"+ + "\5\f\7\2\u00c9\u00c8\3\2\2\2\u00c9\u00ca\3\2\2\2\u00ca\u00cb\3\2\2\2\u00cb"+ + "\u00cc\7\36\2\2\u00cc\37\3\2\2\2\u00cd\u00cf\5\20\t\2\u00ce\u00d0\5\30"+ + "\r\2\u00cf\u00ce\3\2\2\2\u00cf\u00d0\3\2\2\2\u00d0\u00d8\3\2\2\2\u00d1"+ + "\u00d2\7\37\2\2\u00d2\u00d4\7\66\2\2\u00d3\u00d5\5\30\r\2\u00d4\u00d3"+ + "\3\2\2\2\u00d4\u00d5\3\2\2\2\u00d5\u00d7\3\2\2\2\u00d6\u00d1\3\2\2\2\u00d7"+ + "\u00da\3\2\2\2\u00d8\u00d6\3\2\2\2\u00d8\u00d9\3\2\2\2\u00d9!\3\2\2\2"+ + "\u00da\u00d8\3\2\2\2\u00db\u00dc\t\n\2\2\u00dc#\3\2\2\2\u00dd\u00df\7"+ + "8\2\2\u00de\u00e0\5&\24\2\u00df\u00de\3\2\2\2\u00df\u00e0\3\2\2\2\u00e0"+ + "%\3\2\2\2\u00e1\u00e2\7\r\2\2\u00e2\u00e3\5\f\7\2\u00e3\u00e4\7\36\2\2"+ + "\u00e4\'\3\2\2\2\24*\64H~\u0084\u0086\u0090\u0097\u009c\u00ad\u00b7\u00bf"+ + "\u00c2\u00c9\u00cf\u00d4\u00d8\u00df"; + public static final ATN _ATN = + new ATNDeserializer().deserialize(_serializedATN.toCharArray()); + static { + } +}
\ No newline at end of file diff --git a/tools/data-binding/grammarBuilder/src/main/java-gen/android/databinding/parser/BindingExpressionVisitor.java b/tools/data-binding/grammarBuilder/src/main/java-gen/android/databinding/parser/BindingExpressionVisitor.java new file mode 100644 index 0000000..d589a7d --- /dev/null +++ b/tools/data-binding/grammarBuilder/src/main/java-gen/android/databinding/parser/BindingExpressionVisitor.java @@ -0,0 +1,275 @@ +// Generated from BindingExpression.g4 by ANTLR 4.4 +package android.databinding.parser; +import org.antlr.v4.runtime.Token; +import org.antlr.v4.runtime.misc.NotNull; +import org.antlr.v4.runtime.tree.ParseTreeVisitor; + +/** + * This interface defines a complete generic visitor for a parse tree produced + * by {@link BindingExpressionParser}. + * + * @param <Result> The return type of the visit operation. Use {@link Void} for + * operations with no return type. + */ +public interface BindingExpressionVisitor<Result> extends ParseTreeVisitor<Result> { + /** + * Visit a parse tree produced by {@link BindingExpressionParser#expression}. + * @param ctx the parse tree + * @return the visitor result + */ + Result visitExpression(@NotNull BindingExpressionParser.ExpressionContext ctx); + + /** + * Visit a parse tree produced by {@link BindingExpressionParser#resources}. + * @param ctx the parse tree + * @return the visitor result + */ + Result visitResources(@NotNull BindingExpressionParser.ResourcesContext ctx); + + /** + * Visit a parse tree produced by the {@code BracketOp} + * labeled alternative in {@link BindingExpressionParser#expression}. + * @param ctx the parse tree + * @return the visitor result + */ + Result visitBracketOp(@NotNull BindingExpressionParser.BracketOpContext ctx); + + /** + * Visit a parse tree produced by the {@code UnaryOp} + * labeled alternative in {@link BindingExpressionParser#expression}. + * @param ctx the parse tree + * @return the visitor result + */ + Result visitUnaryOp(@NotNull BindingExpressionParser.UnaryOpContext ctx); + + /** + * Visit a parse tree produced by the {@code CastOp} + * labeled alternative in {@link BindingExpressionParser#expression}. + * @param ctx the parse tree + * @return the visitor result + */ + Result visitCastOp(@NotNull BindingExpressionParser.CastOpContext ctx); + + /** + * Visit a parse tree produced by {@link BindingExpressionParser#resourceParameters}. + * @param ctx the parse tree + * @return the visitor result + */ + Result visitResourceParameters(@NotNull BindingExpressionParser.ResourceParametersContext ctx); + + /** + * Visit a parse tree produced by the {@code AndOrOp} + * labeled alternative in {@link BindingExpressionParser#expression}. + * @param ctx the parse tree + * @return the visitor result + */ + Result visitAndOrOp(@NotNull BindingExpressionParser.AndOrOpContext ctx); + + /** + * Visit a parse tree produced by the {@code MethodInvocation} + * labeled alternative in {@link BindingExpressionParser#expression}. + * @param ctx the parse tree + * @return the visitor result + */ + Result visitMethodInvocation(@NotNull BindingExpressionParser.MethodInvocationContext ctx); + + /** + * Visit a parse tree produced by {@link BindingExpressionParser#expressionList}. + * @param ctx the parse tree + * @return the visitor result + */ + Result visitExpressionList(@NotNull BindingExpressionParser.ExpressionListContext ctx); + + /** + * Visit a parse tree produced by {@link BindingExpressionParser#classOrInterfaceType}. + * @param ctx the parse tree + * @return the visitor result + */ + Result visitClassOrInterfaceType(@NotNull BindingExpressionParser.ClassOrInterfaceTypeContext ctx); + + /** + * Visit a parse tree produced by {@link BindingExpressionParser#stringLiteral}. + * @param ctx the parse tree + * @return the visitor result + */ + Result visitStringLiteral(@NotNull BindingExpressionParser.StringLiteralContext ctx); + + /** + * Visit a parse tree produced by the {@code Primary} + * labeled alternative in {@link BindingExpressionParser#expression}. + * @param ctx the parse tree + * @return the visitor result + */ + Result visitPrimary(@NotNull BindingExpressionParser.PrimaryContext ctx); + + /** + * Visit a parse tree produced by {@link BindingExpressionParser#type}. + * @param ctx the parse tree + * @return the visitor result + */ + Result visitType(@NotNull BindingExpressionParser.TypeContext ctx); + + /** + * Visit a parse tree produced by {@link BindingExpressionParser#bindingSyntax}. + * @param ctx the parse tree + * @return the visitor result + */ + Result visitBindingSyntax(@NotNull BindingExpressionParser.BindingSyntaxContext ctx); + + /** + * Visit a parse tree produced by the {@code ComparisonOp} + * labeled alternative in {@link BindingExpressionParser#expression}. + * @param ctx the parse tree + * @return the visitor result + */ + Result visitComparisonOp(@NotNull BindingExpressionParser.ComparisonOpContext ctx); + + /** + * Visit a parse tree produced by the {@code TernaryOp} + * labeled alternative in {@link BindingExpressionParser#expression}. + * @param ctx the parse tree + * @return the visitor result + */ + Result visitTernaryOp(@NotNull BindingExpressionParser.TernaryOpContext ctx); + + /** + * Visit a parse tree produced by {@link BindingExpressionParser#constantValue}. + * @param ctx the parse tree + * @return the visitor result + */ + Result visitConstantValue(@NotNull BindingExpressionParser.ConstantValueContext ctx); + + /** + * Visit a parse tree produced by the {@code DotOp} + * labeled alternative in {@link BindingExpressionParser#expression}. + * @param ctx the parse tree + * @return the visitor result + */ + Result visitDotOp(@NotNull BindingExpressionParser.DotOpContext ctx); + + /** + * Visit a parse tree produced by {@link BindingExpressionParser#defaults}. + * @param ctx the parse tree + * @return the visitor result + */ + Result visitDefaults(@NotNull BindingExpressionParser.DefaultsContext ctx); + + /** + * Visit a parse tree produced by the {@code BitShiftOp} + * labeled alternative in {@link BindingExpressionParser#expression}. + * @param ctx the parse tree + * @return the visitor result + */ + Result visitBitShiftOp(@NotNull BindingExpressionParser.BitShiftOpContext ctx); + + /** + * Visit a parse tree produced by the {@code InstanceOfOp} + * labeled alternative in {@link BindingExpressionParser#expression}. + * @param ctx the parse tree + * @return the visitor result + */ + Result visitInstanceOfOp(@NotNull BindingExpressionParser.InstanceOfOpContext ctx); + + /** + * Visit a parse tree produced by the {@code BinaryOp} + * labeled alternative in {@link BindingExpressionParser#expression}. + * @param ctx the parse tree + * @return the visitor result + */ + Result visitBinaryOp(@NotNull BindingExpressionParser.BinaryOpContext ctx); + + /** + * Visit a parse tree produced by {@link BindingExpressionParser#explicitGenericInvocation}. + * @param ctx the parse tree + * @return the visitor result + */ + Result visitExplicitGenericInvocation(@NotNull BindingExpressionParser.ExplicitGenericInvocationContext ctx); + + /** + * Visit a parse tree produced by the {@code Resource} + * labeled alternative in {@link BindingExpressionParser#expression}. + * @param ctx the parse tree + * @return the visitor result + */ + Result visitResource(@NotNull BindingExpressionParser.ResourceContext ctx); + + /** + * Visit a parse tree produced by {@link BindingExpressionParser#typeArguments}. + * @param ctx the parse tree + * @return the visitor result + */ + Result visitTypeArguments(@NotNull BindingExpressionParser.TypeArgumentsContext ctx); + + /** + * Visit a parse tree produced by the {@code Grouping} + * labeled alternative in {@link BindingExpressionParser#expression}. + * @param ctx the parse tree + * @return the visitor result + */ + Result visitGrouping(@NotNull BindingExpressionParser.GroupingContext ctx); + + /** + * Visit a parse tree produced by the {@code MathOp} + * labeled alternative in {@link BindingExpressionParser#expression}. + * @param ctx the parse tree + * @return the visitor result + */ + Result visitMathOp(@NotNull BindingExpressionParser.MathOpContext ctx); + + /** + * Visit a parse tree produced by {@link BindingExpressionParser#classExtraction}. + * @param ctx the parse tree + * @return the visitor result + */ + Result visitClassExtraction(@NotNull BindingExpressionParser.ClassExtractionContext ctx); + + /** + * Visit a parse tree produced by {@link BindingExpressionParser#arguments}. + * @param ctx the parse tree + * @return the visitor result + */ + Result visitArguments(@NotNull BindingExpressionParser.ArgumentsContext ctx); + + /** + * Visit a parse tree produced by {@link BindingExpressionParser#primitiveType}. + * @param ctx the parse tree + * @return the visitor result + */ + Result visitPrimitiveType(@NotNull BindingExpressionParser.PrimitiveTypeContext ctx); + + /** + * Visit a parse tree produced by the {@code QuestionQuestionOp} + * labeled alternative in {@link BindingExpressionParser#expression}. + * @param ctx the parse tree + * @return the visitor result + */ + Result visitQuestionQuestionOp(@NotNull BindingExpressionParser.QuestionQuestionOpContext ctx); + + /** + * Visit a parse tree produced by {@link BindingExpressionParser#javaLiteral}. + * @param ctx the parse tree + * @return the visitor result + */ + Result visitJavaLiteral(@NotNull BindingExpressionParser.JavaLiteralContext ctx); + + /** + * Visit a parse tree produced by {@link BindingExpressionParser#explicitGenericInvocationSuffix}. + * @param ctx the parse tree + * @return the visitor result + */ + Result visitExplicitGenericInvocationSuffix(@NotNull BindingExpressionParser.ExplicitGenericInvocationSuffixContext ctx); + + /** + * Visit a parse tree produced by {@link BindingExpressionParser#identifier}. + * @param ctx the parse tree + * @return the visitor result + */ + Result visitIdentifier(@NotNull BindingExpressionParser.IdentifierContext ctx); + + /** + * Visit a parse tree produced by {@link BindingExpressionParser#literal}. + * @param ctx the parse tree + * @return the visitor result + */ + Result visitLiteral(@NotNull BindingExpressionParser.LiteralContext ctx); +}
\ No newline at end of file diff --git a/tools/data-binding/grammarBuilder/src/main/java/com/android/databinder/parser/Main.java b/tools/data-binding/grammarBuilder/src/main/java/com/android/databinder/parser/Main.java new file mode 100644 index 0000000..ba5c395 --- /dev/null +++ b/tools/data-binding/grammarBuilder/src/main/java/com/android/databinder/parser/Main.java @@ -0,0 +1,38 @@ +/* + * Copyright (C) 2014 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.databinder.parser; + +import org.antlr.v4.runtime.ANTLRInputStream; +import org.antlr.v4.runtime.CommonTokenStream; +import org.antlr.v4.runtime.tree.ParseTreeWalker; + +public class Main { + static String input = "`name` + last_name"; + static class Field { + String fieldName; + } + public static void main(String[] args) { + // ANTLRInputStream inputStream = new ANTLRInputStream(input); +// DataBinderLexer lexer = new DataBinderLexer(inputStream); +// CommonTokenStream tokenStream = new CommonTokenStream(lexer); +// DataBinderParser parser = new DataBinderParser(tokenStream); +// ParseTreeWalker walker = new ParseTreeWalker(); +// System.out.println(parser.expr().toStringTree(parser)); + float[] aa = new float[2]; + + } +} diff --git a/tools/data-binding/grammarBuilder/src/test/java/android/databinding/BindingExpressionParserTest.java b/tools/data-binding/grammarBuilder/src/test/java/android/databinding/BindingExpressionParserTest.java new file mode 100644 index 0000000..9197ccf --- /dev/null +++ b/tools/data-binding/grammarBuilder/src/test/java/android/databinding/BindingExpressionParserTest.java @@ -0,0 +1,371 @@ +package android.databinding; + +import android.databinding.parser.BindingExpressionLexer; +import android.databinding.parser.BindingExpressionParser; +import android.databinding.parser.BindingExpressionParser.AndOrOpContext; +import android.databinding.parser.BindingExpressionParser.BinaryOpContext; +import android.databinding.parser.BindingExpressionParser.BindingSyntaxContext; +import android.databinding.parser.BindingExpressionParser.BitShiftOpContext; +import android.databinding.parser.BindingExpressionParser.ComparisonOpContext; +import android.databinding.parser.BindingExpressionParser.DefaultsContext; +import android.databinding.parser.BindingExpressionParser.DotOpContext; +import android.databinding.parser.BindingExpressionParser.ExpressionContext; +import android.databinding.parser.BindingExpressionParser.GroupingContext; +import android.databinding.parser.BindingExpressionParser.LiteralContext; +import android.databinding.parser.BindingExpressionParser.MathOpContext; +import android.databinding.parser.BindingExpressionParser.PrimaryContext; +import android.databinding.parser.BindingExpressionParser.PrimitiveTypeContext; +import android.databinding.parser.BindingExpressionParser.QuestionQuestionOpContext; +import android.databinding.parser.BindingExpressionParser.ResourceContext; +import android.databinding.parser.BindingExpressionParser.StringLiteralContext; +import android.databinding.parser.BindingExpressionParser.TernaryOpContext; +import android.databinding.parser.BindingExpressionParser.UnaryOpContext; + +import org.antlr.v4.runtime.ANTLRInputStream; +import org.antlr.v4.runtime.CommonTokenStream; +import org.antlr.v4.runtime.Token; +import org.antlr.v4.runtime.tree.TerminalNode; +import org.junit.Test; + +import java.io.StringReader; + +import static org.junit.Assert.assertEquals; +import static org.junit.Assert.assertNotNull; +import static org.junit.Assert.assertNull; +import static org.junit.Assert.assertTrue; + +public class BindingExpressionParserTest { + + @Test + public void testSingleQuoteStringLiteral() throws Exception { + String expr = "`test`"; + LiteralContext literal = parseLiteral(expr); + assertNotNull(literal); + StringLiteralContext stringLiteral = literal.stringLiteral(); + assertNotNull(stringLiteral); + TerminalNode singleQuote = stringLiteral.SingleQuoteString(); + Token token = singleQuote.getSymbol(); + assertEquals("`test`", token.getText()); + } + + @Test + public void testDoubleQuoteStringLiteral() throws Exception { + String expr = "\"test\""; + + LiteralContext literal = parseLiteral(expr); + StringLiteralContext stringLiteral = literal.stringLiteral(); + TerminalNode singleQuote = stringLiteral.DoubleQuoteString(); + Token token = singleQuote.getSymbol(); + assertEquals("\"test\"", token.getText()); + } + + @Test + public void testSingleQuoteEscapeStringLiteral() throws Exception { + String expr = "`\"t\\`est\"`"; + LiteralContext literal = parseLiteral(expr); + StringLiteralContext stringLiteral = literal.stringLiteral(); + TerminalNode singleQuote = stringLiteral.SingleQuoteString(); + Token token = singleQuote.getSymbol(); + assertEquals("`\"t\\`est\"`", token.getText()); + } + + @Test + public void testCharLiteral() throws Exception { + LiteralContext literal = parseLiteral("'c'"); + assertEquals("'c'", literal.getText()); + literal = parseLiteral("'\\u0054'"); + assertEquals("'\\u0054'", literal.getText()); + literal = parseLiteral("'\\''"); + assertEquals("'\\''", literal.getText()); + } + + @Test + public void testIntLiterals() throws Exception { + compareIntLiteral("123"); + compareIntLiteral("123l"); + compareIntLiteral("1_2_3l"); + compareIntLiteral("123L"); + compareIntLiteral("0xdeadbeef"); + compareIntLiteral("0xdeadbeefl"); + compareIntLiteral("0Xdeadbeef"); + compareIntLiteral("0xdead_beefl"); + compareIntLiteral("0xdead_beefL"); + compareIntLiteral("01234567"); + compareIntLiteral("01234567L"); + compareIntLiteral("01234567l"); + compareIntLiteral("0123_45_67l"); + compareIntLiteral("0b0101"); + compareIntLiteral("0b0101_0101"); + compareIntLiteral("0B0101_0101"); + compareIntLiteral("0B0101_0101L"); + compareIntLiteral("0B0101_0101l"); + } + + @Test + public void testFloatLiterals() throws Exception { + compareFloatLiteral("0.12345"); + compareFloatLiteral("0.12345f"); + compareFloatLiteral("0.12345F"); + compareFloatLiteral("132450.12345F"); + compareFloatLiteral("132450.12345"); + compareFloatLiteral("132450e123"); + compareFloatLiteral("132450.4e123"); + } + + @Test + public void testBoolLiterals() throws Exception { + compareBoolLiteral("true"); + compareBoolLiteral("false"); + } + + @Test + public void testNullLiteral() throws Exception { + LiteralContext literal = parseLiteral("null"); + String token = literal.getText(); + assertEquals("null", token); + } + + @Test + public void testVoidExtraction() throws Exception { + PrimaryContext primary = parsePrimary("void.class"); + assertNotNull(primary.classExtraction()); + assertNull(primary.classExtraction().type()); + assertEquals("void", primary.classExtraction().getChild(0).getText()); + } + + @Test + public void testPrimitiveClassExtraction() throws Exception { + PrimaryContext primary = parsePrimary("int.class"); + PrimitiveTypeContext type = primary.classExtraction().type().primitiveType(); + assertEquals("int", type.getText()); + } + + @Test + public void testIdentifier() throws Exception { + PrimaryContext primary = parsePrimary("abcdEfg"); + assertEquals("abcdEfg", primary.identifier().getText()); + } + + @Test + public void testUnaryOperators() throws Exception { + compareUnaryOperators("+"); + compareUnaryOperators("-"); + compareUnaryOperators("!"); + compareUnaryOperators("~"); + } + + @Test + public void testMathOperators() throws Exception { + compareMathOperators("+"); + compareMathOperators("-"); + compareMathOperators("*"); + compareMathOperators("/"); + compareMathOperators("%"); + } + + @Test + public void testBitShiftOperators() throws Exception { + compareBitShiftOperators(">>>"); + compareBitShiftOperators("<<"); + compareBitShiftOperators(">>"); + } + + @Test + public void testComparisonShiftOperators() throws Exception { + compareComparisonOperators("<"); + compareComparisonOperators(">"); + compareComparisonOperators("<="); + compareComparisonOperators(">="); + compareComparisonOperators("=="); + compareComparisonOperators("!="); + } + + @Test + public void testAndOrOperators() throws Exception { + compareAndOrOperators("&&"); + compareAndOrOperators("||"); + } + + @Test + public void testBinaryOperators() throws Exception { + compareBinaryOperators("&"); + compareBinaryOperators("|"); + compareBinaryOperators("^"); + } + + @Test + public void testTernaryOperator() throws Exception { + TernaryOpContext expression = parseExpression("true ? 1 : 0"); + assertEquals(5, expression.getChildCount()); + assertEquals("true", + ((PrimaryContext) expression.left).literal().javaLiteral().getText()); + assertEquals("?", expression.op.getText()); + assertEquals("1", + ((PrimaryContext) expression.iftrue).literal().javaLiteral().getText()); + assertEquals(":", expression.getChild(3).getText()); + assertEquals("0", ((PrimaryContext) expression.iffalse).literal().javaLiteral().getText()); + } + + @Test + public void testDot() throws Exception { + DotOpContext expression = parseExpression("one.two.three"); + assertEquals(3, expression.getChildCount()); + assertEquals("three", expression.Identifier().getText()); + assertEquals(".", expression.getChild(1).getText()); + DotOpContext left = (DotOpContext) expression.expression(); + assertEquals("two", left.Identifier().getText()); + assertEquals(".", left.getChild(1).getText()); + assertEquals("one", ((PrimaryContext) left.expression()).identifier().getText()); + } + + @Test + public void testQuestionQuestion() throws Exception { + QuestionQuestionOpContext expression = parseExpression("one ?? two"); + assertEquals(3, expression.getChildCount()); + assertEquals("one", ((PrimaryContext) expression.left).identifier().getText()); + assertEquals("two", ((PrimaryContext) expression.right).identifier().getText()); + assertEquals("??", expression.op.getText()); + } + + @Test + public void testResourceReference() throws Exception { + compareResource("@id/foo_bar"); + compareResource("@transition/foo_bar"); + compareResource("@anim/foo_bar"); + compareResource("@animator/foo_bar"); + compareResource("@android:id/foo_bar"); + compareResource("@app:id/foo_bar"); + } + + @Test + public void testDefaults() throws Exception { + BindingSyntaxContext syntax = parseExpressionString("foo.bar, default = @id/foo_bar"); + DefaultsContext defaults = syntax.defaults(); + assertEquals("@id/foo_bar", defaults.constantValue().ResourceReference().getText()); + } + + @Test + public void testParentheses() throws Exception { + GroupingContext grouping = parseExpression("(1234)"); + assertEquals("1234", grouping.expression().getText()); + } + + // ---------------------- Helpers -------------------- + + private void compareResource(String value) throws Exception { + ResourceContext resourceContext = parseExpression(value); + assertEquals(value, resourceContext.getText()); + } + + private void compareUnaryOperators(String op) throws Exception { + UnaryOpContext expression = parseExpression(op + " 2"); + assertEquals(2, expression.getChildCount()); + assertEquals(op, expression.op.getText()); + assertEquals("2", + ((PrimaryContext) expression.expression()).literal().javaLiteral() + .getText()); + } + + private void compareBinaryOperators(String op) throws Exception { + BinaryOpContext expression = parseExpression("1 " + op + " 2"); + assertEquals(3, expression.getChildCount()); + assertTrue(expression.left instanceof ExpressionContext); + String one = ((PrimaryContext) expression.left).literal().javaLiteral().getText(); + assertEquals("1", one); + assertEquals(op, expression.op.getText()); + assertTrue(expression.right instanceof ExpressionContext); + String two = ((PrimaryContext) expression.right).literal().javaLiteral().getText(); + assertEquals("2", two); + } + + private void compareMathOperators(String op) throws Exception { + MathOpContext expression = parseExpression("1 " + op + " 2"); + assertEquals(3, expression.getChildCount()); + assertTrue(expression.left instanceof ExpressionContext); + String one = ((PrimaryContext) expression.left).literal().javaLiteral().getText(); + assertEquals("1", one); + assertEquals(op, expression.op.getText()); + assertTrue(expression.right instanceof ExpressionContext); + String two = ((PrimaryContext) expression.right).literal().javaLiteral().getText(); + assertEquals("2", two); + } + + private void compareBitShiftOperators(String op) throws Exception { + BitShiftOpContext expression = parseExpression("1 " + op + " 2"); + assertEquals(3, expression.getChildCount()); + assertTrue(expression.left instanceof ExpressionContext); + String one = ((PrimaryContext) expression.left).literal().javaLiteral().getText(); + assertEquals("1", one); + assertEquals(op, expression.op.getText()); + assertTrue(expression.right instanceof ExpressionContext); + String two = ((PrimaryContext) expression.right).literal().javaLiteral().getText(); + assertEquals("2", two); + } + + private void compareComparisonOperators(String op) throws Exception { + ComparisonOpContext expression = parseExpression("1 " + op + " 2"); + assertEquals(3, expression.getChildCount()); + assertTrue(expression.left instanceof ExpressionContext); + String one = ((PrimaryContext) expression.left).literal().javaLiteral().getText(); + assertEquals("1", one); + assertEquals(op, expression.op.getText()); + assertTrue(expression.right instanceof ExpressionContext); + String two = ((PrimaryContext) expression.right).literal().javaLiteral().getText(); + assertEquals("2", two); + } + + private void compareAndOrOperators(String op) throws Exception { + AndOrOpContext expression = parseExpression("1 " + op + " 2"); + assertEquals(3, expression.getChildCount()); + assertTrue(expression.left instanceof ExpressionContext); + String one = ((PrimaryContext) expression.left).literal().javaLiteral().getText(); + assertEquals("1", one); + assertEquals(op, expression.op.getText()); + assertTrue(expression.right instanceof ExpressionContext); + String two = ((PrimaryContext) expression.right).literal().javaLiteral().getText(); + assertEquals("2", two); + } + + private void compareIntLiteral(String constant) throws Exception { + LiteralContext literal = parseLiteral(constant); + String token = literal.javaLiteral().getText(); + assertEquals(constant, token); + } + + private void compareFloatLiteral(String constant) throws Exception { + LiteralContext literal = parseLiteral(constant); + String token = literal.javaLiteral().getText(); + assertEquals(constant, token); + } + + private void compareBoolLiteral(String constant) throws Exception { + LiteralContext literal = parseLiteral(constant); + String token = literal.javaLiteral().getText(); + assertEquals(constant, token); + } + + private BindingSyntaxContext parse(String value) throws Exception { + return parseExpressionString(value); + } + + private <T extends ExpressionContext> T parseExpression(String value) throws Exception { + ExpressionContext expressionContext = parse(value).expression(); + return (T) expressionContext; + } + + private PrimaryContext parsePrimary(String value) throws Exception { + return parseExpression(value); + } + + private LiteralContext parseLiteral(String value) throws Exception { + return parsePrimary(value).literal(); + } + + BindingExpressionParser.BindingSyntaxContext parseExpressionString(String s) throws Exception { + ANTLRInputStream input = new ANTLRInputStream(new StringReader(s)); + BindingExpressionLexer lexer = new BindingExpressionLexer(input); + CommonTokenStream tokens = new CommonTokenStream(lexer); + BindingExpressionParser parser = new BindingExpressionParser(tokens); + return parser.bindingSyntax(); + } +} diff --git a/tools/data-binding/integration-tests/IndependentLibrary/app/.gitignore b/tools/data-binding/integration-tests/IndependentLibrary/app/.gitignore new file mode 100644 index 0000000..796b96d --- /dev/null +++ b/tools/data-binding/integration-tests/IndependentLibrary/app/.gitignore @@ -0,0 +1 @@ +/build diff --git a/tools/data-binding/integration-tests/IndependentLibrary/app/build.gradle b/tools/data-binding/integration-tests/IndependentLibrary/app/build.gradle new file mode 100644 index 0000000..552e1a5 --- /dev/null +++ b/tools/data-binding/integration-tests/IndependentLibrary/app/build.gradle @@ -0,0 +1,60 @@ +/* + * 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. + */ +apply plugin: 'maven' +apply plugin: 'com.android.library' +apply plugin: 'com.android.databinding' + +android { + compileSdkVersion 21 + buildToolsVersion "21.1.2" + + defaultConfig { + minSdkVersion 7 + targetSdkVersion 21 + versionCode 1 + versionName "1.0" + } + buildTypes { + release { + minifyEnabled false + proguardFiles getDefaultProguardFile('proguard-android.txt'), 'proguard-rules.pro' + } + } + + packagingOptions { + exclude 'META-INF/services/javax.annotation.processing.Processor' + exclude 'META-INF/LICENSE.txt' + exclude 'META-INF/NOTICE.txt' + } +} + +dependencies { + compile fileTree(dir: 'libs', include: ['*.jar']) + compile "com.android.databinding:library:${config.snapshotVersion}" + provided "com.android.databinding:annotationprocessor:${config.snapshotVersion}" +} + +uploadArchives { + repositories { + mavenDeployer { + repository(url: "file://${config.mavenRepoDir}") + pom.artifactId = 'independent-library' + pom.version = config.snapshotVersion + pom.groupId = config.testGroup + } + } +} + +connectedCheck.dependsOn uploadArchives
\ No newline at end of file diff --git a/tools/data-binding/integration-tests/IndependentLibrary/app/proguard-rules.pro b/tools/data-binding/integration-tests/IndependentLibrary/app/proguard-rules.pro new file mode 100644 index 0000000..b7210d1 --- /dev/null +++ b/tools/data-binding/integration-tests/IndependentLibrary/app/proguard-rules.pro @@ -0,0 +1,17 @@ +# Add project specific ProGuard rules here. +# By default, the flags in this file are appended to flags specified +# in /Users/yboyar/android/sdk/tools/proguard/proguard-android.txt +# You can edit the include path and order by changing the proguardFiles +# directive in build.gradle. +# +# For more details, see +# http://developer.android.com/guide/developing/tools/proguard.html + +# Add any project specific keep options here: + +# If your project uses WebView with JS, uncomment the following +# and specify the fully qualified class name to the JavaScript interface +# class: +#-keepclassmembers class fqcn.of.javascript.interface.for.webview { +# public *; +#} diff --git a/tools/data-binding/integration-tests/IndependentLibrary/app/src/androidTest/java/android/databinding/test/independentlibrary/ApplicationTest.java b/tools/data-binding/integration-tests/IndependentLibrary/app/src/androidTest/java/android/databinding/test/independentlibrary/ApplicationTest.java new file mode 100644 index 0000000..a75974f --- /dev/null +++ b/tools/data-binding/integration-tests/IndependentLibrary/app/src/androidTest/java/android/databinding/test/independentlibrary/ApplicationTest.java @@ -0,0 +1,29 @@ +/* + * 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 android.databinding.test.independentlibrary; + +import android.app.Application; +import android.test.ApplicationTestCase; + +/** + * <a href="http://d.android.com/tools/testing/testing_android.html">Testing Fundamentals</a> + */ +public class ApplicationTest extends ApplicationTestCase<Application> { + + public ApplicationTest() { + super(Application.class); + } +} diff --git a/tools/data-binding/integration-tests/IndependentLibrary/app/src/androidTest/java/android/databinding/test/independentlibrary/LibraryActivityTest.java b/tools/data-binding/integration-tests/IndependentLibrary/app/src/androidTest/java/android/databinding/test/independentlibrary/LibraryActivityTest.java new file mode 100644 index 0000000..2672186 --- /dev/null +++ b/tools/data-binding/integration-tests/IndependentLibrary/app/src/androidTest/java/android/databinding/test/independentlibrary/LibraryActivityTest.java @@ -0,0 +1,41 @@ +/* + * 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 android.databinding.test.independentlibrary; + +import android.test.ActivityInstrumentationTestCase2; +import android.view.View; +import android.widget.TextView; + +public class LibraryActivityTest extends ActivityInstrumentationTestCase2<LibraryActivity> { + public LibraryActivityTest() { + super(LibraryActivity.class); + } + + public void testTextViewContents() throws Throwable { + final LibraryActivity activity = getActivity(); + assertNotNull("test sanity", activity); + runTestOnUiThread(new Runnable() { + @Override + public void run() { + TextView textView = (TextView) activity.findViewById(R.id.fooTextView); + final String expected = LibraryActivity.FIELD_VALUE + " " + + LibraryActivity.FIELD_VALUE; + assertEquals(expected, textView.getText().toString()); + } + }); + } +} diff --git a/tools/data-binding/integration-tests/IndependentLibrary/app/src/main/AndroidManifest.xml b/tools/data-binding/integration-tests/IndependentLibrary/app/src/main/AndroidManifest.xml new file mode 100644 index 0000000..8b26ea7 --- /dev/null +++ b/tools/data-binding/integration-tests/IndependentLibrary/app/src/main/AndroidManifest.xml @@ -0,0 +1,24 @@ +<!-- + ~ 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. + --> + +<manifest xmlns:android="http://schemas.android.com/apk/res/android" + package="android.databinding.test.independentlibrary"> + + <application android:allowBackup="true" + android:label="@string/app_name"> + <activity android:name=".LibraryActivity"/> + </application> + +</manifest> diff --git a/tools/data-binding/integration-tests/IndependentLibrary/app/src/main/java/android/databinding/test/independentlibrary/LibraryActivity.java b/tools/data-binding/integration-tests/IndependentLibrary/app/src/main/java/android/databinding/test/independentlibrary/LibraryActivity.java new file mode 100644 index 0000000..18b7bec --- /dev/null +++ b/tools/data-binding/integration-tests/IndependentLibrary/app/src/main/java/android/databinding/test/independentlibrary/LibraryActivity.java @@ -0,0 +1,36 @@ +/* + * 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 android.databinding.test.independentlibrary; + +import android.app.Activity; +import android.os.Bundle; + +import android.databinding.test.independentlibrary.vo.MyBindableObject; +import android.databinding.test.independentlibrary.databinding.LibraryLayoutBinding; +public class LibraryActivity extends Activity { + public static final String FIELD_VALUE = "BAR"; + @Override + protected void onCreate(Bundle savedInstanceState) { + super.onCreate(savedInstanceState); + LibraryLayoutBinding binding = LibraryLayoutBinding.inflate(this); + setContentView(binding.getRoot()); + MyBindableObject object = new MyBindableObject(); + object.setField(FIELD_VALUE); + binding.setFoo(object); + binding.executePendingBindings(); + } +} diff --git a/tools/data-binding/integration-tests/IndependentLibrary/app/src/main/java/android/databinding/test/independentlibrary/LibraryAdapter.java b/tools/data-binding/integration-tests/IndependentLibrary/app/src/main/java/android/databinding/test/independentlibrary/LibraryAdapter.java new file mode 100644 index 0000000..e358e62 --- /dev/null +++ b/tools/data-binding/integration-tests/IndependentLibrary/app/src/main/java/android/databinding/test/independentlibrary/LibraryAdapter.java @@ -0,0 +1,26 @@ +/* + * 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 android.databinding.test.independentlibrary; + +import android.databinding.BindingAdapter; +import android.view.View; + +public class LibraryAdapter { + @BindingAdapter("myTagAttr") + public static void set(View view, String someTag) { + view.setTag(someTag); + } +} diff --git a/tools/data-binding/integration-tests/IndependentLibrary/app/src/main/java/android/databinding/test/independentlibrary/vo/MyBindableObject.java b/tools/data-binding/integration-tests/IndependentLibrary/app/src/main/java/android/databinding/test/independentlibrary/vo/MyBindableObject.java new file mode 100644 index 0000000..da66cef --- /dev/null +++ b/tools/data-binding/integration-tests/IndependentLibrary/app/src/main/java/android/databinding/test/independentlibrary/vo/MyBindableObject.java @@ -0,0 +1,36 @@ +/* + * 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 android.databinding.test.independentlibrary.vo; + +import android.databinding.BaseObservable; +import android.databinding.test.independentlibrary.BR; + +import android.databinding.Bindable; + +public class MyBindableObject extends BaseObservable { + @Bindable + private String mField; + + public String getField() { + return mField; + } + + public void setField(String field) { + mField = field; + notifyPropertyChanged(BR.field); + } +} diff --git a/tools/data-binding/integration-tests/IndependentLibrary/app/src/main/res/layout/library_layout.xml b/tools/data-binding/integration-tests/IndependentLibrary/app/src/main/res/layout/library_layout.xml new file mode 100644 index 0000000..4262eb3 --- /dev/null +++ b/tools/data-binding/integration-tests/IndependentLibrary/app/src/main/res/layout/library_layout.xml @@ -0,0 +1,25 @@ +<?xml version="1.0" encoding="utf-8"?> +<!-- + ~ 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. + --> + +<LinearLayout xmlns:android="http://schemas.android.com/apk/res/android" + android:orientation="vertical" + android:layout_width="match_parent" + android:layout_height="match_parent"> + <variable name="foo" type="android.databinding.test.independentlibrary.vo.MyBindableObject"/> + <TextView android:layout_width="wrap_content" android:layout_height="wrap_content" + android:id="@+id/fooTextView" + android:text='@{foo.field + " " + foo.field}'/> +</LinearLayout> diff --git a/tools/data-binding/integration-tests/IndependentLibrary/app/src/main/res/values/strings.xml b/tools/data-binding/integration-tests/IndependentLibrary/app/src/main/res/values/strings.xml new file mode 100644 index 0000000..8e6caf7 --- /dev/null +++ b/tools/data-binding/integration-tests/IndependentLibrary/app/src/main/res/values/strings.xml @@ -0,0 +1,18 @@ +<!-- + ~ 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. + --> + +<resources> + <string name="app_name">IndependentLibrary</string> +</resources> diff --git a/tools/data-binding/integration-tests/IndependentLibrary/build.gradle b/tools/data-binding/integration-tests/IndependentLibrary/build.gradle new file mode 100644 index 0000000..d74b7e6 --- /dev/null +++ b/tools/data-binding/integration-tests/IndependentLibrary/build.gradle @@ -0,0 +1,44 @@ +/* + * 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. + */ + +buildscript { + def Properties dataBindingProperties = new Properties() + dataBindingProperties.load(new FileInputStream("${projectDir}/../../databinding.properties")) + dataBindingProperties.mavenRepoDir = "${projectDir}/../../${dataBindingProperties.mavenRepoName}" + ext.config = dataBindingProperties + println "loaded config" + + repositories { + jcenter() + maven { + url config.mavenRepoDir + } + } + dependencies { + classpath 'com.android.tools.build:gradle:1.1.3' + classpath "com.android.databinding:dataBinder:${config.snapshotVersion}" + // NOTE: Do not place your application dependencies here; they belong + // in the individual module build.gradle files + } +} + +allprojects { + repositories { + jcenter() + maven { + url config.mavenRepoDir + } + } +} diff --git a/tools/data-binding/integration-tests/IndependentLibrary/gradle.properties b/tools/data-binding/integration-tests/IndependentLibrary/gradle.properties new file mode 100644 index 0000000..efd2362 --- /dev/null +++ b/tools/data-binding/integration-tests/IndependentLibrary/gradle.properties @@ -0,0 +1,33 @@ +# +# 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. +# + +# Project-wide Gradle settings. + +# IDE (e.g. Android Studio) users: +# Gradle settings configured through the IDE *will override* +# any settings specified in this file. + +# For more details on how to configure your build environment visit +# http://www.gradle.org/docs/current/userguide/build_environment.html + +# Specifies the JVM arguments used for the daemon process. +# The setting is particularly useful for tweaking memory settings. +# Default value: -Xmx10248m -XX:MaxPermSize=256m +# org.gradle.jvmargs=-Xmx2048m -XX:MaxPermSize=512m -XX:+HeapDumpOnOutOfMemoryError -Dfile.encoding=UTF-8 + +# When configured, Gradle will run in incubating parallel mode. +# This option should only be used with decoupled projects. More details, visit +# http://www.gradle.org/docs/current/userguide/multi_project_builds.html#sec:decoupled_projects +# org.gradle.parallel=true
\ No newline at end of file diff --git a/tools/data-binding/integration-tests/IndependentLibrary/gradle/wrapper/gradle-wrapper.jar b/tools/data-binding/integration-tests/IndependentLibrary/gradle/wrapper/gradle-wrapper.jar Binary files differnew file mode 100644 index 0000000..8c0fb64 --- /dev/null +++ b/tools/data-binding/integration-tests/IndependentLibrary/gradle/wrapper/gradle-wrapper.jar diff --git a/tools/data-binding/integration-tests/IndependentLibrary/gradle/wrapper/gradle-wrapper.properties b/tools/data-binding/integration-tests/IndependentLibrary/gradle/wrapper/gradle-wrapper.properties new file mode 100644 index 0000000..de86a57 --- /dev/null +++ b/tools/data-binding/integration-tests/IndependentLibrary/gradle/wrapper/gradle-wrapper.properties @@ -0,0 +1,21 @@ +# +# 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. +# + +#Wed Apr 10 15:27:10 PDT 2013 +distributionBase=GRADLE_USER_HOME +distributionPath=wrapper/dists +zipStoreBase=GRADLE_USER_HOME +zipStorePath=wrapper/dists +distributionUrl=https\://services.gradle.org/distributions/gradle-2.2.1-all.zip diff --git a/tools/data-binding/integration-tests/IndependentLibrary/gradlew b/tools/data-binding/integration-tests/IndependentLibrary/gradlew new file mode 100755 index 0000000..91a7e26 --- /dev/null +++ b/tools/data-binding/integration-tests/IndependentLibrary/gradlew @@ -0,0 +1,164 @@ +#!/usr/bin/env bash + +############################################################################## +## +## Gradle start up script for UN*X +## +############################################################################## + +# Add default JVM options here. You can also use JAVA_OPTS and GRADLE_OPTS to pass JVM options to this script. +DEFAULT_JVM_OPTS="" + +APP_NAME="Gradle" +APP_BASE_NAME=`basename "$0"` + +# Use the maximum available, or set MAX_FD != -1 to use that value. +MAX_FD="maximum" + +warn ( ) { + echo "$*" +} + +die ( ) { + echo + echo "$*" + echo + exit 1 +} + +# OS specific support (must be 'true' or 'false'). +cygwin=false +msys=false +darwin=false +case "`uname`" in + CYGWIN* ) + cygwin=true + ;; + Darwin* ) + darwin=true + ;; + MINGW* ) + msys=true + ;; +esac + +# For Cygwin, ensure paths are in UNIX format before anything is touched. +if $cygwin ; then + [ -n "$JAVA_HOME" ] && JAVA_HOME=`cygpath --unix "$JAVA_HOME"` +fi + +# Attempt to set APP_HOME +# Resolve links: $0 may be a link +PRG="$0" +# Need this for relative symlinks. +while [ -h "$PRG" ] ; do + ls=`ls -ld "$PRG"` + link=`expr "$ls" : '.*-> \(.*\)$'` + if expr "$link" : '/.*' > /dev/null; then + PRG="$link" + else + PRG=`dirname "$PRG"`"/$link" + fi +done +SAVED="`pwd`" +cd "`dirname \"$PRG\"`/" >&- +APP_HOME="`pwd -P`" +cd "$SAVED" >&- + +CLASSPATH=$APP_HOME/gradle/wrapper/gradle-wrapper.jar + +# Determine the Java command to use to start the JVM. +if [ -n "$JAVA_HOME" ] ; then + if [ -x "$JAVA_HOME/jre/sh/java" ] ; then + # IBM's JDK on AIX uses strange locations for the executables + JAVACMD="$JAVA_HOME/jre/sh/java" + else + JAVACMD="$JAVA_HOME/bin/java" + fi + if [ ! -x "$JAVACMD" ] ; then + die "ERROR: JAVA_HOME is set to an invalid directory: $JAVA_HOME + +Please set the JAVA_HOME variable in your environment to match the +location of your Java installation." + fi +else + JAVACMD="java" + which java >/dev/null 2>&1 || die "ERROR: JAVA_HOME is not set and no 'java' command could be found in your PATH. + +Please set the JAVA_HOME variable in your environment to match the +location of your Java installation." +fi + +# Increase the maximum file descriptors if we can. +if [ "$cygwin" = "false" -a "$darwin" = "false" ] ; then + MAX_FD_LIMIT=`ulimit -H -n` + if [ $? -eq 0 ] ; then + if [ "$MAX_FD" = "maximum" -o "$MAX_FD" = "max" ] ; then + MAX_FD="$MAX_FD_LIMIT" + fi + ulimit -n $MAX_FD + if [ $? -ne 0 ] ; then + warn "Could not set maximum file descriptor limit: $MAX_FD" + fi + else + warn "Could not query maximum file descriptor limit: $MAX_FD_LIMIT" + fi +fi + +# For Darwin, add options to specify how the application appears in the dock +if $darwin; then + GRADLE_OPTS="$GRADLE_OPTS \"-Xdock:name=$APP_NAME\" \"-Xdock:icon=$APP_HOME/media/gradle.icns\"" +fi + +# For Cygwin, switch paths to Windows format before running java +if $cygwin ; then + APP_HOME=`cygpath --path --mixed "$APP_HOME"` + CLASSPATH=`cygpath --path --mixed "$CLASSPATH"` + + # We build the pattern for arguments to be converted via cygpath + ROOTDIRSRAW=`find -L / -maxdepth 1 -mindepth 1 -type d 2>/dev/null` + SEP="" + for dir in $ROOTDIRSRAW ; do + ROOTDIRS="$ROOTDIRS$SEP$dir" + SEP="|" + done + OURCYGPATTERN="(^($ROOTDIRS))" + # Add a user-defined pattern to the cygpath arguments + if [ "$GRADLE_CYGPATTERN" != "" ] ; then + OURCYGPATTERN="$OURCYGPATTERN|($GRADLE_CYGPATTERN)" + fi + # Now convert the arguments - kludge to limit ourselves to /bin/sh + i=0 + for arg in "$@" ; do + CHECK=`echo "$arg"|egrep -c "$OURCYGPATTERN" -` + CHECK2=`echo "$arg"|egrep -c "^-"` ### Determine if an option + + if [ $CHECK -ne 0 ] && [ $CHECK2 -eq 0 ] ; then ### Added a condition + eval `echo args$i`=`cygpath --path --ignore --mixed "$arg"` + else + eval `echo args$i`="\"$arg\"" + fi + i=$((i+1)) + done + case $i in + (0) set -- ;; + (1) set -- "$args0" ;; + (2) set -- "$args0" "$args1" ;; + (3) set -- "$args0" "$args1" "$args2" ;; + (4) set -- "$args0" "$args1" "$args2" "$args3" ;; + (5) set -- "$args0" "$args1" "$args2" "$args3" "$args4" ;; + (6) set -- "$args0" "$args1" "$args2" "$args3" "$args4" "$args5" ;; + (7) set -- "$args0" "$args1" "$args2" "$args3" "$args4" "$args5" "$args6" ;; + (8) set -- "$args0" "$args1" "$args2" "$args3" "$args4" "$args5" "$args6" "$args7" ;; + (9) set -- "$args0" "$args1" "$args2" "$args3" "$args4" "$args5" "$args6" "$args7" "$args8" ;; + esac +fi + +# Split up the JVM_OPTS And GRADLE_OPTS values into an array, following the shell quoting and substitution rules +function splitJvmOpts() { + JVM_OPTS=("$@") +} +eval splitJvmOpts $DEFAULT_JVM_OPTS $JAVA_OPTS $GRADLE_OPTS +JVM_OPTS[${#JVM_OPTS[*]}]="-Dorg.gradle.appname=$APP_BASE_NAME" + +exec "$JAVACMD" "${JVM_OPTS[@]}" -classpath "$CLASSPATH" org.gradle.wrapper.GradleWrapperMain "$@" diff --git a/tools/data-binding/integration-tests/IndependentLibrary/gradlew.bat b/tools/data-binding/integration-tests/IndependentLibrary/gradlew.bat new file mode 100644 index 0000000..aec9973 --- /dev/null +++ b/tools/data-binding/integration-tests/IndependentLibrary/gradlew.bat @@ -0,0 +1,90 @@ +@if "%DEBUG%" == "" @echo off
+@rem ##########################################################################
+@rem
+@rem Gradle startup script for Windows
+@rem
+@rem ##########################################################################
+
+@rem Set local scope for the variables with windows NT shell
+if "%OS%"=="Windows_NT" setlocal
+
+@rem Add default JVM options here. You can also use JAVA_OPTS and GRADLE_OPTS to pass JVM options to this script.
+set DEFAULT_JVM_OPTS=
+
+set DIRNAME=%~dp0
+if "%DIRNAME%" == "" set DIRNAME=.
+set APP_BASE_NAME=%~n0
+set APP_HOME=%DIRNAME%
+
+@rem Find java.exe
+if defined JAVA_HOME goto findJavaFromJavaHome
+
+set JAVA_EXE=java.exe
+%JAVA_EXE% -version >NUL 2>&1
+if "%ERRORLEVEL%" == "0" goto init
+
+echo.
+echo ERROR: JAVA_HOME is not set and no 'java' command could be found in your PATH.
+echo.
+echo Please set the JAVA_HOME variable in your environment to match the
+echo location of your Java installation.
+
+goto fail
+
+:findJavaFromJavaHome
+set JAVA_HOME=%JAVA_HOME:"=%
+set JAVA_EXE=%JAVA_HOME%/bin/java.exe
+
+if exist "%JAVA_EXE%" goto init
+
+echo.
+echo ERROR: JAVA_HOME is set to an invalid directory: %JAVA_HOME%
+echo.
+echo Please set the JAVA_HOME variable in your environment to match the
+echo location of your Java installation.
+
+goto fail
+
+:init
+@rem Get command-line arguments, handling Windowz variants
+
+if not "%OS%" == "Windows_NT" goto win9xME_args
+if "%@eval[2+2]" == "4" goto 4NT_args
+
+:win9xME_args
+@rem Slurp the command line arguments.
+set CMD_LINE_ARGS=
+set _SKIP=2
+
+:win9xME_args_slurp
+if "x%~1" == "x" goto execute
+
+set CMD_LINE_ARGS=%*
+goto execute
+
+:4NT_args
+@rem Get arguments from the 4NT Shell from JP Software
+set CMD_LINE_ARGS=%$
+
+:execute
+@rem Setup the command line
+
+set CLASSPATH=%APP_HOME%\gradle\wrapper\gradle-wrapper.jar
+
+@rem Execute Gradle
+"%JAVA_EXE%" %DEFAULT_JVM_OPTS% %JAVA_OPTS% %GRADLE_OPTS% "-Dorg.gradle.appname=%APP_BASE_NAME%" -classpath "%CLASSPATH%" org.gradle.wrapper.GradleWrapperMain %CMD_LINE_ARGS%
+
+:end
+@rem End local scope for the variables with windows NT shell
+if "%ERRORLEVEL%"=="0" goto mainEnd
+
+:fail
+rem Set variable GRADLE_EXIT_CONSOLE if you need the _script_ return code instead of
+rem the _cmd.exe /c_ return code!
+if not "" == "%GRADLE_EXIT_CONSOLE%" exit 1
+exit /b 1
+
+:mainEnd
+if "%OS%"=="Windows_NT" endlocal
+
+:omega
diff --git a/tools/data-binding/integration-tests/IndependentLibrary/settings.gradle b/tools/data-binding/integration-tests/IndependentLibrary/settings.gradle new file mode 100644 index 0000000..e2afad2 --- /dev/null +++ b/tools/data-binding/integration-tests/IndependentLibrary/settings.gradle @@ -0,0 +1,16 @@ +/* + * 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. + */ + +include ':app' diff --git a/tools/data-binding/integration-tests/MultiModuleTestApp/app/.gitignore b/tools/data-binding/integration-tests/MultiModuleTestApp/app/.gitignore new file mode 100644 index 0000000..796b96d --- /dev/null +++ b/tools/data-binding/integration-tests/MultiModuleTestApp/app/.gitignore @@ -0,0 +1 @@ +/build diff --git a/tools/data-binding/integration-tests/MultiModuleTestApp/app/build.gradle b/tools/data-binding/integration-tests/MultiModuleTestApp/app/build.gradle new file mode 100644 index 0000000..b09ed61 --- /dev/null +++ b/tools/data-binding/integration-tests/MultiModuleTestApp/app/build.gradle @@ -0,0 +1,53 @@ +/* + * 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. + */ + +apply plugin: 'com.android.application' +apply plugin: 'com.android.databinding' + +android { + compileSdkVersion 21 + buildToolsVersion "22" + + defaultConfig { + applicationId "com.android.databinding.multimoduletestapp" + minSdkVersion 7 + targetSdkVersion 21 + versionCode 1 + versionName "1.0" + } + buildTypes { + release { + minifyEnabled false + proguardFiles getDefaultProguardFile('proguard-android.txt'), 'proguard-rules.pro' + } + } + + packagingOptions { + exclude 'META-INF/services/javax.annotation.processing.Processor' + exclude 'META-INF/LICENSE.txt' + exclude 'META-INF/NOTICE.txt' + } +} + +println "combined ${config.testGroup}.independent-library:${config.snapshotVersion}" +dependencies { + compile fileTree(dir: 'libs', include: ['*.jar']) + compile "com.android.databinding:library:${config.snapshotVersion}" + compile project(':testlibrary') + compile "com.android.support:support-v4:+" + provided "com.android.databinding:annotationprocessor:${config.snapshotVersion}" + compile "${config.testGroup}:independent-library:${config.snapshotVersion}" +} diff --git a/tools/data-binding/integration-tests/MultiModuleTestApp/app/proguard-rules.pro b/tools/data-binding/integration-tests/MultiModuleTestApp/app/proguard-rules.pro new file mode 100644 index 0000000..b7210d1 --- /dev/null +++ b/tools/data-binding/integration-tests/MultiModuleTestApp/app/proguard-rules.pro @@ -0,0 +1,17 @@ +# Add project specific ProGuard rules here. +# By default, the flags in this file are appended to flags specified +# in /Users/yboyar/android/sdk/tools/proguard/proguard-android.txt +# You can edit the include path and order by changing the proguardFiles +# directive in build.gradle. +# +# For more details, see +# http://developer.android.com/guide/developing/tools/proguard.html + +# Add any project specific keep options here: + +# If your project uses WebView with JS, uncomment the following +# and specify the fully qualified class name to the JavaScript interface +# class: +#-keepclassmembers class fqcn.of.javascript.interface.for.webview { +# public *; +#} diff --git a/tools/data-binding/integration-tests/MultiModuleTestApp/app/src/androidTest/java/com/android/databinding/multimoduletestapp/ApplicationTest.java b/tools/data-binding/integration-tests/MultiModuleTestApp/app/src/androidTest/java/com/android/databinding/multimoduletestapp/ApplicationTest.java new file mode 100644 index 0000000..b3f219c --- /dev/null +++ b/tools/data-binding/integration-tests/MultiModuleTestApp/app/src/androidTest/java/com/android/databinding/multimoduletestapp/ApplicationTest.java @@ -0,0 +1,38 @@ +/* + * 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 android.databinding.multimoduletestapp; + +import android.databinding.testlibrary.ObservableInLibrary; + +import android.app.Application; +import android.databinding.Observable; +import android.databinding.OnPropertyChangedListener; +import android.test.ApplicationTestCase; + +import java.beans.PropertyChangeEvent; +import java.beans.PropertyChangeListener; +import java.util.HashMap; +import java.util.Map; + +/** + * <a href="http://d.android.com/tools/testing/testing_android.html">Testing Fundamentals</a> + */ +public class ApplicationTest extends ApplicationTestCase<Application> { + public ApplicationTest() { + super(Application.class); + } +} diff --git a/tools/data-binding/integration-tests/MultiModuleTestApp/app/src/androidTest/java/com/android/databinding/multimoduletestapp/EventIdsTest.java b/tools/data-binding/integration-tests/MultiModuleTestApp/app/src/androidTest/java/com/android/databinding/multimoduletestapp/EventIdsTest.java new file mode 100644 index 0000000..585571b --- /dev/null +++ b/tools/data-binding/integration-tests/MultiModuleTestApp/app/src/androidTest/java/com/android/databinding/multimoduletestapp/EventIdsTest.java @@ -0,0 +1,132 @@ +/* + * 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 android.databinding.multimoduletestapp; + +import android.databinding.testlibrary.ObservableInLibrary; + +import android.databinding.Observable; +import android.databinding.OnPropertyChangedListener; +import android.os.Debug; +import android.test.AndroidTestCase; + +import java.util.HashMap; +import java.util.Map; + +import android.databinding.multimoduletestapp.BR; + +public class EventIdsTest extends AndroidTestCase { + public void testLibraryObservable() { + ObservableInLibrary observableInLibrary = new ObservableInLibrary(); + EventCounter ec = new EventCounter(); + observableInLibrary.addOnPropertyChangedListener(ec); + ec.assertProperty(BR.libField1, 0); + ec.assertProperty(BR.libField2, 0); + ec.assertProperty(BR.sharedField, 0); + + observableInLibrary.setLibField1("a"); + ec.assertProperty(BR.libField1, 1); + ec.assertProperty(BR.libField2, 0); + ec.assertProperty(BR.sharedField, 0); + + observableInLibrary.setLibField2("b"); + ec.assertProperty(BR.libField1, 1); + ec.assertProperty(BR.libField2, 1); + ec.assertProperty(BR.sharedField, 0); + + observableInLibrary.setSharedField(3); + ec.assertProperty(BR.libField1, 1); + ec.assertProperty(BR.libField2, 1); + ec.assertProperty(BR.sharedField, 1); + } + + public void testAppObservable() { + ObservableInMainApp observableInMainApp = new ObservableInMainApp(); + EventCounter ec = new EventCounter(); + observableInMainApp.addOnPropertyChangedListener(ec); + ec.assertProperty(BR.appField1, 0); + ec.assertProperty(BR.appField2, 0); + ec.assertProperty(BR.sharedField, 0); + + observableInMainApp.setAppField2(3); + ec.assertProperty(BR.appField1, 0); + ec.assertProperty(BR.appField2, 1); + ec.assertProperty(BR.sharedField, 0); + + observableInMainApp.setAppField1("b"); + ec.assertProperty(BR.appField1, 1); + ec.assertProperty(BR.appField2, 1); + ec.assertProperty(BR.sharedField, 0); + + observableInMainApp.setSharedField(5); + ec.assertProperty(BR.appField1, 1); + ec.assertProperty(BR.appField2, 1); + ec.assertProperty(BR.sharedField, 1); + } + + public void testExtendingObservable() { + ObservableExtendingLib observable = new ObservableExtendingLib(); + EventCounter ec = new EventCounter(); + observable.addOnPropertyChangedListener(ec); + + ec.assertProperty(BR.childClassField, 0); + ec.assertProperty(BR.libField1, 0); + ec.assertProperty(BR.libField2, 0); + ec.assertProperty(BR.sharedField, 0); + + observable.setChildClassField("a"); + ec.assertProperty(BR.childClassField, 1); + ec.assertProperty(BR.libField1, 0); + ec.assertProperty(BR.libField2, 0); + ec.assertProperty(BR.sharedField, 0); + + observable.setLibField1("b"); + ec.assertProperty(BR.childClassField, 1); + ec.assertProperty(BR.libField1, 1); + ec.assertProperty(BR.libField2, 0); + ec.assertProperty(BR.sharedField, 0); + + observable.setLibField2("c"); + ec.assertProperty(BR.childClassField, 1); + ec.assertProperty(BR.libField1, 1); + ec.assertProperty(BR.libField2, 1); + ec.assertProperty(BR.sharedField, 0); + + observable.setSharedField(2); + ec.assertProperty(BR.childClassField, 1); + ec.assertProperty(BR.libField1, 1); + ec.assertProperty(BR.libField2, 1); + ec.assertProperty(BR.sharedField, 1); + } + + private static class EventCounter implements OnPropertyChangedListener { + Map<Integer, Integer> mCounter = new HashMap<>(); + + @Override + public void onPropertyChanged(Observable observable, int propertyId) { + mCounter.put(propertyId, get(propertyId) + 1); + } + + public int get(int propertyId) { + Integer val = mCounter.get(propertyId); + return val == null ? 0 : val; + } + + private void assertProperty(int propertyId, int value) { + assertEquals(get(propertyId), value); + } + } +} diff --git a/tools/data-binding/integration-tests/MultiModuleTestApp/app/src/main/AndroidManifest.xml b/tools/data-binding/integration-tests/MultiModuleTestApp/app/src/main/AndroidManifest.xml new file mode 100644 index 0000000..7e1cb9b --- /dev/null +++ b/tools/data-binding/integration-tests/MultiModuleTestApp/app/src/main/AndroidManifest.xml @@ -0,0 +1,37 @@ +<?xml version="1.0" encoding="utf-8"?> +<!-- + ~ 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. + --> + +<manifest xmlns:android="http://schemas.android.com/apk/res/android" + package="android.databinding.multimoduletestapp" > + + <application + android:allowBackup="true" + android:icon="@drawable/ic_launcher" + android:label="@string/app_name" + android:theme="@style/AppTheme" > + <activity + android:name=".MainActivity" + android:label="@string/app_name" > + <intent-filter> + <action android:name="android.intent.action.MAIN" /> + + <category android:name="android.intent.category.LAUNCHER" /> + </intent-filter> + </activity> + </application> + +</manifest> diff --git a/tools/data-binding/integration-tests/MultiModuleTestApp/app/src/main/java/android/databinding/multimoduletestapp/MainActivity.java b/tools/data-binding/integration-tests/MultiModuleTestApp/app/src/main/java/android/databinding/multimoduletestapp/MainActivity.java new file mode 100644 index 0000000..d90606b --- /dev/null +++ b/tools/data-binding/integration-tests/MultiModuleTestApp/app/src/main/java/android/databinding/multimoduletestapp/MainActivity.java @@ -0,0 +1,61 @@ +/* + * 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 android.databinding.multimoduletestapp; + +import android.databinding.multimoduletestapp.databinding.ActivityMainBinding; +import android.app.Activity; +import android.os.Bundle; +import android.view.Menu; +import android.view.MenuItem; +import android.view.ViewGroup; + +public class MainActivity extends Activity { + ActivityMainBinding mBinder; + @Override + protected void onCreate(Bundle savedInstanceState) { + super.onCreate(savedInstanceState); + mBinder = ActivityMainBinding.inflate(this); + setContentView(mBinder.getRoot()); + } + + public ActivityMainBinding getBinder() { + return mBinder; + } + + + @Override + public boolean onCreateOptionsMenu(Menu menu) { + // Inflate the menu; this adds items to the action bar if it is present. + getMenuInflater().inflate(R.menu.menu_main, menu); + return true; + } + + @Override + public boolean onOptionsItemSelected(MenuItem item) { + // Handle action bar item clicks here. The action bar will + // automatically handle clicks on the Home/Up button, so long + // as you specify a parent activity in AndroidManifest.xml. + int id = item.getItemId(); + + //noinspection SimplifiableIfStatement + if (id == R.id.action_settings) { + return true; + } + + return super.onOptionsItemSelected(item); + } +} diff --git a/tools/data-binding/integration-tests/MultiModuleTestApp/app/src/main/java/android/databinding/multimoduletestapp/ObservableExtendingLib.java b/tools/data-binding/integration-tests/MultiModuleTestApp/app/src/main/java/android/databinding/multimoduletestapp/ObservableExtendingLib.java new file mode 100644 index 0000000..b1ef5fe --- /dev/null +++ b/tools/data-binding/integration-tests/MultiModuleTestApp/app/src/main/java/android/databinding/multimoduletestapp/ObservableExtendingLib.java @@ -0,0 +1,36 @@ +/* + * 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 android.databinding.multimoduletestapp; + +import android.databinding.testlibrary.ObservableInLibrary; + +import android.databinding.Bindable; +import android.databinding.multimoduletestapp.BR; + +public class ObservableExtendingLib extends ObservableInLibrary { + @Bindable + private String mChildClassField; + + public String getChildClassField() { + return mChildClassField; + } + + public void setChildClassField(String childClassField) { + mChildClassField = childClassField; + notifyPropertyChanged(BR.childClassField); + } +} diff --git a/tools/data-binding/integration-tests/MultiModuleTestApp/app/src/main/java/android/databinding/multimoduletestapp/ObservableInMainApp.java b/tools/data-binding/integration-tests/MultiModuleTestApp/app/src/main/java/android/databinding/multimoduletestapp/ObservableInMainApp.java new file mode 100644 index 0000000..22317c7 --- /dev/null +++ b/tools/data-binding/integration-tests/MultiModuleTestApp/app/src/main/java/android/databinding/multimoduletestapp/ObservableInMainApp.java @@ -0,0 +1,58 @@ +/* + * Copyright (C) 2015 The Android Open Source Project + * + * Licensed under the Apache License, Version 2.0 (the "License"); + * you may not use this file except in compliance with the License. + * You may obtain a copy of the License at + * + * http://www.apache.org/licenses/LICENSE-2.0 + * + * Unless required by applicable law or agreed to in writing, software + * distributed under the License is distributed on an "AS IS" BASIS, + * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. + * See the License for the specific language governing permissions and + * limitations under the License. + */ + +package android.databinding.multimoduletestapp; + +import android.databinding.Bindable; + +import android.databinding.BaseObservable; +import android.databinding.multimoduletestapp.BR; + +public class ObservableInMainApp extends BaseObservable { + @Bindable + private String mAppField1; + @Bindable + private int mAppField2; + @Bindable + private int mSharedField; + + public String getAppField1() { + return mAppField1; + } + + public void setAppField1(String appField1) { + mAppField1 = appField1; + notifyPropertyChanged(BR.appField1); + } + + public int getAppField2() { + return mAppField2; + } + + public void setAppField2(int appField2) { + mAppField2 = appField2; + notifyPropertyChanged(BR.appField2); + } + + public int getSharedField() { + return mSharedField; + } + + public void setSharedField(int sharedField) { + mSharedField = sharedField; + notifyPropertyChanged(BR.sharedField); + } +} diff --git a/tools/data-binding/integration-tests/MultiModuleTestApp/app/src/main/res/drawable-hdpi/ic_launcher.png b/tools/data-binding/integration-tests/MultiModuleTestApp/app/src/main/res/drawable-hdpi/ic_launcher.png Binary files differnew file mode 100644 index 0000000..96a442e --- /dev/null +++ b/tools/data-binding/integration-tests/MultiModuleTestApp/app/src/main/res/drawable-hdpi/ic_launcher.png diff --git a/tools/data-binding/integration-tests/MultiModuleTestApp/app/src/main/res/drawable-mdpi/ic_launcher.png b/tools/data-binding/integration-tests/MultiModuleTestApp/app/src/main/res/drawable-mdpi/ic_launcher.png Binary files differnew file mode 100644 index 0000000..359047d --- /dev/null +++ b/tools/data-binding/integration-tests/MultiModuleTestApp/app/src/main/res/drawable-mdpi/ic_launcher.png diff --git a/tools/data-binding/integration-tests/MultiModuleTestApp/app/src/main/res/drawable-xhdpi/ic_launcher.png b/tools/data-binding/integration-tests/MultiModuleTestApp/app/src/main/res/drawable-xhdpi/ic_launcher.png Binary files differnew file mode 100644 index 0000000..71c6d76 --- /dev/null +++ b/tools/data-binding/integration-tests/MultiModuleTestApp/app/src/main/res/drawable-xhdpi/ic_launcher.png diff --git a/tools/data-binding/integration-tests/MultiModuleTestApp/app/src/main/res/drawable-xxhdpi/ic_launcher.png b/tools/data-binding/integration-tests/MultiModuleTestApp/app/src/main/res/drawable-xxhdpi/ic_launcher.png Binary files differnew file mode 100644 index 0000000..4df1894 --- /dev/null +++ b/tools/data-binding/integration-tests/MultiModuleTestApp/app/src/main/res/drawable-xxhdpi/ic_launcher.png diff --git a/tools/data-binding/integration-tests/MultiModuleTestApp/app/src/main/res/layout/activity_main.xml b/tools/data-binding/integration-tests/MultiModuleTestApp/app/src/main/res/layout/activity_main.xml new file mode 100644 index 0000000..4549e81 --- /dev/null +++ b/tools/data-binding/integration-tests/MultiModuleTestApp/app/src/main/res/layout/activity_main.xml @@ -0,0 +1,27 @@ +<!-- + ~ Copyright (C) 2015 The Android Open Source Project + ~ + ~ Licensed under the Apache License, Version 2.0 (the "License"); + ~ you may not use this file except in compliance with the License. + ~ You may obtain a copy of the License at + ~ + ~ http://www.apache.org/licenses/LICENSE-2.0 + ~ + ~ Unless required by applicable law or agreed to in writing, software + ~ distributed under the License is distributed on an "AS IS" BASIS, + ~ WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. + ~ See the License for the specific language governing permissions and + ~ limitations under the License. + --> + +<RelativeLayout xmlns:android="http://schemas.android.com/apk/res/android" + xmlns:tools="http://schemas.android.com/tools" android:layout_width="match_parent" + android:layout_height="match_parent" android:paddingLeft="@dimen/activity_horizontal_margin" + android:paddingRight="@dimen/activity_horizontal_margin" + android:paddingTop="@dimen/activity_vertical_margin" + android:paddingBottom="@dimen/activity_vertical_margin" tools:context=".MainActivity"> + <variable name="foo" type="String"/> + <TextView android:text='@{foo + " " + foo}' android:layout_width="wrap_content" + android:layout_height="wrap_content" /> + +</RelativeLayout> diff --git a/tools/data-binding/integration-tests/MultiModuleTestApp/app/src/main/res/layout/activity_test_library_main.xml b/tools/data-binding/integration-tests/MultiModuleTestApp/app/src/main/res/layout/activity_test_library_main.xml new file mode 100644 index 0000000..5a34049 --- /dev/null +++ b/tools/data-binding/integration-tests/MultiModuleTestApp/app/src/main/res/layout/activity_test_library_main.xml @@ -0,0 +1,27 @@ +<!-- + ~ Copyright (C) 2015 The Android Open Source Project + ~ + ~ Licensed under the Apache License, Version 2.0 (the "License"); + ~ you may not use this file except in compliance with the License. + ~ You may obtain a copy of the License at + ~ + ~ http://www.apache.org/licenses/LICENSE-2.0 + ~ + ~ Unless required by applicable law or agreed to in writing, software + ~ distributed under the License is distributed on an "AS IS" BASIS, + ~ WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. + ~ See the License for the specific language governing permissions and + ~ limitations under the License. + --> + +<RelativeLayout xmlns:android="http://schemas.android.com/apk/res/android" + xmlns:tools="http://schemas.android.com/tools" android:layout_width="match_parent" + android:layout_height="match_parent" android:paddingLeft="@dimen/activity_horizontal_margin" + android:paddingRight="@dimen/activity_horizontal_margin" + android:paddingTop="@dimen/activity_vertical_margin" + android:paddingBottom="@dimen/activity_vertical_margin" + > + <TextView android:layout_width="wrap_content" + android:layout_height="wrap_content" /> + +</RelativeLayout> diff --git a/tools/data-binding/integration-tests/MultiModuleTestApp/app/src/main/res/layout/another_layout.xml b/tools/data-binding/integration-tests/MultiModuleTestApp/app/src/main/res/layout/another_layout.xml new file mode 100644 index 0000000..891e70f --- /dev/null +++ b/tools/data-binding/integration-tests/MultiModuleTestApp/app/src/main/res/layout/another_layout.xml @@ -0,0 +1,11 @@ +<?xml version="1.0" encoding="utf-8"?> +<LinearLayout xmlns:android="http://schemas.android.com/apk/res/android" + android:orientation="vertical" android:layout_width="match_parent" + android:layout_height="match_parent"> + <variable name="userName" type="String"/> + <variable name="userLastName" type="String"/> + <TextView + android:text='@{userName + " " + userLastName}' + android:layout_width="wrap_content" + android:layout_height="wrap_content" /> +</LinearLayout>
\ No newline at end of file diff --git a/tools/data-binding/integration-tests/MultiModuleTestApp/app/src/main/res/layout/demo_layout.xml b/tools/data-binding/integration-tests/MultiModuleTestApp/app/src/main/res/layout/demo_layout.xml new file mode 100644 index 0000000..54c26dd --- /dev/null +++ b/tools/data-binding/integration-tests/MultiModuleTestApp/app/src/main/res/layout/demo_layout.xml @@ -0,0 +1,12 @@ +<?xml version="1.0" encoding="utf-8"?> +<LinearLayout xmlns:android="http://schemas.android.com/apk/res/android" + android:orientation="vertical" android:layout_width="match_parent" + android:layout_height="match_parent"> + <variable name="name" type="String"/> + <variable name="lastName" type="String"/> + <variable name="anotherVariable" type="String"/> + <TextView + android:text='@{name + " " + lastName}' + android:layout_width="wrap_content" + android:layout_height="wrap_content" /> +</LinearLayout>
\ No newline at end of file diff --git a/tools/data-binding/integration-tests/MultiModuleTestApp/app/src/main/res/layout/some_new_layout.xml b/tools/data-binding/integration-tests/MultiModuleTestApp/app/src/main/res/layout/some_new_layout.xml new file mode 100644 index 0000000..36872fe --- /dev/null +++ b/tools/data-binding/integration-tests/MultiModuleTestApp/app/src/main/res/layout/some_new_layout.xml @@ -0,0 +1,11 @@ +<?xml version="1.0" encoding="utf-8"?> +<LinearLayout xmlns:android="http://schemas.android.com/apk/res/android" + android:orientation="vertical" android:layout_width="match_parent" + android:layout_height="match_parent"> + <variable name="name" type="String"/> + <variable name="lastName" type="String"/> + <TextView + android:text='@{name + " " + lastName}' + android:layout_width="wrap_content" + android:layout_height="wrap_content" /> +</LinearLayout>
\ No newline at end of file diff --git a/tools/data-binding/integration-tests/MultiModuleTestApp/app/src/main/res/menu/menu_main.xml b/tools/data-binding/integration-tests/MultiModuleTestApp/app/src/main/res/menu/menu_main.xml new file mode 100644 index 0000000..4674d4d --- /dev/null +++ b/tools/data-binding/integration-tests/MultiModuleTestApp/app/src/main/res/menu/menu_main.xml @@ -0,0 +1,21 @@ +<!-- + ~ 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. + --> + +<menu xmlns:android="http://schemas.android.com/apk/res/android" + xmlns:tools="http://schemas.android.com/tools" tools:context=".MainActivity"> + <item android:id="@+id/action_settings" android:title="@string/action_settings" + android:orderInCategory="100" android:showAsAction="never" /> +</menu> diff --git a/tools/data-binding/integration-tests/MultiModuleTestApp/app/src/main/res/values-v21/styles.xml b/tools/data-binding/integration-tests/MultiModuleTestApp/app/src/main/res/values-v21/styles.xml new file mode 100644 index 0000000..9b24d4f --- /dev/null +++ b/tools/data-binding/integration-tests/MultiModuleTestApp/app/src/main/res/values-v21/styles.xml @@ -0,0 +1,21 @@ +<?xml version="1.0" encoding="utf-8"?> +<!-- + ~ 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. + --> + +<resources> + <style name="AppTheme" parent="android:Theme.Material.Light"> + </style> +</resources> diff --git a/tools/data-binding/integration-tests/MultiModuleTestApp/app/src/main/res/values-w820dp/dimens.xml b/tools/data-binding/integration-tests/MultiModuleTestApp/app/src/main/res/values-w820dp/dimens.xml new file mode 100644 index 0000000..4719591 --- /dev/null +++ b/tools/data-binding/integration-tests/MultiModuleTestApp/app/src/main/res/values-w820dp/dimens.xml @@ -0,0 +1,22 @@ +<!-- + ~ 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. + --> + +<resources> + <!-- Example customization of dimensions originally defined in res/values/dimens.xml + (such as screen margins) for screens with more than 820dp of available width. This + would include 7" and 10" devices in landscape (~960dp and ~1280dp respectively). --> + <dimen name="activity_horizontal_margin">64dp</dimen> +</resources> diff --git a/tools/data-binding/integration-tests/MultiModuleTestApp/app/src/main/res/values/dimens.xml b/tools/data-binding/integration-tests/MultiModuleTestApp/app/src/main/res/values/dimens.xml new file mode 100644 index 0000000..c06ae3f --- /dev/null +++ b/tools/data-binding/integration-tests/MultiModuleTestApp/app/src/main/res/values/dimens.xml @@ -0,0 +1,21 @@ +<!-- + ~ 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. + --> + +<resources> + <!-- Default screen margins, per the Android Design guidelines. --> + <dimen name="activity_horizontal_margin">16dp</dimen> + <dimen name="activity_vertical_margin">16dp</dimen> +</resources> diff --git a/tools/data-binding/integration-tests/MultiModuleTestApp/app/src/main/res/values/strings.xml b/tools/data-binding/integration-tests/MultiModuleTestApp/app/src/main/res/values/strings.xml new file mode 100644 index 0000000..20d68aa --- /dev/null +++ b/tools/data-binding/integration-tests/MultiModuleTestApp/app/src/main/res/values/strings.xml @@ -0,0 +1,24 @@ +<?xml version="1.0" encoding="utf-8"?> +<!-- + ~ 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. + --> + +<resources> + + <string name="app_name">Multi Module Test App</string> + <string name="hello_world">Hello world!</string> + <string name="action_settings">Settings</string> + +</resources> diff --git a/tools/data-binding/integration-tests/MultiModuleTestApp/app/src/main/res/values/styles.xml b/tools/data-binding/integration-tests/MultiModuleTestApp/app/src/main/res/values/styles.xml new file mode 100644 index 0000000..7dc23c8 --- /dev/null +++ b/tools/data-binding/integration-tests/MultiModuleTestApp/app/src/main/res/values/styles.xml @@ -0,0 +1,24 @@ +<!-- + ~ 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. + --> + +<resources> + + <!-- Base application theme. --> + <style name="AppTheme" parent="android:Theme.Holo.Light.DarkActionBar"> + <!-- Customize your theme here. --> + </style> + +</resources> diff --git a/tools/data-binding/integration-tests/MultiModuleTestApp/build.gradle b/tools/data-binding/integration-tests/MultiModuleTestApp/build.gradle new file mode 100644 index 0000000..b9340e5 --- /dev/null +++ b/tools/data-binding/integration-tests/MultiModuleTestApp/build.gradle @@ -0,0 +1,44 @@ +/* + * 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. + */ +buildscript { + def Properties dataBindingProperties = new Properties() + dataBindingProperties.load(new FileInputStream("${projectDir}/../../databinding.properties")) + dataBindingProperties.mavenRepoDir = "${projectDir}/../../${dataBindingProperties.mavenRepoName}" + ext.config = dataBindingProperties + println "loaded config" + + repositories { + jcenter() + maven { + url config.mavenRepoDir + } + } + dependencies { + classpath 'com.android.tools.build:gradle:1.1.3' + classpath "com.android.databinding:dataBinder:${config.snapshotVersion}" + // NOTE: Do not place your application dependencies here; they belong + // in the individual module build.gradle files + } +} + +allprojects { + repositories { + jcenter() + maven { + url config.mavenRepoDir + } + } +} diff --git a/tools/data-binding/integration-tests/MultiModuleTestApp/gradle.properties b/tools/data-binding/integration-tests/MultiModuleTestApp/gradle.properties new file mode 100644 index 0000000..5b24ba3 --- /dev/null +++ b/tools/data-binding/integration-tests/MultiModuleTestApp/gradle.properties @@ -0,0 +1,34 @@ +# +# 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. +# + +# Project-wide Gradle settings. + +# IDE (e.g. Android Studio) users: +# Gradle settings configured through the IDE *will override* +# any settings specified in this file. + +# For more details on how to configure your build environment visit +# http://www.gradle.org/docs/current/userguide/build_environment.html + +# Specifies the JVM arguments used for the daemon process. +# The setting is particularly useful for tweaking memory settings. +# Default value: -Xmx10248m -XX:MaxPermSize=256m +# org.gradle.jvmargs=-Xmx2048m -XX:MaxPermSize=512m -XX:+HeapDumpOnOutOfMemoryError -Dfile.encoding=UTF-8 + +# When configured, Gradle will run in incubating parallel mode. +# This option should only be used with decoupled projects. More details, visit +# http://www.gradle.org/docs/current/userguide/multi_project_builds.html#sec:decoupled_projects +# org.gradle.parallel=true
\ No newline at end of file diff --git a/tools/data-binding/integration-tests/MultiModuleTestApp/gradle/wrapper/gradle-wrapper.jar b/tools/data-binding/integration-tests/MultiModuleTestApp/gradle/wrapper/gradle-wrapper.jar Binary files differnew file mode 100644 index 0000000..8c0fb64 --- /dev/null +++ b/tools/data-binding/integration-tests/MultiModuleTestApp/gradle/wrapper/gradle-wrapper.jar diff --git a/tools/data-binding/integration-tests/MultiModuleTestApp/gradle/wrapper/gradle-wrapper.properties b/tools/data-binding/integration-tests/MultiModuleTestApp/gradle/wrapper/gradle-wrapper.properties new file mode 100644 index 0000000..992e276 --- /dev/null +++ b/tools/data-binding/integration-tests/MultiModuleTestApp/gradle/wrapper/gradle-wrapper.properties @@ -0,0 +1,22 @@ +# +# 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. +# + +#Wed Apr 10 15:27:10 PDT 2013 +distributionBase=GRADLE_USER_HOME +distributionPath=wrapper/dists +zipStoreBase=GRADLE_USER_HOME +zipStorePath=wrapper/dists +distributionUrl=https\://services.gradle.org/distributions/gradle-2.2.1-all.zip diff --git a/tools/data-binding/integration-tests/MultiModuleTestApp/gradlew b/tools/data-binding/integration-tests/MultiModuleTestApp/gradlew new file mode 100755 index 0000000..91a7e26 --- /dev/null +++ b/tools/data-binding/integration-tests/MultiModuleTestApp/gradlew @@ -0,0 +1,164 @@ +#!/usr/bin/env bash + +############################################################################## +## +## Gradle start up script for UN*X +## +############################################################################## + +# Add default JVM options here. You can also use JAVA_OPTS and GRADLE_OPTS to pass JVM options to this script. +DEFAULT_JVM_OPTS="" + +APP_NAME="Gradle" +APP_BASE_NAME=`basename "$0"` + +# Use the maximum available, or set MAX_FD != -1 to use that value. +MAX_FD="maximum" + +warn ( ) { + echo "$*" +} + +die ( ) { + echo + echo "$*" + echo + exit 1 +} + +# OS specific support (must be 'true' or 'false'). +cygwin=false +msys=false +darwin=false +case "`uname`" in + CYGWIN* ) + cygwin=true + ;; + Darwin* ) + darwin=true + ;; + MINGW* ) + msys=true + ;; +esac + +# For Cygwin, ensure paths are in UNIX format before anything is touched. +if $cygwin ; then + [ -n "$JAVA_HOME" ] && JAVA_HOME=`cygpath --unix "$JAVA_HOME"` +fi + +# Attempt to set APP_HOME +# Resolve links: $0 may be a link +PRG="$0" +# Need this for relative symlinks. +while [ -h "$PRG" ] ; do + ls=`ls -ld "$PRG"` + link=`expr "$ls" : '.*-> \(.*\)$'` + if expr "$link" : '/.*' > /dev/null; then + PRG="$link" + else + PRG=`dirname "$PRG"`"/$link" + fi +done +SAVED="`pwd`" +cd "`dirname \"$PRG\"`/" >&- +APP_HOME="`pwd -P`" +cd "$SAVED" >&- + +CLASSPATH=$APP_HOME/gradle/wrapper/gradle-wrapper.jar + +# Determine the Java command to use to start the JVM. +if [ -n "$JAVA_HOME" ] ; then + if [ -x "$JAVA_HOME/jre/sh/java" ] ; then + # IBM's JDK on AIX uses strange locations for the executables + JAVACMD="$JAVA_HOME/jre/sh/java" + else + JAVACMD="$JAVA_HOME/bin/java" + fi + if [ ! -x "$JAVACMD" ] ; then + die "ERROR: JAVA_HOME is set to an invalid directory: $JAVA_HOME + +Please set the JAVA_HOME variable in your environment to match the +location of your Java installation." + fi +else + JAVACMD="java" + which java >/dev/null 2>&1 || die "ERROR: JAVA_HOME is not set and no 'java' command could be found in your PATH. + +Please set the JAVA_HOME variable in your environment to match the +location of your Java installation." +fi + +# Increase the maximum file descriptors if we can. +if [ "$cygwin" = "false" -a "$darwin" = "false" ] ; then + MAX_FD_LIMIT=`ulimit -H -n` + if [ $? -eq 0 ] ; then + if [ "$MAX_FD" = "maximum" -o "$MAX_FD" = "max" ] ; then + MAX_FD="$MAX_FD_LIMIT" + fi + ulimit -n $MAX_FD + if [ $? -ne 0 ] ; then + warn "Could not set maximum file descriptor limit: $MAX_FD" + fi + else + warn "Could not query maximum file descriptor limit: $MAX_FD_LIMIT" + fi +fi + +# For Darwin, add options to specify how the application appears in the dock +if $darwin; then + GRADLE_OPTS="$GRADLE_OPTS \"-Xdock:name=$APP_NAME\" \"-Xdock:icon=$APP_HOME/media/gradle.icns\"" +fi + +# For Cygwin, switch paths to Windows format before running java +if $cygwin ; then + APP_HOME=`cygpath --path --mixed "$APP_HOME"` + CLASSPATH=`cygpath --path --mixed "$CLASSPATH"` + + # We build the pattern for arguments to be converted via cygpath + ROOTDIRSRAW=`find -L / -maxdepth 1 -mindepth 1 -type d 2>/dev/null` + SEP="" + for dir in $ROOTDIRSRAW ; do + ROOTDIRS="$ROOTDIRS$SEP$dir" + SEP="|" + done + OURCYGPATTERN="(^($ROOTDIRS))" + # Add a user-defined pattern to the cygpath arguments + if [ "$GRADLE_CYGPATTERN" != "" ] ; then + OURCYGPATTERN="$OURCYGPATTERN|($GRADLE_CYGPATTERN)" + fi + # Now convert the arguments - kludge to limit ourselves to /bin/sh + i=0 + for arg in "$@" ; do + CHECK=`echo "$arg"|egrep -c "$OURCYGPATTERN" -` + CHECK2=`echo "$arg"|egrep -c "^-"` ### Determine if an option + + if [ $CHECK -ne 0 ] && [ $CHECK2 -eq 0 ] ; then ### Added a condition + eval `echo args$i`=`cygpath --path --ignore --mixed "$arg"` + else + eval `echo args$i`="\"$arg\"" + fi + i=$((i+1)) + done + case $i in + (0) set -- ;; + (1) set -- "$args0" ;; + (2) set -- "$args0" "$args1" ;; + (3) set -- "$args0" "$args1" "$args2" ;; + (4) set -- "$args0" "$args1" "$args2" "$args3" ;; + (5) set -- "$args0" "$args1" "$args2" "$args3" "$args4" ;; + (6) set -- "$args0" "$args1" "$args2" "$args3" "$args4" "$args5" ;; + (7) set -- "$args0" "$args1" "$args2" "$args3" "$args4" "$args5" "$args6" ;; + (8) set -- "$args0" "$args1" "$args2" "$args3" "$args4" "$args5" "$args6" "$args7" ;; + (9) set -- "$args0" "$args1" "$args2" "$args3" "$args4" "$args5" "$args6" "$args7" "$args8" ;; + esac +fi + +# Split up the JVM_OPTS And GRADLE_OPTS values into an array, following the shell quoting and substitution rules +function splitJvmOpts() { + JVM_OPTS=("$@") +} +eval splitJvmOpts $DEFAULT_JVM_OPTS $JAVA_OPTS $GRADLE_OPTS +JVM_OPTS[${#JVM_OPTS[*]}]="-Dorg.gradle.appname=$APP_BASE_NAME" + +exec "$JAVACMD" "${JVM_OPTS[@]}" -classpath "$CLASSPATH" org.gradle.wrapper.GradleWrapperMain "$@" diff --git a/tools/data-binding/integration-tests/MultiModuleTestApp/gradlew.bat b/tools/data-binding/integration-tests/MultiModuleTestApp/gradlew.bat new file mode 100644 index 0000000..aec9973 --- /dev/null +++ b/tools/data-binding/integration-tests/MultiModuleTestApp/gradlew.bat @@ -0,0 +1,90 @@ +@if "%DEBUG%" == "" @echo off
+@rem ##########################################################################
+@rem
+@rem Gradle startup script for Windows
+@rem
+@rem ##########################################################################
+
+@rem Set local scope for the variables with windows NT shell
+if "%OS%"=="Windows_NT" setlocal
+
+@rem Add default JVM options here. You can also use JAVA_OPTS and GRADLE_OPTS to pass JVM options to this script.
+set DEFAULT_JVM_OPTS=
+
+set DIRNAME=%~dp0
+if "%DIRNAME%" == "" set DIRNAME=.
+set APP_BASE_NAME=%~n0
+set APP_HOME=%DIRNAME%
+
+@rem Find java.exe
+if defined JAVA_HOME goto findJavaFromJavaHome
+
+set JAVA_EXE=java.exe
+%JAVA_EXE% -version >NUL 2>&1
+if "%ERRORLEVEL%" == "0" goto init
+
+echo.
+echo ERROR: JAVA_HOME is not set and no 'java' command could be found in your PATH.
+echo.
+echo Please set the JAVA_HOME variable in your environment to match the
+echo location of your Java installation.
+
+goto fail
+
+:findJavaFromJavaHome
+set JAVA_HOME=%JAVA_HOME:"=%
+set JAVA_EXE=%JAVA_HOME%/bin/java.exe
+
+if exist "%JAVA_EXE%" goto init
+
+echo.
+echo ERROR: JAVA_HOME is set to an invalid directory: %JAVA_HOME%
+echo.
+echo Please set the JAVA_HOME variable in your environment to match the
+echo location of your Java installation.
+
+goto fail
+
+:init
+@rem Get command-line arguments, handling Windowz variants
+
+if not "%OS%" == "Windows_NT" goto win9xME_args
+if "%@eval[2+2]" == "4" goto 4NT_args
+
+:win9xME_args
+@rem Slurp the command line arguments.
+set CMD_LINE_ARGS=
+set _SKIP=2
+
+:win9xME_args_slurp
+if "x%~1" == "x" goto execute
+
+set CMD_LINE_ARGS=%*
+goto execute
+
+:4NT_args
+@rem Get arguments from the 4NT Shell from JP Software
+set CMD_LINE_ARGS=%$
+
+:execute
+@rem Setup the command line
+
+set CLASSPATH=%APP_HOME%\gradle\wrapper\gradle-wrapper.jar
+
+@rem Execute Gradle
+"%JAVA_EXE%" %DEFAULT_JVM_OPTS% %JAVA_OPTS% %GRADLE_OPTS% "-Dorg.gradle.appname=%APP_BASE_NAME%" -classpath "%CLASSPATH%" org.gradle.wrapper.GradleWrapperMain %CMD_LINE_ARGS%
+
+:end
+@rem End local scope for the variables with windows NT shell
+if "%ERRORLEVEL%"=="0" goto mainEnd
+
+:fail
+rem Set variable GRADLE_EXIT_CONSOLE if you need the _script_ return code instead of
+rem the _cmd.exe /c_ return code!
+if not "" == "%GRADLE_EXIT_CONSOLE%" exit 1
+exit /b 1
+
+:mainEnd
+if "%OS%"=="Windows_NT" endlocal
+
+:omega
diff --git a/tools/data-binding/integration-tests/MultiModuleTestApp/settings.gradle b/tools/data-binding/integration-tests/MultiModuleTestApp/settings.gradle new file mode 100644 index 0000000..c79cb3d --- /dev/null +++ b/tools/data-binding/integration-tests/MultiModuleTestApp/settings.gradle @@ -0,0 +1,17 @@ +/* + * 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. + */ + +include ':app', ':testlibrary' diff --git a/tools/data-binding/integration-tests/MultiModuleTestApp/testlibrary/.gitignore b/tools/data-binding/integration-tests/MultiModuleTestApp/testlibrary/.gitignore new file mode 100644 index 0000000..796b96d --- /dev/null +++ b/tools/data-binding/integration-tests/MultiModuleTestApp/testlibrary/.gitignore @@ -0,0 +1 @@ +/build diff --git a/tools/data-binding/integration-tests/MultiModuleTestApp/testlibrary/build.gradle b/tools/data-binding/integration-tests/MultiModuleTestApp/testlibrary/build.gradle new file mode 100644 index 0000000..4b2c87c --- /dev/null +++ b/tools/data-binding/integration-tests/MultiModuleTestApp/testlibrary/build.gradle @@ -0,0 +1,48 @@ +/* + * 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. + */ + +apply plugin: 'com.android.library' +apply plugin: 'com.android.databinding' + +android { + compileSdkVersion 21 + buildToolsVersion "21.1.2" + + defaultConfig { + minSdkVersion 7 + targetSdkVersion 21 + versionCode 1 + versionName "1.0" + } + buildTypes { + release { + minifyEnabled false + proguardFiles getDefaultProguardFile('proguard-android.txt'), 'proguard-rules.pro' + } + } + + packagingOptions { + exclude 'META-INF/services/javax.annotation.processing.Processor' + exclude 'META-INF/LICENSE.txt' + exclude 'META-INF/NOTICE.txt' + } +} + +dependencies { + compile fileTree(dir: 'libs', include: ['*.jar']) + compile "com.android.databinding:library:${config.snapshotVersion}" + provided "com.android.databinding:annotationprocessor:${config.snapshotVersion}" +} diff --git a/tools/data-binding/integration-tests/MultiModuleTestApp/testlibrary/proguard-rules.pro b/tools/data-binding/integration-tests/MultiModuleTestApp/testlibrary/proguard-rules.pro new file mode 100644 index 0000000..b7210d1 --- /dev/null +++ b/tools/data-binding/integration-tests/MultiModuleTestApp/testlibrary/proguard-rules.pro @@ -0,0 +1,17 @@ +# Add project specific ProGuard rules here. +# By default, the flags in this file are appended to flags specified +# in /Users/yboyar/android/sdk/tools/proguard/proguard-android.txt +# You can edit the include path and order by changing the proguardFiles +# directive in build.gradle. +# +# For more details, see +# http://developer.android.com/guide/developing/tools/proguard.html + +# Add any project specific keep options here: + +# If your project uses WebView with JS, uncomment the following +# and specify the fully qualified class name to the JavaScript interface +# class: +#-keepclassmembers class fqcn.of.javascript.interface.for.webview { +# public *; +#} diff --git a/tools/data-binding/integration-tests/MultiModuleTestApp/testlibrary/src/androidTest/java/android/databinding/testlibrary/ApplicationTest.java b/tools/data-binding/integration-tests/MultiModuleTestApp/testlibrary/src/androidTest/java/android/databinding/testlibrary/ApplicationTest.java new file mode 100644 index 0000000..49d1b7c --- /dev/null +++ b/tools/data-binding/integration-tests/MultiModuleTestApp/testlibrary/src/androidTest/java/android/databinding/testlibrary/ApplicationTest.java @@ -0,0 +1,29 @@ +/* + * 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 android.databinding.testlibrary; + +import android.app.Application; +import android.test.ApplicationTestCase; + +/** + * <a href="http://d.android.com/tools/testing/testing_android.html">Testing Fundamentals</a> + */ +public class ApplicationTest extends ApplicationTestCase<Application> { + public ApplicationTest() { + super(Application.class); + } +} diff --git a/tools/data-binding/integration-tests/MultiModuleTestApp/testlibrary/src/main/AndroidManifest.xml b/tools/data-binding/integration-tests/MultiModuleTestApp/testlibrary/src/main/AndroidManifest.xml new file mode 100644 index 0000000..1f1fb2b --- /dev/null +++ b/tools/data-binding/integration-tests/MultiModuleTestApp/testlibrary/src/main/AndroidManifest.xml @@ -0,0 +1,36 @@ +<?xml version="1.0" encoding="utf-8"?> +<!-- + ~ 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. + --> + +<manifest xmlns:android="http://schemas.android.com/apk/res/android" + package="android.databinding.testlibrary" > + + <application + android:allowBackup="true" + android:icon="@drawable/ic_launcher" + android:label="@string/app_name" > + <activity + android:name=".TestLibraryMainActivity" + android:label="@string/app_name" > + <intent-filter> + <action android:name="android.intent.action.MAIN" /> + + <category android:name="android.intent.category.LAUNCHER" /> + </intent-filter> + </activity> + </application> + +</manifest> diff --git a/tools/data-binding/integration-tests/MultiModuleTestApp/testlibrary/src/main/java/android/databinding/testlibrary/ObservableInLibrary.java b/tools/data-binding/integration-tests/MultiModuleTestApp/testlibrary/src/main/java/android/databinding/testlibrary/ObservableInLibrary.java new file mode 100644 index 0000000..c3fa89c --- /dev/null +++ b/tools/data-binding/integration-tests/MultiModuleTestApp/testlibrary/src/main/java/android/databinding/testlibrary/ObservableInLibrary.java @@ -0,0 +1,62 @@ +/* + * 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 android.databinding.testlibrary; + +import android.databinding.Bindable; + +import android.databinding.testlibrary.BR; + +import android.databinding.BaseObservable; + +public class ObservableInLibrary extends BaseObservable { + + @Bindable + private String mLibField1; + + @Bindable + private String mLibField2; + + @Bindable + private int mSharedField; + + public String getLibField1() { + return mLibField1; + } + + public void setLibField1(String libField1) { + mLibField1 = libField1; + notifyPropertyChanged(BR.libField1); + } + + public String getLibField2() { + return mLibField2; + } + + public void setLibField2(String libField2) { + mLibField2 = libField2; + notifyPropertyChanged(BR.libField2); + } + + public int getSharedField() { + return mSharedField; + } + + public void setSharedField(int sharedField) { + mSharedField = sharedField; + notifyPropertyChanged(BR.sharedField); + } +} diff --git a/tools/data-binding/integration-tests/MultiModuleTestApp/testlibrary/src/main/java/android/databinding/testlibrary/TestLibObject.java b/tools/data-binding/integration-tests/MultiModuleTestApp/testlibrary/src/main/java/android/databinding/testlibrary/TestLibObject.java new file mode 100644 index 0000000..8bf253a --- /dev/null +++ b/tools/data-binding/integration-tests/MultiModuleTestApp/testlibrary/src/main/java/android/databinding/testlibrary/TestLibObject.java @@ -0,0 +1,32 @@ +/* + * 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 android.databinding.testlibrary; + +import android.databinding.Bindable; + +public class TestLibObject { + @Bindable + private String mField; + + public String getField() { + return mField; + } + + public void setField(String field) { + this.mField = field; + } +} diff --git a/tools/data-binding/integration-tests/MultiModuleTestApp/testlibrary/src/main/java/android/databinding/testlibrary/TestLibraryMainActivity.java b/tools/data-binding/integration-tests/MultiModuleTestApp/testlibrary/src/main/java/android/databinding/testlibrary/TestLibraryMainActivity.java new file mode 100644 index 0000000..783d51f --- /dev/null +++ b/tools/data-binding/integration-tests/MultiModuleTestApp/testlibrary/src/main/java/android/databinding/testlibrary/TestLibraryMainActivity.java @@ -0,0 +1,56 @@ +/* + * 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 android.databinding.testlibrary; + +import android.app.Activity; +import android.os.Bundle; +import android.view.Menu; +import android.view.MenuItem; +import android.databinding.testlibrary.R; +import android.databinding.testlibrary.databinding.ActivityTestLibraryMainBinding; +public class TestLibraryMainActivity extends Activity { + @Override + protected void onCreate(Bundle savedInstanceState) { + super.onCreate(savedInstanceState); + ActivityTestLibraryMainBinding binder = ActivityTestLibraryMainBinding.inflate(this); + setContentView(binder.getRoot()); + + } + + + @Override + public boolean onCreateOptionsMenu(Menu menu) { + // Inflate the menu; this adds items to the action bar if it is present. + getMenuInflater().inflate(R.menu.menu_test_library_main, menu); + return true; + } + + @Override + public boolean onOptionsItemSelected(MenuItem item) { + // Handle action bar item clicks here. The action bar will + // automatically handle clicks on the Home/Up button, so long + // as you specify a parent activity in AndroidManifest.xml. + int id = item.getItemId(); + + //noinspection SimplifiableIfStatement + if (id == R.id.action_settings) { + return true; + } + + return super.onOptionsItemSelected(item); + } +} diff --git a/tools/data-binding/integration-tests/MultiModuleTestApp/testlibrary/src/main/res/drawable-hdpi/ic_launcher.png b/tools/data-binding/integration-tests/MultiModuleTestApp/testlibrary/src/main/res/drawable-hdpi/ic_launcher.png Binary files differnew file mode 100644 index 0000000..96a442e --- /dev/null +++ b/tools/data-binding/integration-tests/MultiModuleTestApp/testlibrary/src/main/res/drawable-hdpi/ic_launcher.png diff --git a/tools/data-binding/integration-tests/MultiModuleTestApp/testlibrary/src/main/res/drawable-mdpi/ic_launcher.png b/tools/data-binding/integration-tests/MultiModuleTestApp/testlibrary/src/main/res/drawable-mdpi/ic_launcher.png Binary files differnew file mode 100644 index 0000000..359047d --- /dev/null +++ b/tools/data-binding/integration-tests/MultiModuleTestApp/testlibrary/src/main/res/drawable-mdpi/ic_launcher.png diff --git a/tools/data-binding/integration-tests/MultiModuleTestApp/testlibrary/src/main/res/drawable-xhdpi/ic_launcher.png b/tools/data-binding/integration-tests/MultiModuleTestApp/testlibrary/src/main/res/drawable-xhdpi/ic_launcher.png Binary files differnew file mode 100644 index 0000000..71c6d76 --- /dev/null +++ b/tools/data-binding/integration-tests/MultiModuleTestApp/testlibrary/src/main/res/drawable-xhdpi/ic_launcher.png diff --git a/tools/data-binding/integration-tests/MultiModuleTestApp/testlibrary/src/main/res/drawable-xxhdpi/ic_launcher.png b/tools/data-binding/integration-tests/MultiModuleTestApp/testlibrary/src/main/res/drawable-xxhdpi/ic_launcher.png Binary files differnew file mode 100644 index 0000000..4df1894 --- /dev/null +++ b/tools/data-binding/integration-tests/MultiModuleTestApp/testlibrary/src/main/res/drawable-xxhdpi/ic_launcher.png diff --git a/tools/data-binding/integration-tests/MultiModuleTestApp/testlibrary/src/main/res/layout/activity_test_library_main.xml b/tools/data-binding/integration-tests/MultiModuleTestApp/testlibrary/src/main/res/layout/activity_test_library_main.xml new file mode 100644 index 0000000..d60fa98 --- /dev/null +++ b/tools/data-binding/integration-tests/MultiModuleTestApp/testlibrary/src/main/res/layout/activity_test_library_main.xml @@ -0,0 +1,29 @@ +<!-- + ~ 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. + --> + +<RelativeLayout xmlns:android="http://schemas.android.com/apk/res/android" + xmlns:tools="http://schemas.android.com/tools" android:layout_width="match_parent" + android:layout_height="match_parent" android:paddingLeft="@dimen/activity_horizontal_margin" + android:paddingRight="@dimen/activity_horizontal_margin" + android:paddingTop="@dimen/activity_vertical_margin" + android:paddingBottom="@dimen/activity_vertical_margin" + tools:context=".TestLibraryMainActivity"> + <variable name="obj1" type="android.databinding.testlibrary.TestLibObject"/> + + <TextView android:text="@{obj1.field}" android:layout_width="wrap_content" + android:layout_height="wrap_content" /> + +</RelativeLayout>
\ No newline at end of file diff --git a/tools/data-binding/integration-tests/MultiModuleTestApp/testlibrary/src/main/res/layout/another_layout_file.xml b/tools/data-binding/integration-tests/MultiModuleTestApp/testlibrary/src/main/res/layout/another_layout_file.xml new file mode 100644 index 0000000..62b2c73 --- /dev/null +++ b/tools/data-binding/integration-tests/MultiModuleTestApp/testlibrary/src/main/res/layout/another_layout_file.xml @@ -0,0 +1,28 @@ +<!-- + ~ 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. + --> + +<RelativeLayout xmlns:android="http://schemas.android.com/apk/res/android" + xmlns:tools="http://schemas.android.com/tools" android:layout_width="match_parent" + android:layout_height="match_parent" android:paddingLeft="@dimen/activity_horizontal_margin" + android:paddingRight="@dimen/activity_horizontal_margin" + android:paddingTop="@dimen/activity_vertical_margin" + android:paddingBottom="@dimen/activity_vertical_margin" tools:context=".MainActivity"> + <variable name="foo" type="String"/> + <variable name="foo2" type="int"/> + <TextView android:text='@{foo + " " + foo}' android:layout_width="wrap_content" + android:layout_height="wrap_content" /> + +</RelativeLayout> diff --git a/tools/data-binding/integration-tests/MultiModuleTestApp/testlibrary/src/main/res/layout/library_only_layout.xml b/tools/data-binding/integration-tests/MultiModuleTestApp/testlibrary/src/main/res/layout/library_only_layout.xml new file mode 100644 index 0000000..989517d --- /dev/null +++ b/tools/data-binding/integration-tests/MultiModuleTestApp/testlibrary/src/main/res/layout/library_only_layout.xml @@ -0,0 +1,30 @@ +<!-- + ~ Copyright (C) 2015 The Android Open Source Project + ~ + ~ Licensed under the Apache License, Version 2.0 (the "License"); + ~ you may not use this file except in compliance with the License. + ~ You may obtain a copy of the License at + ~ + ~ http://www.apache.org/licenses/LICENSE-2.0 + ~ + ~ Unless required by applicable law or agreed to in writing, software + ~ distributed under the License is distributed on an "AS IS" BASIS, + ~ WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. + ~ See the License for the specific language governing permissions and + ~ limitations under the License. + --> + +<RelativeLayout xmlns:android="http://schemas.android.com/apk/res/android" + xmlns:tools="http://schemas.android.com/tools" android:layout_width="match_parent" + android:layout_height="match_parent" android:paddingLeft="@dimen/activity_horizontal_margin" + android:paddingRight="@dimen/activity_horizontal_margin" + android:paddingTop="@dimen/activity_vertical_margin" + android:paddingBottom="@dimen/activity_vertical_margin" + tools:context=".TestLibraryMainActivity"> + <variable name="obj1" type="android.databinding.testlibrary.TestLibObject"/> + <variable name="obj2" type="android.databinding.testlibrary.TestLibObject"/> + + <TextView android:text="@{obj1.field}" android:layout_width="wrap_content" + android:layout_height="wrap_content" /> + +</RelativeLayout> diff --git a/tools/data-binding/integration-tests/MultiModuleTestApp/testlibrary/src/main/res/menu/menu_test_library_main.xml b/tools/data-binding/integration-tests/MultiModuleTestApp/testlibrary/src/main/res/menu/menu_test_library_main.xml new file mode 100644 index 0000000..68d936d --- /dev/null +++ b/tools/data-binding/integration-tests/MultiModuleTestApp/testlibrary/src/main/res/menu/menu_test_library_main.xml @@ -0,0 +1,21 @@ +<!-- + ~ 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. + --> + +<menu xmlns:android="http://schemas.android.com/apk/res/android" + xmlns:tools="http://schemas.android.com/tools" tools:context=".TestLibraryMainActivity"> + <item android:id="@+id/action_settings" android:title="@string/action_settings" + android:orderInCategory="100" android:showAsAction="never" /> +</menu> diff --git a/tools/data-binding/integration-tests/MultiModuleTestApp/testlibrary/src/main/res/values-w820dp/dimens.xml b/tools/data-binding/integration-tests/MultiModuleTestApp/testlibrary/src/main/res/values-w820dp/dimens.xml new file mode 100644 index 0000000..4719591 --- /dev/null +++ b/tools/data-binding/integration-tests/MultiModuleTestApp/testlibrary/src/main/res/values-w820dp/dimens.xml @@ -0,0 +1,22 @@ +<!-- + ~ 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. + --> + +<resources> + <!-- Example customization of dimensions originally defined in res/values/dimens.xml + (such as screen margins) for screens with more than 820dp of available width. This + would include 7" and 10" devices in landscape (~960dp and ~1280dp respectively). --> + <dimen name="activity_horizontal_margin">64dp</dimen> +</resources> diff --git a/tools/data-binding/integration-tests/MultiModuleTestApp/testlibrary/src/main/res/values/dimens.xml b/tools/data-binding/integration-tests/MultiModuleTestApp/testlibrary/src/main/res/values/dimens.xml new file mode 100644 index 0000000..c06ae3f --- /dev/null +++ b/tools/data-binding/integration-tests/MultiModuleTestApp/testlibrary/src/main/res/values/dimens.xml @@ -0,0 +1,21 @@ +<!-- + ~ 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. + --> + +<resources> + <!-- Default screen margins, per the Android Design guidelines. --> + <dimen name="activity_horizontal_margin">16dp</dimen> + <dimen name="activity_vertical_margin">16dp</dimen> +</resources> diff --git a/tools/data-binding/integration-tests/MultiModuleTestApp/testlibrary/src/main/res/values/strings.xml b/tools/data-binding/integration-tests/MultiModuleTestApp/testlibrary/src/main/res/values/strings.xml new file mode 100644 index 0000000..d930ddb --- /dev/null +++ b/tools/data-binding/integration-tests/MultiModuleTestApp/testlibrary/src/main/res/values/strings.xml @@ -0,0 +1,24 @@ +<?xml version="1.0" encoding="utf-8"?> +<!-- + ~ 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. + --> + +<resources> + + <string name="app_name">Test Library</string> + <string name="hello_world">Hello world!</string> + <string name="action_settings">Settings</string> + +</resources> diff --git a/tools/data-binding/integration-tests/TestApp/.gitignore b/tools/data-binding/integration-tests/TestApp/.gitignore new file mode 100644 index 0000000..afbdab3 --- /dev/null +++ b/tools/data-binding/integration-tests/TestApp/.gitignore @@ -0,0 +1,6 @@ +.gradle +/local.properties +/.idea/workspace.xml +/.idea/libraries +.DS_Store +/build diff --git a/tools/data-binding/integration-tests/TestApp/app/.gitignore b/tools/data-binding/integration-tests/TestApp/app/.gitignore new file mode 100644 index 0000000..796b96d --- /dev/null +++ b/tools/data-binding/integration-tests/TestApp/app/.gitignore @@ -0,0 +1 @@ +/build diff --git a/tools/data-binding/integration-tests/TestApp/app/build.gradle b/tools/data-binding/integration-tests/TestApp/app/build.gradle new file mode 100644 index 0000000..d9da119 --- /dev/null +++ b/tools/data-binding/integration-tests/TestApp/app/build.gradle @@ -0,0 +1,37 @@ +apply plugin: 'com.android.application' +apply plugin: 'com.android.databinding' + +android { + compileSdkVersion 21 + buildToolsVersion "22" + + defaultConfig { + applicationId "com.android.databinding.testapp" + minSdkVersion 7 + targetSdkVersion 21 + versionCode 1 + versionName "1.0" + } + buildTypes { + release { + minifyEnabled false + proguardFiles getDefaultProguardFile('proguard-android.txt'), 'proguard-rules.pro' + } + } + + packagingOptions { + exclude 'META-INF/services/javax.annotation.processing.Processor' + } + compileOptions { + sourceCompatibility JavaVersion.VERSION_1_7 + targetCompatibility JavaVersion.VERSION_1_7 + } +} + +dependencies { + compile fileTree(dir: 'libs', include: ['*.jar']) + compile "com.android.databinding:library:${config.snapshotVersion}" + compile "com.android.databinding:adapters:${config.snapshotVersion}" + compile "com.android.support:support-v4:+" + provided "com.android.databinding:annotationprocessor:${config.snapshotVersion}" +} diff --git a/tools/data-binding/integration-tests/TestApp/app/proguard-rules.pro b/tools/data-binding/integration-tests/TestApp/app/proguard-rules.pro new file mode 100644 index 0000000..b7210d1 --- /dev/null +++ b/tools/data-binding/integration-tests/TestApp/app/proguard-rules.pro @@ -0,0 +1,17 @@ +# Add project specific ProGuard rules here. +# By default, the flags in this file are appended to flags specified +# in /Users/yboyar/android/sdk/tools/proguard/proguard-android.txt +# You can edit the include path and order by changing the proguardFiles +# directive in build.gradle. +# +# For more details, see +# http://developer.android.com/guide/developing/tools/proguard.html + +# Add any project specific keep options here: + +# If your project uses WebView with JS, uncomment the following +# and specify the fully qualified class name to the JavaScript interface +# class: +#-keepclassmembers class fqcn.of.javascript.interface.for.webview { +# public *; +#} diff --git a/tools/data-binding/integration-tests/TestApp/app/src/androidTest/java/android/databinding/library/DataBinderTrojan.java b/tools/data-binding/integration-tests/TestApp/app/src/androidTest/java/android/databinding/library/DataBinderTrojan.java new file mode 100644 index 0000000..763f5a4 --- /dev/null +++ b/tools/data-binding/integration-tests/TestApp/app/src/androidTest/java/android/databinding/library/DataBinderTrojan.java @@ -0,0 +1,26 @@ +/* + * 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 android.databinding; + +/** + * This helper is used to toggle DataBinder's package private values to change behavior for testing + */ +public class DataBinderTrojan { + public static void setBuildSdkInt(int level) { + ViewDataBinding.SDK_INT = level; + } +} diff --git a/tools/data-binding/integration-tests/TestApp/app/src/androidTest/java/android/databinding/testapp/AbsListViewBindingAdapterTest.java b/tools/data-binding/integration-tests/TestApp/app/src/androidTest/java/android/databinding/testapp/AbsListViewBindingAdapterTest.java new file mode 100644 index 0000000..287fa24 --- /dev/null +++ b/tools/data-binding/integration-tests/TestApp/app/src/androidTest/java/android/databinding/testapp/AbsListViewBindingAdapterTest.java @@ -0,0 +1,69 @@ +/* + * 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 android.databinding.testapp; + +import android.databinding.testapp.databinding.AbsListViewAdapterTestBinding; +import android.databinding.testapp.vo.AbsListViewBindingObject; + +import android.graphics.drawable.ColorDrawable; +import android.os.Build; +import android.os.Debug; +import android.widget.ListView; + +public class AbsListViewBindingAdapterTest + extends BindingAdapterTestBase<AbsListViewAdapterTestBinding, AbsListViewBindingObject> { + + ListView mView; + + public AbsListViewBindingAdapterTest() { + super(AbsListViewAdapterTestBinding.class, AbsListViewBindingObject.class, + R.layout.abs_list_view_adapter_test); + } + + @Override + protected void setUp() throws Exception { + super.setUp(); + mView = mBinder.view; + } + + public void testListSelector() throws Throwable { + if (Build.VERSION.SDK_INT >= Build.VERSION_CODES.HONEYCOMB) { + assertEquals(mBindingObject.getListSelector().getColor(), + ((ColorDrawable) mView.getSelector()).getColor()); + + changeValues(); + + assertEquals(mBindingObject.getListSelector().getColor(), + ((ColorDrawable) mView.getSelector()).getColor()); + } + } + + public void testScrollingCache() throws Throwable { + assertEquals(mBindingObject.isScrollingCache(), mView.isScrollingCacheEnabled()); + + changeValues(); + + assertEquals(mBindingObject.isScrollingCache(), mView.isScrollingCacheEnabled()); + } + + public void testSmoothScrollbar() throws Throwable { + assertEquals(mBindingObject.isSmoothScrollbar(), mView.isSmoothScrollbarEnabled()); + + changeValues(); + + assertEquals(mBindingObject.isSmoothScrollbar(), mView.isSmoothScrollbarEnabled()); + } +} diff --git a/tools/data-binding/integration-tests/TestApp/app/src/androidTest/java/android/databinding/testapp/AbsSeekBarBindingAdapterTest.java b/tools/data-binding/integration-tests/TestApp/app/src/androidTest/java/android/databinding/testapp/AbsSeekBarBindingAdapterTest.java new file mode 100644 index 0000000..5424309 --- /dev/null +++ b/tools/data-binding/integration-tests/TestApp/app/src/androidTest/java/android/databinding/testapp/AbsSeekBarBindingAdapterTest.java @@ -0,0 +1,49 @@ +/* + * 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 android.databinding.testapp; + +import android.databinding.testapp.databinding.AbsSeekBarAdapterTestBinding; +import android.databinding.testapp.vo.AbsSeekBarBindingObject; + +import android.os.Build; +import android.widget.SeekBar; + +public class AbsSeekBarBindingAdapterTest + extends BindingAdapterTestBase<AbsSeekBarAdapterTestBinding, AbsSeekBarBindingObject> { + + SeekBar mView; + + public AbsSeekBarBindingAdapterTest() { + super(AbsSeekBarAdapterTestBinding.class, AbsSeekBarBindingObject.class, + R.layout.abs_seek_bar_adapter_test); + } + + @Override + protected void setUp() throws Exception { + super.setUp(); + mView = mBinder.view; + } + + public void testThumbTint() throws Throwable { + if (Build.VERSION.SDK_INT >= Build.VERSION_CODES.LOLLIPOP) { + assertEquals(mBindingObject.getThumbTint(), mView.getThumbTintList().getDefaultColor()); + + changeValues(); + + assertEquals(mBindingObject.getThumbTint(), mView.getThumbTintList().getDefaultColor()); + } + } +} diff --git a/tools/data-binding/integration-tests/TestApp/app/src/androidTest/java/android/databinding/testapp/AbsSpinnerBindingAdapterTest.java b/tools/data-binding/integration-tests/TestApp/app/src/androidTest/java/android/databinding/testapp/AbsSpinnerBindingAdapterTest.java new file mode 100644 index 0000000..d1d45f5 --- /dev/null +++ b/tools/data-binding/integration-tests/TestApp/app/src/androidTest/java/android/databinding/testapp/AbsSpinnerBindingAdapterTest.java @@ -0,0 +1,59 @@ +/* + * 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 android.databinding.testapp; + +import android.databinding.testapp.databinding.AbsSpinnerAdapterTestBinding; +import android.databinding.testapp.vo.AbsSpinnerBindingObject; + +import android.os.Build; +import android.widget.Spinner; +import android.widget.SpinnerAdapter; + +public class AbsSpinnerBindingAdapterTest + extends BindingAdapterTestBase<AbsSpinnerAdapterTestBinding, AbsSpinnerBindingObject> { + + Spinner mView; + + public AbsSpinnerBindingAdapterTest() { + super(AbsSpinnerAdapterTestBinding.class, AbsSpinnerBindingObject.class, + R.layout.abs_spinner_adapter_test); + } + + @Override + protected void setUp() throws Exception { + super.setUp(); + mView = mBinder.view; + } + + public void testEntries() throws Throwable { + if (Build.VERSION.SDK_INT >= Build.VERSION_CODES.LOLLIPOP) { + validateEntries(); + + changeValues(); + + validateEntries(); + } + } + + private void validateEntries() { + assertEquals(mBindingObject.getEntries().length, mView.getAdapter().getCount()); + CharSequence[] entries = mBindingObject.getEntries(); + SpinnerAdapter adapter = mView.getAdapter(); + for (int i = 0; i < entries.length; i++) { + assertEquals(adapter.getItem(i), entries[i]); + } + } +} diff --git a/tools/data-binding/integration-tests/TestApp/app/src/androidTest/java/android/databinding/testapp/ApplicationTest.java b/tools/data-binding/integration-tests/TestApp/app/src/androidTest/java/android/databinding/testapp/ApplicationTest.java new file mode 100644 index 0000000..7c7a9b4 --- /dev/null +++ b/tools/data-binding/integration-tests/TestApp/app/src/androidTest/java/android/databinding/testapp/ApplicationTest.java @@ -0,0 +1,27 @@ +/* + * Copyright (C) 2015 The Android Open Source Project + * Licensed under the Apache License, Version 2.0 (the "License"); + * you may not use this file except in compliance with the License. + * You may obtain a copy of the License at + * http://www.apache.org/licenses/LICENSE-2.0 + * Unless required by applicable law or agreed to in writing, software + * distributed under the License is distributed on an "AS IS" BASIS, + * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. + * See the License for the specific language governing permissions and + * limitations under the License. + */ + +package android.databinding.testapp; + +import android.app.Application; +import android.test.ApplicationTestCase; + +/** + * <a href="http://d.android.com/tools/testing/testing_android.html">Testing Fundamentals</a> + */ +public class ApplicationTest extends ApplicationTestCase<Application> { + + public ApplicationTest() { + super(Application.class); + } +} diff --git a/tools/data-binding/integration-tests/TestApp/app/src/androidTest/java/android/databinding/testapp/AutoCompleteTextViewBindingAdapterTest.java b/tools/data-binding/integration-tests/TestApp/app/src/androidTest/java/android/databinding/testapp/AutoCompleteTextViewBindingAdapterTest.java new file mode 100644 index 0000000..a14d05d --- /dev/null +++ b/tools/data-binding/integration-tests/TestApp/app/src/androidTest/java/android/databinding/testapp/AutoCompleteTextViewBindingAdapterTest.java @@ -0,0 +1,61 @@ +/* + * 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 android.databinding.testapp; + +import android.databinding.testapp.databinding.AutoCompleteTextViewAdapterTestBinding; +import android.databinding.testapp.vo.AutoCompleteTextViewBindingObject; + +import android.graphics.drawable.ColorDrawable; +import android.os.Build; +import android.widget.AutoCompleteTextView; + +public class AutoCompleteTextViewBindingAdapterTest extends + BindingAdapterTestBase<AutoCompleteTextViewAdapterTestBinding, + AutoCompleteTextViewBindingObject> { + + AutoCompleteTextView mView; + + public AutoCompleteTextViewBindingAdapterTest() { + super(AutoCompleteTextViewAdapterTestBinding.class, AutoCompleteTextViewBindingObject.class, + R.layout.auto_complete_text_view_adapter_test); + } + + @Override + protected void setUp() throws Exception { + super.setUp(); + mView = mBinder.view; + } + + public void testCompletionThreshold() throws Throwable { + assertEquals(mBindingObject.getCompletionThreshold(), mView.getThreshold()); + + changeValues(); + + assertEquals(mBindingObject.getCompletionThreshold(), mView.getThreshold()); + } + + public void testPopupBackground() throws Throwable { + if (Build.VERSION.SDK_INT >= Build.VERSION_CODES.ICE_CREAM_SANDWICH) { + assertEquals(mBindingObject.getPopupBackground(), + ((ColorDrawable) mView.getDropDownBackground()).getColor()); + + changeValues(); + + assertEquals(mBindingObject.getPopupBackground(), + ((ColorDrawable) mView.getDropDownBackground()).getColor()); + } + } +} diff --git a/tools/data-binding/integration-tests/TestApp/app/src/androidTest/java/android/databinding/testapp/BaseDataBinderTest.java b/tools/data-binding/integration-tests/TestApp/app/src/androidTest/java/android/databinding/testapp/BaseDataBinderTest.java new file mode 100644 index 0000000..ff85523 --- /dev/null +++ b/tools/data-binding/integration-tests/TestApp/app/src/androidTest/java/android/databinding/testapp/BaseDataBinderTest.java @@ -0,0 +1,100 @@ +/* + * 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 android.databinding.testapp; + +import android.databinding.ViewDataBinding; + +import android.content.Context; +import android.content.pm.ActivityInfo; +import android.os.Looper; +import android.test.ActivityInstrumentationTestCase2; +import android.util.Log; + +import java.io.PrintWriter; +import java.io.StringWriter; +import java.lang.reflect.Method; + +public class BaseDataBinderTest<T extends ViewDataBinding> + extends ActivityInstrumentationTestCase2<TestActivity> { + protected Class<T> mBinderClass; + private int mOrientation; + protected T mBinder; + + public BaseDataBinderTest(final Class<T> binderClass) { + this(binderClass, ActivityInfo.SCREEN_ORIENTATION_PORTRAIT); + } + + public BaseDataBinderTest(final Class<T> binderClass, final int orientation) { + super(TestActivity.class); + mBinderClass = binderClass; + mOrientation = orientation; + } + + @Override + protected void setUp() throws Exception { + super.setUp(); + getActivity().setRequestedOrientation(mOrientation); + createBinder(); + } + + public boolean isMainThread() { + return Looper.myLooper() == Looper.getMainLooper(); + } + + protected void createBinder() { + mBinder = null; + getActivity().runOnUiThread(new Runnable() { + @Override + public void run() { + Method method = null; + try { + method = mBinderClass.getMethod("inflate", Context.class); + mBinder = (T) method.invoke(null, getActivity()); + getActivity().setContentView(mBinder.getRoot()); + } catch (Exception e) { + StringWriter sw = new StringWriter(); + PrintWriter pw = new PrintWriter(sw); + e.printStackTrace(pw); + fail("Error creating binder: " + sw.toString()); + } + } + }); + if (!isMainThread()) { + getInstrumentation().waitForIdleSync(); + } + assertNotNull(mBinder); + } + + protected void assertMethod(Class<?> klass, String methodName) throws NoSuchMethodException { + assertEquals(klass, mBinder.getClass().getDeclaredMethod(methodName).getReturnType()); + } + + protected void assertField(Class<?> klass, String fieldName) throws NoSuchFieldException { + assertEquals(klass, mBinder.getClass().getDeclaredField(fieldName).getType()); + } + + protected void assertPublicField(Class<?> klass, String fieldName) throws NoSuchFieldException { + assertEquals(klass, mBinder.getClass().getField(fieldName).getType()); + } + + protected void assertNoField(String fieldName) { + Exception[] ex = new Exception[1]; + try { + mBinder.getClass().getField(fieldName); + } catch (NoSuchFieldException e) { + ex[0] = e; + } + assertNotNull(ex[0]); + } +} diff --git a/tools/data-binding/integration-tests/TestApp/app/src/androidTest/java/android/databinding/testapp/BaseLandDataBinderTest.java b/tools/data-binding/integration-tests/TestApp/app/src/androidTest/java/android/databinding/testapp/BaseLandDataBinderTest.java new file mode 100644 index 0000000..830e79b --- /dev/null +++ b/tools/data-binding/integration-tests/TestApp/app/src/androidTest/java/android/databinding/testapp/BaseLandDataBinderTest.java @@ -0,0 +1,25 @@ +/* + * 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 android.databinding.testapp; + +import android.databinding.ViewDataBinding; + +import android.content.pm.ActivityInfo; + +public class BaseLandDataBinderTest<T extends ViewDataBinding> extends BaseDataBinderTest<T> { + + public BaseLandDataBinderTest(Class<T> binderClass, int layoutId) { + super(binderClass, ActivityInfo.SCREEN_ORIENTATION_LANDSCAPE); + } +} diff --git a/tools/data-binding/integration-tests/TestApp/app/src/androidTest/java/android/databinding/testapp/BaseObservableTest.java b/tools/data-binding/integration-tests/TestApp/app/src/androidTest/java/android/databinding/testapp/BaseObservableTest.java new file mode 100644 index 0000000..c168ecb --- /dev/null +++ b/tools/data-binding/integration-tests/TestApp/app/src/androidTest/java/android/databinding/testapp/BaseObservableTest.java @@ -0,0 +1,84 @@ +/* + * 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 android.databinding.testapp; + +import android.databinding.BaseObservable; +import android.databinding.testapp.databinding.BasicBindingBinding; + +import android.databinding.Observable; +import android.databinding.OnPropertyChangedListener; + +import java.util.ArrayList; + +public class BaseObservableTest extends BaseDataBinderTest<BasicBindingBinding> { + private BaseObservable mObservable; + private ArrayList<Integer> mNotifications = new ArrayList<>(); + private OnPropertyChangedListener mListener = new OnPropertyChangedListener() { + @Override + public void onPropertyChanged(Observable observable, int i) { + assertEquals(mObservable, observable); + mNotifications.add(i); + } + }; + + public BaseObservableTest() { + super(BasicBindingBinding.class); + } + + @Override + protected void setUp() throws Exception { + mNotifications.clear(); + mObservable = new BaseObservable(); + } + + public void testAddListener() { + mObservable.notifyChange(); + assertTrue(mNotifications.isEmpty()); + mObservable.addOnPropertyChangedListener(mListener); + mObservable.notifyChange(); + assertFalse(mNotifications.isEmpty()); + } + + public void testRemoveListener() { + // test there is no exception when the listener isn't there + mObservable.removeOnPropertyChangedListener(mListener); + + mObservable.addOnPropertyChangedListener(mListener); + mObservable.notifyChange(); + mNotifications.clear(); + mObservable.removeOnPropertyChangedListener(mListener); + mObservable.notifyChange(); + assertTrue(mNotifications.isEmpty()); + + // test there is no exception when the listener isn't there + mObservable.removeOnPropertyChangedListener(mListener); + } + + public void testNotifyChange() { + mObservable.addOnPropertyChangedListener(mListener); + mObservable.notifyChange(); + assertEquals(1, mNotifications.size()); + assertEquals(0, (int) mNotifications.get(0)); + } + + public void testNotifyPropertyChanged() { + final int expectedId = 100; + mObservable.addOnPropertyChangedListener(mListener); + mObservable.notifyPropertyChanged(expectedId); + assertEquals(1, mNotifications.size()); + assertEquals(expectedId, (int) mNotifications.get(0)); + } +} diff --git a/tools/data-binding/integration-tests/TestApp/app/src/androidTest/java/android/databinding/testapp/BasicBindingTest.java b/tools/data-binding/integration-tests/TestApp/app/src/androidTest/java/android/databinding/testapp/BasicBindingTest.java new file mode 100644 index 0000000..024d7f2 --- /dev/null +++ b/tools/data-binding/integration-tests/TestApp/app/src/androidTest/java/android/databinding/testapp/BasicBindingTest.java @@ -0,0 +1,75 @@ +/* + * 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 android.databinding.testapp; + +import android.databinding.testapp.databinding.BasicBindingBinding; + +import android.test.UiThreadTest; + +public class BasicBindingTest extends BaseDataBinderTest<BasicBindingBinding> { + public BasicBindingTest() { + super(BasicBindingBinding.class); + } + + @UiThreadTest + public void testTextViewContentInInitialization() { + assertAB("X", "Y"); + } + + @UiThreadTest + public void testNullValuesInInitialization() { + assertAB(null, null); + } + + @UiThreadTest + public void testSecondIsNullInInitialization() { + assertAB(null, "y"); + } + + @UiThreadTest + public void testFirstIsNullInInitialization() { + assertAB("x", null); + } + + @UiThreadTest + public void testTextViewContent() { + assertAB("X", "Y"); + } + + @UiThreadTest + public void testNullValues() { + assertAB(null, null); + } + + @UiThreadTest + public void testSecondIsNull() { + assertAB(null, "y"); + } + + @UiThreadTest + public void testFirstIsNull() { + assertAB("x", null); + } + + private void assertAB(String a, String b) { + mBinder.setA(a); + mBinder.setB(b); + rebindAndAssert(a + b); + } + + private void rebindAndAssert(String text) { + mBinder.executePendingBindings(); + assertEquals(text, mBinder.textView.getText().toString()); + } +} diff --git a/tools/data-binding/integration-tests/TestApp/app/src/androidTest/java/android/databinding/testapp/BasicDependantBindingTest.java b/tools/data-binding/integration-tests/TestApp/app/src/androidTest/java/android/databinding/testapp/BasicDependantBindingTest.java new file mode 100644 index 0000000..536a00a --- /dev/null +++ b/tools/data-binding/integration-tests/TestApp/app/src/androidTest/java/android/databinding/testapp/BasicDependantBindingTest.java @@ -0,0 +1,80 @@ +/* + * 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 android.databinding.testapp; + +import android.databinding.testapp.databinding.BasicDependantBindingBinding; +import android.databinding.testapp.vo.NotBindableVo; + +import android.test.UiThreadTest; + +import java.util.ArrayList; +import java.util.List; + +public class BasicDependantBindingTest extends BaseDataBinderTest<BasicDependantBindingBinding> { + + public BasicDependantBindingTest() { + super(BasicDependantBindingBinding.class); + } + + public List<NotBindableVo> permutations(String value) { + List<NotBindableVo> result = new ArrayList<>(); + result.add(null); + result.add(new NotBindableVo(null)); + result.add(new NotBindableVo(value)); + return result; + } + + @UiThreadTest + public void testAllPermutations() { + List<NotBindableVo> obj1s = permutations("a"); + List<NotBindableVo> obj2s = permutations("b"); + for (NotBindableVo obj1 : obj1s) { + for (NotBindableVo obj2 : obj2s) { + createBinder(); //get a new one + testWith(obj1, obj2); + createBinder(); + mBinder.executePendingBindings(); + testWith(obj1, obj2); + } + } + } + + private void testWith(NotBindableVo obj1, NotBindableVo obj2) { + mBinder.setObj1(obj1); + mBinder.setObj2(obj2); + mBinder.executePendingBindings(); + assertValues(safeGet(obj1), safeGet(obj2), + obj1 == null ? "" : obj1.mergeStringFields(obj2), + obj2 == null ? "" : obj2.mergeStringFields(obj1), + (obj1 == null ? null : obj1.getStringValue()) + + (obj2 == null ? null : obj2.getStringValue()) + ); + } + + private String safeGet(NotBindableVo vo) { + if (vo == null || vo.getStringValue() == null) { + return ""; + } + return vo.getStringValue(); + } + + private void assertValues(String textView1, String textView2, + String mergedView1, String mergedView2, String rawMerge) { + assertEquals(textView1, mBinder.textView1.getText().toString()); + assertEquals(textView2, mBinder.textView2.getText().toString()); + assertEquals(mergedView1, mBinder.mergedTextView1.getText().toString()); + assertEquals(mergedView2, mBinder.mergedTextView2.getText().toString()); + assertEquals(rawMerge, mBinder.rawStringMerge.getText().toString()); + } +} diff --git a/tools/data-binding/integration-tests/TestApp/app/src/androidTest/java/android/databinding/testapp/BindToFinalFieldTest.java b/tools/data-binding/integration-tests/TestApp/app/src/androidTest/java/android/databinding/testapp/BindToFinalFieldTest.java new file mode 100644 index 0000000..73f3811 --- /dev/null +++ b/tools/data-binding/integration-tests/TestApp/app/src/androidTest/java/android/databinding/testapp/BindToFinalFieldTest.java @@ -0,0 +1,38 @@ +/* + * 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 android.databinding.testapp; + +import android.databinding.testapp.databinding.BindToFinalBinding; +import android.databinding.testapp.vo.PublicFinalTestVo; + +import android.test.UiThreadTest; +import android.widget.TextView; + +public class BindToFinalFieldTest extends BaseDataBinderTest<BindToFinalBinding>{ + + public BindToFinalFieldTest() { + super(BindToFinalBinding.class); + } + + @UiThreadTest + public void testSimple() { + final PublicFinalTestVo vo = new PublicFinalTestVo(R.string.app_name); + mBinder.setObj(vo); + mBinder.executePendingBindings(); + final TextView textView = (TextView) mBinder.getRoot().findViewById(R.id.text_view); + assertEquals(getActivity().getResources().getString(R.string.app_name), textView.getText().toString()); + } + + +} diff --git a/tools/data-binding/integration-tests/TestApp/app/src/androidTest/java/android/databinding/testapp/BindToFinalObservableFieldTest.java b/tools/data-binding/integration-tests/TestApp/app/src/androidTest/java/android/databinding/testapp/BindToFinalObservableFieldTest.java new file mode 100644 index 0000000..72c2efb --- /dev/null +++ b/tools/data-binding/integration-tests/TestApp/app/src/androidTest/java/android/databinding/testapp/BindToFinalObservableFieldTest.java @@ -0,0 +1,43 @@ +/* + * 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 android.databinding.testapp; + +import android.databinding.testapp.databinding.BindToFinalObservableBinding; +import android.databinding.testapp.vo.PublicFinalWithObservableTestVo; + +import android.test.UiThreadTest; +import android.widget.TextView; + +public class BindToFinalObservableFieldTest extends BaseDataBinderTest<BindToFinalObservableBinding>{ + + public BindToFinalObservableFieldTest() { + super(BindToFinalObservableBinding.class); + } + + @UiThreadTest + public void testSimple() { + final PublicFinalWithObservableTestVo vo = new PublicFinalWithObservableTestVo(R.string.app_name); + mBinder.setObj(vo); + mBinder.executePendingBindings(); + final TextView textView = (TextView) mBinder.getRoot().findViewById(R.id.text_view); + assertEquals(getActivity().getResources().getString(R.string.app_name), textView.getText().toString()); + vo.myFinalVo.setVal(R.string.rain); + mBinder.executePendingBindings(); + assertEquals("The field should be observed and its notify event should've invalidated" + + " binder flags.", getActivity().getResources().getString(R.string.rain), + textView.getText().toString()); + } + + +} diff --git a/tools/data-binding/integration-tests/TestApp/app/src/androidTest/java/android/databinding/testapp/BindingAdapterTestBase.java b/tools/data-binding/integration-tests/TestApp/app/src/androidTest/java/android/databinding/testapp/BindingAdapterTestBase.java new file mode 100644 index 0000000..730ebb2 --- /dev/null +++ b/tools/data-binding/integration-tests/TestApp/app/src/androidTest/java/android/databinding/testapp/BindingAdapterTestBase.java @@ -0,0 +1,76 @@ +/* + * 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 android.databinding.testapp; + +import android.databinding.ViewDataBinding; +import android.databinding.testapp.vo.BindingAdapterBindingObject; + +import java.lang.reflect.InvocationTargetException; +import java.lang.reflect.Method; + +public class BindingAdapterTestBase<T extends ViewDataBinding, V extends BindingAdapterBindingObject> + extends BaseDataBinderTest<T> { + private Class<V> mBindingObjectClass; + + protected V mBindingObject; + + private Method mSetMethod; + + public BindingAdapterTestBase(Class<T> binderClass, Class<V> observableClass, int layoutId) { + super(binderClass); + mBindingObjectClass = observableClass; + try { + mSetMethod = binderClass.getDeclaredMethod("setObj", observableClass); + } catch (NoSuchMethodException e) { + throw new RuntimeException(e); + } + } + + @Override + protected void setUp() throws Exception { + super.setUp(); + try { + runTestOnUiThread(new Runnable() { + @Override + public void run() { + try { + mBindingObject = mBindingObjectClass.newInstance(); + mSetMethod.invoke(mBinder, mBindingObject); + mBinder.executePendingBindings(); + } catch (IllegalAccessException e) { + throw new RuntimeException(e); + } catch (InvocationTargetException e) { + throw new RuntimeException(e); + } catch (InstantiationException e) { + throw new RuntimeException(e); + } + } + }); + } catch (Throwable throwable) { + throw new Exception(throwable); + } + } + + protected void changeValues() throws Throwable { + runTestOnUiThread(new Runnable() { + @Override + public void run() { + mBindingObject.changeValues(); + mBinder.executePendingBindings(); + } + }); + } +} diff --git a/tools/data-binding/integration-tests/TestApp/app/src/androidTest/java/android/databinding/testapp/BracketTest.java b/tools/data-binding/integration-tests/TestApp/app/src/androidTest/java/android/databinding/testapp/BracketTest.java new file mode 100644 index 0000000..20d90ed --- /dev/null +++ b/tools/data-binding/integration-tests/TestApp/app/src/androidTest/java/android/databinding/testapp/BracketTest.java @@ -0,0 +1,76 @@ +/* + * 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 android.databinding.testapp; + +import android.databinding.testapp.databinding.BracketTestBinding; + +import android.test.UiThreadTest; +import android.util.LongSparseArray; +import android.util.SparseArray; +import android.util.SparseBooleanArray; +import android.util.SparseIntArray; +import android.util.SparseLongArray; + +public class BracketTest extends BaseDataBinderTest<BracketTestBinding> { + private String[] mArray = { + "Hello World" + }; + + private SparseArray<String> mSparseArray = new SparseArray<>(); + private SparseIntArray mSparseIntArray = new SparseIntArray(); + private SparseBooleanArray mSparseBooleanArray = new SparseBooleanArray(); + private SparseLongArray mSparseLongArray = new SparseLongArray(); + private LongSparseArray<String> mLongSparseArray = new LongSparseArray<>(); + + public BracketTest() { + super(BracketTestBinding.class); + mSparseArray.put(0, "Hello"); + mLongSparseArray.put(0, "World"); + mSparseIntArray.put(0, 100); + mSparseBooleanArray.put(0, true); + mSparseLongArray.put(0, 5); + } + + @Override + protected void setUp() throws Exception { + super.setUp(); + try { + runTestOnUiThread(new Runnable() { + @Override + public void run() { + mBinder.setArray(mArray); + mBinder.setSparseArray(mSparseArray); + mBinder.setSparseIntArray(mSparseIntArray); + mBinder.setSparseBooleanArray(mSparseBooleanArray); + mBinder.setSparseLongArray(mSparseLongArray); + mBinder.setLongSparseArray(mLongSparseArray); + + mBinder.executePendingBindings(); + } + }); + } catch (Throwable throwable) { + throw new Exception(throwable); + } + } + + @UiThreadTest + public void testBrackets() { + assertEquals("Hello World", mBinder.arrayText.getText().toString()); + assertEquals("Hello", mBinder.sparseArrayText.getText().toString()); + assertEquals("World", mBinder.longSparseArrayText.getText().toString()); + assertEquals("100", mBinder.sparseIntArrayText.getText().toString()); + assertEquals("true", mBinder.sparseBooleanArrayText.getText().toString()); + assertEquals("5", mBinder.sparseLongArrayText.getText().toString()); + } +} diff --git a/tools/data-binding/integration-tests/TestApp/app/src/androidTest/java/android/databinding/testapp/CastTest.java b/tools/data-binding/integration-tests/TestApp/app/src/androidTest/java/android/databinding/testapp/CastTest.java new file mode 100644 index 0000000..2fce2d4 --- /dev/null +++ b/tools/data-binding/integration-tests/TestApp/app/src/androidTest/java/android/databinding/testapp/CastTest.java @@ -0,0 +1,62 @@ +/* + * 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 android.databinding.testapp; + +import android.databinding.testapp.databinding.CastTestBinding; + +import android.support.v4.util.ArrayMap; +import android.test.UiThreadTest; + +import java.util.ArrayList; + +public class CastTest extends BaseDataBinderTest<CastTestBinding> { + ArrayList<String> mValues = new ArrayList<>(); + ArrayMap<String, String> mMap = new ArrayMap<>(); + + public CastTest() { + super(CastTestBinding.class); + } + + @Override + protected void setUp() throws Exception { + super.setUp(); + try { + runTestOnUiThread(new Runnable() { + @Override + public void run() { + mValues.clear(); + mValues.add("hello"); + mValues.add("world"); + mValues.add("not seen"); + mMap.clear(); + mMap.put("hello", "world"); + mMap.put("world", "hello"); + mBinder.setList(mValues); + mBinder.setMap(mMap); + mBinder.executePendingBindings(); + } + }); + } catch (Throwable throwable) { + throw new Exception(throwable); + } + } + + @UiThreadTest + public void testCast() throws Throwable { + assertEquals("hello", mBinder.textView0.getText().toString()); + assertEquals("world", mBinder.textView1.getText().toString()); + } +} diff --git a/tools/data-binding/integration-tests/TestApp/app/src/androidTest/java/android/databinding/testapp/CheckedTextViewBindingAdapterTest.java b/tools/data-binding/integration-tests/TestApp/app/src/androidTest/java/android/databinding/testapp/CheckedTextViewBindingAdapterTest.java new file mode 100644 index 0000000..68464e6 --- /dev/null +++ b/tools/data-binding/integration-tests/TestApp/app/src/androidTest/java/android/databinding/testapp/CheckedTextViewBindingAdapterTest.java @@ -0,0 +1,56 @@ +/* + * 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 android.databinding.testapp; + +import android.databinding.testapp.databinding.CheckedTextViewAdapterTestBinding; +import android.databinding.testapp.vo.CheckedTextViewBindingObject; + +import android.graphics.drawable.ColorDrawable; +import android.os.Build; +import android.widget.CheckedTextView; + +public class CheckedTextViewBindingAdapterTest extends + BindingAdapterTestBase<CheckedTextViewAdapterTestBinding, CheckedTextViewBindingObject> { + + CheckedTextView mView; + + public CheckedTextViewBindingAdapterTest() { + super(CheckedTextViewAdapterTestBinding.class, CheckedTextViewBindingObject.class, + R.layout.checked_text_view_adapter_test); + } + + @Override + protected void setUp() throws Exception { + super.setUp(); + mView = mBinder.view; + } + + public void testView() throws Throwable { + if (Build.VERSION.SDK_INT >= Build.VERSION_CODES.LOLLIPOP) { + assertEquals(mBindingObject.getCheckMark().getColor(), + ((ColorDrawable) mView.getCheckMarkDrawable()).getColor()); + assertEquals(mBindingObject.getCheckMarkTint(), + mView.getCheckMarkTintList().getDefaultColor()); + + changeValues(); + + assertEquals(mBindingObject.getCheckMark().getColor(), + ((ColorDrawable) mView.getCheckMarkDrawable()).getColor()); + assertEquals(mBindingObject.getCheckMarkTint(), + mView.getCheckMarkTintList().getDefaultColor()); + } + } +} diff --git a/tools/data-binding/integration-tests/TestApp/app/src/androidTest/java/android/databinding/testapp/CompoundButtonBindingAdapterTest.java b/tools/data-binding/integration-tests/TestApp/app/src/androidTest/java/android/databinding/testapp/CompoundButtonBindingAdapterTest.java new file mode 100644 index 0000000..7845923 --- /dev/null +++ b/tools/data-binding/integration-tests/TestApp/app/src/androidTest/java/android/databinding/testapp/CompoundButtonBindingAdapterTest.java @@ -0,0 +1,46 @@ +/* + * 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 android.databinding.testapp; + +import android.databinding.testapp.databinding.CompoundButtonAdapterTestBinding; +import android.databinding.testapp.vo.CompoundButtonBindingObject; + +import android.widget.CompoundButton; + +public class CompoundButtonBindingAdapterTest extends + BindingAdapterTestBase<CompoundButtonAdapterTestBinding, CompoundButtonBindingObject> { + + CompoundButton mView; + + public CompoundButtonBindingAdapterTest() { + super(CompoundButtonAdapterTestBinding.class, CompoundButtonBindingObject.class, + R.layout.compound_button_adapter_test); + } + + @Override + protected void setUp() throws Exception { + super.setUp(); + mView = mBinder.view; + } + + public void testCompoundButton() throws Throwable { + assertEquals(mBindingObject.getButtonTint(), mView.getButtonTintList().getDefaultColor()); + + changeValues(); + + assertEquals(mBindingObject.getButtonTint(), mView.getButtonTintList().getDefaultColor()); + } +} diff --git a/tools/data-binding/integration-tests/TestApp/app/src/androidTest/java/android/databinding/testapp/ConditionalBindingTest.java b/tools/data-binding/integration-tests/TestApp/app/src/androidTest/java/android/databinding/testapp/ConditionalBindingTest.java new file mode 100644 index 0000000..61839db --- /dev/null +++ b/tools/data-binding/integration-tests/TestApp/app/src/androidTest/java/android/databinding/testapp/ConditionalBindingTest.java @@ -0,0 +1,34 @@ +package android.databinding.testapp; + +import android.databinding.testapp.databinding.ConditionalBindingBinding; +import android.databinding.testapp.vo.NotBindableVo; + +import android.test.UiThreadTest; + +public class ConditionalBindingTest extends BaseDataBinderTest<ConditionalBindingBinding>{ + + public ConditionalBindingTest() { + super(ConditionalBindingBinding.class); + } + + @UiThreadTest + public void test1() { + testCorrectness(true, true); + } + + private void testCorrectness(boolean cond1, boolean cond2) { + NotBindableVo o1 = new NotBindableVo("a"); + NotBindableVo o2 = new NotBindableVo("b"); + NotBindableVo o3 = new NotBindableVo("c"); + mBinder.setObj1(o1); + mBinder.setObj2(o2); + mBinder.setObj3(o3); + mBinder.setCond1(cond1); + mBinder.setCond2(cond2); + mBinder.executePendingBindings(); + final String text = mBinder.textView.getText().toString(); + assertEquals(cond1 && cond2, "a".equals(text)); + assertEquals(cond1 && !cond2, "b".equals(text)); + assertEquals(!cond1, "c".equals(text)); + } +} diff --git a/tools/data-binding/integration-tests/TestApp/app/src/androidTest/java/android/databinding/testapp/FindMethodTest.java b/tools/data-binding/integration-tests/TestApp/app/src/androidTest/java/android/databinding/testapp/FindMethodTest.java new file mode 100644 index 0000000..ca826fa --- /dev/null +++ b/tools/data-binding/integration-tests/TestApp/app/src/androidTest/java/android/databinding/testapp/FindMethodTest.java @@ -0,0 +1,113 @@ +/* + * 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 android.databinding.testapp; + +import android.databinding.testapp.databinding.FindMethodTestBinding; +import android.databinding.testapp.vo.FindMethodBindingObject; + +import android.test.UiThreadTest; +import android.widget.TextView; + +public class FindMethodTest + extends BindingAdapterTestBase<FindMethodTestBinding, FindMethodBindingObject> { + + public FindMethodTest() { + super(FindMethodTestBinding.class, FindMethodBindingObject.class, R.layout.find_method_test); + } + + public void testNoArg() throws Throwable { + TextView textView = mBinder.textView6; + assertEquals("no arg", textView.getText().toString()); + } + + public void testIntArg() throws Throwable { + TextView textView = mBinder.textView0; + assertEquals("1", textView.getText().toString()); + } + + public void testFloatArg() throws Throwable { + TextView textView = mBinder.textView1; + assertEquals("1.25", textView.getText().toString()); + } + + public void testStringArg() throws Throwable { + TextView textView = mBinder.textView2; + assertEquals("hello", textView.getText().toString()); + } + + public void testBoxedArg() throws Throwable { + TextView textView = mBinder.textView3; + assertEquals("1", textView.getText().toString()); + } + + public void testInheritedMethod() throws Throwable { + TextView textView = mBinder.textView4; + assertEquals("base", textView.getText().toString()); + } + + public void testInheritedMethodInt() throws Throwable { + TextView textView = mBinder.textView5; + assertEquals("base 2", textView.getText().toString()); + } + + public void testStaticMethod() throws Throwable { + TextView textView = mBinder.textView7; + assertEquals("world", textView.getText().toString()); + } + + public void testStaticField() throws Throwable { + TextView textView = mBinder.textView8; + assertEquals("hello world", textView.getText().toString()); + } + + public void testImportStaticMethod() throws Throwable { + TextView textView = mBinder.textView9; + assertEquals("world", textView.getText().toString()); + } + + public void testImportStaticField() throws Throwable { + TextView textView = mBinder.textView10; + assertEquals("hello world", textView.getText().toString()); + } + + public void testAliasStaticMethod() throws Throwable { + TextView textView = mBinder.textView11; + assertEquals("world", textView.getText().toString()); + } + + public void testAliasStaticField() throws Throwable { + TextView textView = mBinder.textView12; + assertEquals("hello world", textView.getText().toString()); + } + + @UiThreadTest + public void testImports() throws Throwable { + mBinder.setObj2(new FindMethodBindingObject.Bar<String>()); + mBinder.executePendingBindings(); + TextView textView = mBinder.textView15; + assertEquals("hello", textView.getText().toString()); + } + + @UiThreadTest + public void testConfusingMethods() throws Throwable { + assertEquals("1", mBinder.textView16.getText().toString()); + assertEquals("1", mBinder.textView17.getText().toString()); + assertEquals("hello", mBinder.textView18.getText().toString()); + assertEquals("yay", mBinder.textView19.getText().toString()); + assertEquals("hello", mBinder.textView20.getText().toString()); + assertEquals("hello", mBinder.textView21.getText().toString()); + } +} diff --git a/tools/data-binding/integration-tests/TestApp/app/src/androidTest/java/android/databinding/testapp/FrameLayoutBindingAdapterTest.java b/tools/data-binding/integration-tests/TestApp/app/src/androidTest/java/android/databinding/testapp/FrameLayoutBindingAdapterTest.java new file mode 100644 index 0000000..1f2f835 --- /dev/null +++ b/tools/data-binding/integration-tests/TestApp/app/src/androidTest/java/android/databinding/testapp/FrameLayoutBindingAdapterTest.java @@ -0,0 +1,51 @@ +/* + * 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 android.databinding.testapp; + +import android.databinding.testapp.databinding.FrameLayoutAdapterTestBinding; +import android.databinding.testapp.vo.FrameLayoutBindingObject; + +import android.os.Build; +import android.widget.FrameLayout; + +public class FrameLayoutBindingAdapterTest + extends BindingAdapterTestBase<FrameLayoutAdapterTestBinding, FrameLayoutBindingObject> { + + FrameLayout mView; + + public FrameLayoutBindingAdapterTest() { + super(FrameLayoutAdapterTestBinding.class, FrameLayoutBindingObject.class, + R.layout.frame_layout_adapter_test); + } + + @Override + protected void setUp() throws Exception { + super.setUp(); + mView = mBinder.view; + } + + public void testTint() throws Throwable { + if (Build.VERSION.SDK_INT >= Build.VERSION_CODES.LOLLIPOP) { + assertEquals(mBindingObject.getForegroundTint(), + mView.getForegroundTintList().getDefaultColor()); + + changeValues(); + + assertEquals(mBindingObject.getForegroundTint(), + mView.getForegroundTintList().getDefaultColor()); + } + } +} diff --git a/tools/data-binding/integration-tests/TestApp/app/src/androidTest/java/android/databinding/testapp/ImageViewBindingAdapterTest.java b/tools/data-binding/integration-tests/TestApp/app/src/androidTest/java/android/databinding/testapp/ImageViewBindingAdapterTest.java new file mode 100644 index 0000000..e4dcdfe --- /dev/null +++ b/tools/data-binding/integration-tests/TestApp/app/src/androidTest/java/android/databinding/testapp/ImageViewBindingAdapterTest.java @@ -0,0 +1,50 @@ +/* + * 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 android.databinding.testapp; + +import android.databinding.testapp.databinding.ImageViewAdapterTestBinding; +import android.databinding.testapp.vo.ImageViewBindingObject; + +import android.widget.ImageView; + +public class ImageViewBindingAdapterTest + extends BindingAdapterTestBase<ImageViewAdapterTestBinding, ImageViewBindingObject> { + + ImageView mView; + + public ImageViewBindingAdapterTest() { + super(ImageViewAdapterTestBinding.class, ImageViewBindingObject.class, + R.layout.image_view_adapter_test); + } + + @Override + protected void setUp() throws Exception { + super.setUp(); + mView = mBinder.view; + } + + public void testImageView() throws Throwable { + assertEquals(mBindingObject.getSrc(), mView.getDrawable()); + assertEquals(mBindingObject.getTint(), mView.getImageTintList().getDefaultColor()); + assertEquals(mBindingObject.getTintMode(), mView.getImageTintMode()); + + changeValues(); + + assertEquals(mBindingObject.getSrc(), mView.getDrawable()); + assertEquals(mBindingObject.getTint(), mView.getImageTintList().getDefaultColor()); + assertEquals(mBindingObject.getTintMode(), mView.getImageTintMode()); + } +} diff --git a/tools/data-binding/integration-tests/TestApp/app/src/androidTest/java/android/databinding/testapp/IncludeTagTest.java b/tools/data-binding/integration-tests/TestApp/app/src/androidTest/java/android/databinding/testapp/IncludeTagTest.java new file mode 100644 index 0000000..48d6689 --- /dev/null +++ b/tools/data-binding/integration-tests/TestApp/app/src/androidTest/java/android/databinding/testapp/IncludeTagTest.java @@ -0,0 +1,45 @@ +/* + * 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 android.databinding.testapp; + +import android.databinding.testapp.databinding.LayoutWithIncludeBinding; +import android.databinding.testapp.vo.NotBindableVo; + +import android.test.UiThreadTest; +import android.widget.TextView; + +public class IncludeTagTest extends BaseDataBinderTest<LayoutWithIncludeBinding> { + + public IncludeTagTest() { + super(LayoutWithIncludeBinding.class); + } + + @UiThreadTest + public void testIncludeTag() { + NotBindableVo vo = new NotBindableVo(3, "a"); + mBinder.setOuterObject(vo); + mBinder.executePendingBindings(); + final TextView outerText = (TextView) mBinder.getRoot().findViewById(R.id.outerTextView); + assertEquals("a", outerText.getText()); + final TextView innerText = (TextView) mBinder.getRoot().findViewById(R.id.innerTextView); + assertEquals("modified 3a", innerText.getText().toString()); + + vo.setIntValue(5); + vo.setStringValue("b"); + mBinder.invalidateAll(); + mBinder.executePendingBindings(); + assertEquals("b", outerText.getText()); + assertEquals("modified 5b", innerText.getText().toString()); + } +} diff --git a/tools/data-binding/integration-tests/TestApp/app/src/androidTest/java/android/databinding/testapp/InnerCannotReadDependencyTest.java b/tools/data-binding/integration-tests/TestApp/app/src/androidTest/java/android/databinding/testapp/InnerCannotReadDependencyTest.java new file mode 100644 index 0000000..68cf06b --- /dev/null +++ b/tools/data-binding/integration-tests/TestApp/app/src/androidTest/java/android/databinding/testapp/InnerCannotReadDependencyTest.java @@ -0,0 +1,49 @@ +/* + * 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 android.databinding.testapp; + +import android.databinding.testapp.databinding.InnerCannotReadDependencyBinding; +import android.databinding.testapp.vo.BasicObject; +import android.os.Debug; +import android.test.UiThreadTest; + +import org.junit.Test; + +public class InnerCannotReadDependencyTest extends + BaseDataBinderTest<InnerCannotReadDependencyBinding> { + + public InnerCannotReadDependencyTest() { + super(InnerCannotReadDependencyBinding.class); + } + + @UiThreadTest + public void testBinding() { + BasicObject object = new BasicObject(); + object.setField1("a"); + mBinder.setObj(object); + mBinder.executePendingBindings(); + assertEquals("a ", mBinder.textView.getText().toString()); + object.setField1(null); + mBinder.executePendingBindings(); + assertEquals("null ", mBinder.textView.getText().toString()); + object.setField2("b"); + mBinder.executePendingBindings(); + assertEquals("null b", mBinder.textView.getText().toString()); + object.setField1("c"); + mBinder.executePendingBindings(); + assertEquals("c b", mBinder.textView.getText().toString()); + } +} diff --git a/tools/data-binding/integration-tests/TestApp/app/src/androidTest/java/android/databinding/testapp/LeakTest.java b/tools/data-binding/integration-tests/TestApp/app/src/androidTest/java/android/databinding/testapp/LeakTest.java new file mode 100644 index 0000000..0455a46 --- /dev/null +++ b/tools/data-binding/integration-tests/TestApp/app/src/androidTest/java/android/databinding/testapp/LeakTest.java @@ -0,0 +1,130 @@ +/* + * 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 android.databinding.testapp; + +import android.databinding.testapp.databinding.LeakTestBinding; +import android.test.ActivityInstrumentationTestCase2; +import android.util.Log; +import android.widget.FrameLayout; + +import java.lang.ref.WeakReference; + +public class LeakTest extends ActivityInstrumentationTestCase2<TestActivity> { + WeakReference<LeakTestBinding> mWeakReference = new WeakReference<LeakTestBinding>(null); + + public LeakTest() { + super(TestActivity.class); + } + + @Override + protected void setUp() throws Exception { + super.setUp(); + + try { + getActivity().runOnUiThread(new Runnable() { + @Override + public void run() { + try { + LeakTestBinding binding = LeakTestBinding.inflate(getActivity()); + getActivity().setContentView(binding.getRoot()); + mWeakReference = new WeakReference<LeakTestBinding>(binding); + binding.setName("hello world"); + binding.executePendingBindings(); + } catch (Exception e) { + e.printStackTrace(); + throw e; + } + } + }); + getInstrumentation().waitForIdleSync(); + } catch (Throwable t) { + throw new Exception(t); + } + } + + public void testBindingLeak() throws Throwable { + assertNotNull(mWeakReference.get()); + runTestOnUiThread(new Runnable() { + @Override + public void run() { + getActivity().setContentView(new FrameLayout(getActivity())); + } + }); + WeakReference<Object> canary = new WeakReference<Object>(new Object()); + while (canary.get() != null) { + byte[] b = new byte[1024 * 1024]; + System.gc(); + } + assertNull(mWeakReference.get()); + } + + // Test to ensure that when the View is detached that it doesn't rebind + // the dirty Views. The rebind should happen only after the root view is + // reattached. + public void testNoChangeWhenDetached() throws Throwable { + final LeakTestBinding binding = mWeakReference.get(); + final AnimationWatcher watcher = new AnimationWatcher(); + + runTestOnUiThread(new Runnable() { + @Override + public void run() { + getActivity().setContentView(new FrameLayout(getActivity())); + binding.setName("goodbye world"); + binding.getRoot().postOnAnimation(watcher); + } + }); + + watcher.waitForAnimationThread(); + + runTestOnUiThread(new Runnable() { + @Override + public void run() { + assertEquals("hello world", binding.textView.getText().toString()); + getActivity().setContentView(binding.getRoot()); + binding.getRoot().postOnAnimation(watcher); + } + }); + + watcher.waitForAnimationThread(); + + runTestOnUiThread(new Runnable() { + @Override + public void run() { + assertEquals("goodbye world", binding.textView.getText().toString()); + } + }); + } + + private static class AnimationWatcher implements Runnable { + private boolean mWaiting = true; + + public void waitForAnimationThread() throws InterruptedException { + synchronized (this) { + while (mWaiting) { + this.wait(); + } + mWaiting = true; + } + } + + + @Override + public void run() { + synchronized (this) { + mWaiting = false; + this.notifyAll(); + } + } + } +} diff --git a/tools/data-binding/integration-tests/TestApp/app/src/androidTest/java/android/databinding/testapp/LinearLayoutBindingAdapterTest.java b/tools/data-binding/integration-tests/TestApp/app/src/androidTest/java/android/databinding/testapp/LinearLayoutBindingAdapterTest.java new file mode 100644 index 0000000..c601564 --- /dev/null +++ b/tools/data-binding/integration-tests/TestApp/app/src/androidTest/java/android/databinding/testapp/LinearLayoutBindingAdapterTest.java @@ -0,0 +1,51 @@ +/* + * 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 android.databinding.testapp; + +import android.databinding.testapp.databinding.LinearLayoutAdapterTestBinding; +import android.databinding.testapp.vo.LinearLayoutBindingObject; + +import android.os.Build; +import android.widget.LinearLayout; + +public class LinearLayoutBindingAdapterTest + extends BindingAdapterTestBase<LinearLayoutAdapterTestBinding, LinearLayoutBindingObject> { + + LinearLayout mView; + + public LinearLayoutBindingAdapterTest() { + super(LinearLayoutAdapterTestBinding.class, LinearLayoutBindingObject.class, + R.layout.linear_layout_adapter_test); + } + + @Override + protected void setUp() throws Exception { + super.setUp(); + mView = mBinder.view; + } + + public void testMeasureWithLargestChild() throws Throwable { + if (Build.VERSION.SDK_INT >= Build.VERSION_CODES.HONEYCOMB) { + assertEquals(mBindingObject.isMeasureWithLargestChild(), + mView.isMeasureWithLargestChildEnabled()); + + changeValues(); + + assertEquals(mBindingObject.isMeasureWithLargestChild(), + mView.isMeasureWithLargestChildEnabled()); + } + } +} diff --git a/tools/data-binding/integration-tests/TestApp/app/src/androidTest/java/android/databinding/testapp/ListChangeRegistryTest.java b/tools/data-binding/integration-tests/TestApp/app/src/androidTest/java/android/databinding/testapp/ListChangeRegistryTest.java new file mode 100644 index 0000000..365eab9 --- /dev/null +++ b/tools/data-binding/integration-tests/TestApp/app/src/androidTest/java/android/databinding/testapp/ListChangeRegistryTest.java @@ -0,0 +1,237 @@ +/* + * 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 android.databinding.testapp; + +import android.databinding.ListChangeRegistry; +import android.databinding.testapp.databinding.BasicBindingBinding; + +import android.databinding.OnListChangedListener; + +public class ListChangeRegistryTest extends BaseDataBinderTest<BasicBindingBinding> { + + private ListChangeRegistry mListChangeRegistry; + + private int mCallCount; + + public ListChangeRegistryTest() { + super(BasicBindingBinding.class); + } + + @Override + protected void setUp() throws Exception { + super.setUp(); + mListChangeRegistry = new ListChangeRegistry(); + mCallCount = 0; + } + + @Override + protected void tearDown() throws Exception { + super.tearDown(); + mListChangeRegistry = null; + } + + public void testNotifyChangedAll() { + OnListChangedListener listChangedListener = new OnListChangedListener() { + @Override + public void onChanged() { + mCallCount++; + } + + @Override + public void onItemRangeChanged(int start, int count) { + fail("onItemRangeChanged should not be called"); + } + + @Override + public void onItemRangeInserted(int start, int count) { + fail("onItemRangeInserted should not be called"); + } + + @Override + public void onItemRangeMoved(int from, int to, int count) { + fail("onItemRangeMoved should not be called"); + } + + @Override + public void onItemRangeRemoved(int start, int count) { + fail("onItemRangeRemoved should not be called"); + } + }; + + mListChangeRegistry.add(listChangedListener); + assertEquals(0, mCallCount); + mListChangeRegistry.notifyChanged(null); + assertEquals(1, mCallCount); + } + + public void testNotifyChanged() { + final int expectedStart = 10; + final int expectedCount = 3; + + OnListChangedListener listChangedListener = new OnListChangedListener() { + @Override + public void onChanged() { + fail("onChanged should not be called"); + } + + @Override + public void onItemRangeChanged(int start, int count) { + assertEquals(expectedStart, start); + assertEquals(expectedCount, count); + mCallCount++; + } + + @Override + public void onItemRangeInserted(int start, int count) { + fail("onItemRangeInserted should not be called"); + } + + @Override + public void onItemRangeMoved(int from, int to, int count) { + fail("onItemRangeMoved should not be called"); + } + + @Override + public void onItemRangeRemoved(int start, int count) { + fail("onItemRangeRemoved should not be called"); + } + }; + + mListChangeRegistry.add(listChangedListener); + assertEquals(0, mCallCount); + mListChangeRegistry.notifyChanged(null, expectedStart, expectedCount); + assertEquals(1, mCallCount); + } + + public void testNotifyInserted() { + final int expectedStart = 10; + final int expectedCount = 3; + + OnListChangedListener listChangedListener = new OnListChangedListener() { + @Override + public void onChanged() { + fail("onChanged should not be called"); + } + + @Override + public void onItemRangeChanged(int start, int count) { + fail("onItemRangeChanged should not be called"); + } + + @Override + public void onItemRangeInserted(int start, int count) { + assertEquals(expectedStart, start); + assertEquals(expectedCount, count); + mCallCount++; + } + + @Override + public void onItemRangeMoved(int from, int to, int count) { + fail("onItemRangeMoved should not be called"); + } + + @Override + public void onItemRangeRemoved(int start, int count) { + fail("onItemRangeRemoved should not be called"); + } + }; + + mListChangeRegistry.add(listChangedListener); + assertEquals(0, mCallCount); + mListChangeRegistry.notifyInserted(null, expectedStart, expectedCount); + assertEquals(1, mCallCount); + } + + public void testNotifyMoved() { + final int expectedFrom = 10; + final int expectedTo = 100; + final int expectedCount = 3; + + OnListChangedListener listChangedListener = new OnListChangedListener() { + @Override + public void onChanged() { + fail("onChanged should not be called"); + } + + @Override + public void onItemRangeChanged(int start, int count) { + fail("onItemRangeChanged should not be called"); + } + + @Override + public void onItemRangeInserted(int start, int count) { + fail("onItemRangeInserted should not be called"); + } + + @Override + public void onItemRangeMoved(int from, int to, int count) { + assertEquals(expectedFrom, from); + assertEquals(expectedTo, to); + assertEquals(expectedCount, count); + mCallCount++; + } + + @Override + public void onItemRangeRemoved(int start, int count) { + fail("onItemRangeRemoved should not be called"); + } + }; + + mListChangeRegistry.add(listChangedListener); + assertEquals(0, mCallCount); + mListChangeRegistry.notifyMoved(null, expectedFrom, expectedTo, expectedCount); + assertEquals(1, mCallCount); + } + + public void testNotifyRemoved() { + final int expectedStart = 10; + final int expectedCount = 3; + + OnListChangedListener listChangedListener = new OnListChangedListener() { + @Override + public void onChanged() { + fail("onChanged should not be called"); + } + + @Override + public void onItemRangeChanged(int start, int count) { + fail("onItemRangeChanged should not be called"); + } + + @Override + public void onItemRangeInserted(int start, int count) { + fail("onItemRangeInserted should not be called"); + } + + @Override + public void onItemRangeMoved(int from, int to, int count) { + fail("onItemRangeMoved should not be called"); + } + + @Override + public void onItemRangeRemoved(int start, int count) { + assertEquals(expectedStart, start); + assertEquals(expectedCount, count); + mCallCount++; + } + }; + + mListChangeRegistry.add(listChangedListener); + assertEquals(0, mCallCount); + mListChangeRegistry.notifyRemoved(null, expectedStart, expectedCount); + assertEquals(1, mCallCount); + } +} diff --git a/tools/data-binding/integration-tests/TestApp/app/src/androidTest/java/android/databinding/testapp/MapChangeRegistryTest.java b/tools/data-binding/integration-tests/TestApp/app/src/androidTest/java/android/databinding/testapp/MapChangeRegistryTest.java new file mode 100644 index 0000000..fa5b536 --- /dev/null +++ b/tools/data-binding/integration-tests/TestApp/app/src/androidTest/java/android/databinding/testapp/MapChangeRegistryTest.java @@ -0,0 +1,53 @@ +/* + * 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 android.databinding.testapp; + +import android.databinding.MapChangeRegistry; +import android.databinding.ObservableArrayMap; +import android.databinding.testapp.databinding.BasicBindingBinding; + +import android.databinding.ObservableMap; +import android.databinding.OnMapChangedListener; + +public class MapChangeRegistryTest extends BaseDataBinderTest<BasicBindingBinding> { + + private int notificationCount = 0; + + public MapChangeRegistryTest() { + super(BasicBindingBinding.class); + } + + public void testNotifyAllChanged() { + MapChangeRegistry mapChangeRegistry = new MapChangeRegistry(); + + final ObservableMap<String, Integer> observableObj = new ObservableArrayMap<>(); + + final String expectedKey = "key"; + OnMapChangedListener listener = new OnMapChangedListener<ObservableMap<String, Integer>, String>() { + @Override + public void onMapChanged(ObservableMap sender, String key) { + notificationCount++; + assertEquals(observableObj, sender); + assertEquals(key, expectedKey); + } + }; + mapChangeRegistry.add(listener); + + assertEquals(0, notificationCount); + mapChangeRegistry.notifyChange(observableObj, expectedKey); + assertEquals(1, notificationCount); + } +} diff --git a/tools/data-binding/integration-tests/TestApp/app/src/androidTest/java/android/databinding/testapp/NewApiTest.java b/tools/data-binding/integration-tests/TestApp/app/src/androidTest/java/android/databinding/testapp/NewApiTest.java new file mode 100644 index 0000000..2c6fdf6 --- /dev/null +++ b/tools/data-binding/integration-tests/TestApp/app/src/androidTest/java/android/databinding/testapp/NewApiTest.java @@ -0,0 +1,79 @@ +/* + * 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 android.databinding.testapp; + +import android.databinding.DataBinderTrojan; +import android.databinding.testapp.databinding.NewApiLayoutBinding; + +import android.os.Build; +import android.test.UiThreadTest; +import android.view.View; +import android.widget.TextView; + +import java.util.ArrayList; + +public class NewApiTest extends BaseDataBinderTest<NewApiLayoutBinding> { + public NewApiTest() { + super(NewApiLayoutBinding.class); + } + + @UiThreadTest + public void testSetElevation() { + mBinder.setElevation(3); + mBinder.setName("foo"); + mBinder.setChildren(new ArrayList<View>()); + mBinder.executePendingBindings(); + assertEquals("foo", mBinder.textView.getText().toString()); + assertEquals(3f, mBinder.textView.getElevation()); + } + + @UiThreadTest + public void testSetElevationOlderAPI() { + DataBinderTrojan.setBuildSdkInt(1); + try { + TextView textView = mBinder.textView; + float originalElevation = textView.getElevation(); + mBinder.setElevation(3); + mBinder.setName("foo2"); + mBinder.executePendingBindings(); + assertEquals("foo2", textView.getText().toString()); + assertEquals(originalElevation, textView.getElevation()); + } finally { + DataBinderTrojan.setBuildSdkInt(Build.VERSION.SDK_INT); + } + } + + @UiThreadTest + public void testGeneric() { + ArrayList<View> views = new ArrayList<>(); + mBinder.setChildren(views); + mBinder.executePendingBindings(); + assertEquals(1, views.size()); + assertSame(mBinder.textView, views.get(0)); + } + + @UiThreadTest + public void testGenericOlderApi() { + DataBinderTrojan.setBuildSdkInt(1); + try { + ArrayList<View> views = new ArrayList<>(); + mBinder.setChildren(views); + mBinder.executePendingBindings(); + // we should not call the api on older platforms. + assertEquals(0, views.size()); + } finally { + DataBinderTrojan.setBuildSdkInt(Build.VERSION.SDK_INT); + } + } +} diff --git a/tools/data-binding/integration-tests/TestApp/app/src/androidTest/java/android/databinding/testapp/NoIdTest.java b/tools/data-binding/integration-tests/TestApp/app/src/androidTest/java/android/databinding/testapp/NoIdTest.java new file mode 100644 index 0000000..bf58709 --- /dev/null +++ b/tools/data-binding/integration-tests/TestApp/app/src/androidTest/java/android/databinding/testapp/NoIdTest.java @@ -0,0 +1,91 @@ +/* + * 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 android.databinding.testapp; + +import android.databinding.testapp.databinding.NoIdTestBinding; + +import android.test.UiThreadTest; +import android.widget.LinearLayout; +import android.widget.TextView; + +public class NoIdTest extends BaseDataBinderTest<NoIdTestBinding> { + public NoIdTest() { + super(NoIdTestBinding.class); + } + + @Override + protected void setUp() throws Exception { + super.setUp(); + try { + runTestOnUiThread(new Runnable() { + @Override + public void run() { + mBinder.setName("hello"); + mBinder.setOrientation(LinearLayout.VERTICAL); + mBinder.executePendingBindings(); + } + }); + } catch (Throwable throwable) { + throw new Exception(throwable); + } + } + + @UiThreadTest + public void testOnRoot() { + LinearLayout linearLayout = (LinearLayout) mBinder.getRoot(); + assertEquals(LinearLayout.VERTICAL, linearLayout.getOrientation()); + mBinder.setOrientation(LinearLayout.HORIZONTAL); + mBinder.executePendingBindings(); + assertEquals(LinearLayout.HORIZONTAL, linearLayout.getOrientation()); + } + + @UiThreadTest + public void testNormal() { + LinearLayout linearLayout = (LinearLayout) mBinder.getRoot(); + TextView view = (TextView) linearLayout.getChildAt(0); + assertEquals("hello world", view.getTag()); + assertEquals("hello", view.getText().toString()); + mBinder.setName("world"); + mBinder.executePendingBindings(); + assertEquals("world", view.getText().toString()); + } + + @UiThreadTest + public void testNoTag() { + LinearLayout linearLayout = (LinearLayout) mBinder.getRoot(); + TextView view = (TextView) linearLayout.getChildAt(1); + assertNull(view.getTag()); + } + + @UiThreadTest + public void testResourceTag() { + LinearLayout linearLayout = (LinearLayout) mBinder.getRoot(); + TextView view = (TextView) linearLayout.getChildAt(2); + String expectedValue = view.getResources().getString(R.string.app_name); + assertEquals(expectedValue, view.getTag()); + } + + @UiThreadTest + public void testAndroidResourceTag() { + LinearLayout linearLayout = (LinearLayout) mBinder.getRoot(); + TextView view = (TextView) linearLayout.getChildAt(3); + String expectedValue = view.getResources().getString(android.R.string.ok); + assertEquals(expectedValue, view.getTag()); + } + + @UiThreadTest + public void testIdOnly() { + assertEquals("hello", mBinder.textView.getText().toString()); + } +} diff --git a/tools/data-binding/integration-tests/TestApp/app/src/androidTest/java/android/databinding/testapp/ObservableArrayListTest.java b/tools/data-binding/integration-tests/TestApp/app/src/androidTest/java/android/databinding/testapp/ObservableArrayListTest.java new file mode 100644 index 0000000..739ea7a --- /dev/null +++ b/tools/data-binding/integration-tests/TestApp/app/src/androidTest/java/android/databinding/testapp/ObservableArrayListTest.java @@ -0,0 +1,250 @@ +/* + * 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 android.databinding.testapp; + +import android.databinding.ObservableArrayList; +import android.databinding.testapp.databinding.BasicBindingBinding; + +import android.databinding.ObservableList; +import android.databinding.OnListChangedListener; + +import java.util.ArrayList; + +public class ObservableArrayListTest extends BaseDataBinderTest<BasicBindingBinding> { + + private static final int ALL = 0; + + private static final int CHANGE = 1; + + private static final int INSERT = 2; + + private static final int MOVE = 3; + + private static final int REMOVE = 4; + + private ObservableList<String> mObservable; + + private ArrayList<ListChange> mNotifications = new ArrayList<>(); + + private OnListChangedListener mListener = new OnListChangedListener() { + @Override + public void onChanged() { + mNotifications.add(new ListChange(ALL, 0, 0)); + } + + @Override + public void onItemRangeChanged(int start, int count) { + mNotifications.add(new ListChange(CHANGE, start, count)); + } + + @Override + public void onItemRangeInserted(int start, int count) { + mNotifications.add(new ListChange(INSERT, start, count)); + } + + @Override + public void onItemRangeMoved(int from, int to, int count) { + mNotifications.add(new ListChange(MOVE, from, to, count)); + } + + @Override + public void onItemRangeRemoved(int start, int count) { + mNotifications.add(new ListChange(REMOVE, start, count)); + } + }; + + private static class ListChange { + + public ListChange(int change, int start, int count) { + this.start = start; + this.count = count; + this.from = 0; + this.to = 0; + this.change = change; + } + + public ListChange(int change, int from, int to, int count) { + this.from = from; + this.to = to; + this.count = count; + this.start = 0; + this.change = change; + } + + public final int start; + + public final int count; + + public final int from; + + public final int to; + + public final int change; + } + + public ObservableArrayListTest() { + super(BasicBindingBinding.class); + } + + @Override + protected void setUp() throws Exception { + mNotifications.clear(); + mObservable = new ObservableArrayList<>(); + } + + public void testAddListener() { + mObservable.add("Hello"); + assertTrue(mNotifications.isEmpty()); + mObservable.addOnListChangedListener(mListener); + mObservable.add("World"); + assertFalse(mNotifications.isEmpty()); + } + + public void testRemoveListener() { + // test there is no exception when the listener isn't there + mObservable.removeOnListChangedListener(mListener); + + mObservable.addOnListChangedListener(mListener); + mObservable.add("Hello"); + mNotifications.clear(); + mObservable.removeOnListChangedListener(mListener); + mObservable.add("World"); + assertTrue(mNotifications.isEmpty()); + + // test there is no exception when the listener isn't there + mObservable.removeOnListChangedListener(mListener); + } + + public void testAdd() { + mObservable.addOnListChangedListener(mListener); + mObservable.add("Hello"); + assertEquals(1, mNotifications.size()); + ListChange change = mNotifications.get(0); + assertEquals(INSERT, change.change); + assertEquals(0, change.start); + assertEquals(1, change.count); + assertEquals("Hello", mObservable.get(0)); + } + + public void testInsert() { + mObservable.addOnListChangedListener(mListener); + mObservable.add("Hello"); + mObservable.add(0, "World"); + mObservable.add(1, "Dang"); + mObservable.add(3, "End"); + assertEquals(4, mObservable.size()); + assertEquals("World", mObservable.get(0)); + assertEquals("Dang", mObservable.get(1)); + assertEquals("Hello", mObservable.get(2)); + assertEquals("End", mObservable.get(3)); + assertEquals(4, mNotifications.size()); + ListChange change = mNotifications.get(1); + assertEquals(INSERT, change.change); + assertEquals(0, change.start); + assertEquals(1, change.count); + } + + public void testAddAll() { + ArrayList<String> toAdd = new ArrayList<>(); + toAdd.add("Hello"); + toAdd.add("World"); + mObservable.add("First"); + mObservable.addOnListChangedListener(mListener); + mObservable.addAll(toAdd); + assertEquals(3, mObservable.size()); + assertEquals("Hello", mObservable.get(1)); + assertEquals("World", mObservable.get(2)); + assertEquals(1, mNotifications.size()); + ListChange change = mNotifications.get(0); + assertEquals(INSERT, change.change); + assertEquals(1, change.start); + assertEquals(2, change.count); + } + + public void testInsertAll() { + ArrayList<String> toAdd = new ArrayList<>(); + toAdd.add("Hello"); + toAdd.add("World"); + mObservable.add("First"); + mObservable.addOnListChangedListener(mListener); + mObservable.addAll(0, toAdd); + assertEquals(3, mObservable.size()); + assertEquals("Hello", mObservable.get(0)); + assertEquals("World", mObservable.get(1)); + assertEquals(1, mNotifications.size()); + ListChange change = mNotifications.get(0); + assertEquals(INSERT, change.change); + assertEquals(0, change.start); + assertEquals(2, change.count); + } + + public void testClear() { + mObservable.add("Hello"); + mObservable.add("World"); + mObservable.addOnListChangedListener(mListener); + mObservable.clear(); + assertEquals(1, mNotifications.size()); + ListChange change = mNotifications.get(0); + assertEquals(REMOVE, change.change); + assertEquals(0, change.start); + assertEquals(2, change.count); + + mObservable.clear(); + // No notification when nothing is cleared. + assertEquals(1, mNotifications.size()); + } + + public void testRemoveIndex() { + mObservable.add("Hello"); + mObservable.add("World"); + mObservable.addOnListChangedListener(mListener); + assertEquals("Hello", mObservable.remove(0)); + assertEquals(1, mNotifications.size()); + ListChange change = mNotifications.get(0); + assertEquals(REMOVE, change.change); + assertEquals(0, change.start); + assertEquals(1, change.count); + } + + public void testRemoveObject() { + mObservable.add("Hello"); + mObservable.add("World"); + mObservable.addOnListChangedListener(mListener); + assertTrue(mObservable.remove("Hello")); + assertEquals(1, mNotifications.size()); + ListChange change = mNotifications.get(0); + assertEquals(REMOVE, change.change); + assertEquals(0, change.start); + assertEquals(1, change.count); + + assertFalse(mObservable.remove("Hello")); + // nothing removed, don't notify + assertEquals(1, mNotifications.size()); + } + + public void testSet() { + mObservable.add("Hello"); + mObservable.add("World"); + mObservable.addOnListChangedListener(mListener); + assertEquals("Hello", mObservable.set(0, "Goodbye")); + assertEquals("Goodbye", mObservable.get(0)); + assertEquals(2, mObservable.size()); + ListChange change = mNotifications.get(0); + assertEquals(CHANGE, change.change); + assertEquals(0, change.start); + assertEquals(1, change.count); + } +} diff --git a/tools/data-binding/integration-tests/TestApp/app/src/androidTest/java/android/databinding/testapp/ObservableArrayMapTest.java b/tools/data-binding/integration-tests/TestApp/app/src/androidTest/java/android/databinding/testapp/ObservableArrayMapTest.java new file mode 100644 index 0000000..fc5f689 --- /dev/null +++ b/tools/data-binding/integration-tests/TestApp/app/src/androidTest/java/android/databinding/testapp/ObservableArrayMapTest.java @@ -0,0 +1,213 @@ +/* + * 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 android.databinding.testapp; + +import android.databinding.ObservableArrayMap; +import android.databinding.testapp.databinding.BasicBindingBinding; + +import android.databinding.ObservableMap; +import android.databinding.OnMapChangedListener; +import android.support.v4.util.ArrayMap; +import android.support.v4.util.SimpleArrayMap; + +import java.util.ArrayList; +import java.util.Map; + +public class ObservableArrayMapTest extends BaseDataBinderTest<BasicBindingBinding> { + + private ObservableArrayMap<String, String> mObservable; + + private ArrayList<String> mNotifications = new ArrayList<>(); + + private OnMapChangedListener mListener = new OnMapChangedListener() { + @Override + public void onMapChanged(ObservableMap observableMap, Object o) { + assertEquals(mObservable, observableMap); + mNotifications.add((String) o); + } + }; + + public ObservableArrayMapTest() { + super(BasicBindingBinding.class); + } + + @Override + protected void setUp() throws Exception { + mNotifications.clear(); + mObservable = new ObservableArrayMap<>(); + } + + public void testAddListener() { + mObservable.put("Hello", "World"); + assertTrue(mNotifications.isEmpty()); + mObservable.addOnMapChangedListener(mListener); + mObservable.put("Hello", "Goodbye"); + assertFalse(mNotifications.isEmpty()); + } + + public void testRemoveListener() { + // test there is no exception when the listener isn't there + mObservable.removeOnMapChangedListener(mListener); + + mObservable.addOnMapChangedListener(mListener); + mObservable.put("Hello", "World"); + mNotifications.clear(); + mObservable.removeOnMapChangedListener(mListener); + mObservable.put("World", "Hello"); + assertTrue(mNotifications.isEmpty()); + + // test there is no exception when the listener isn't there + mObservable.removeOnMapChangedListener(mListener); + } + + public void testClear() { + mObservable.put("Hello", "World"); + mObservable.put("World", "Hello"); + mObservable.addOnMapChangedListener(mListener); + mObservable.clear(); + assertEquals(1, mNotifications.size()); + assertNull(mNotifications.get(0)); + assertEquals(0, mObservable.size()); + assertTrue(mObservable.isEmpty()); + + mObservable.clear(); + // No notification when nothing is cleared. + assertEquals(1, mNotifications.size()); + } + + public void testPut() { + mObservable.addOnMapChangedListener(mListener); + mObservable.put("Hello", "World"); + assertEquals(1, mNotifications.size()); + assertEquals("Hello", mNotifications.get(0)); + assertEquals("World", mObservable.get("Hello")); + + mObservable.put("Hello", "World2"); + assertEquals(2, mNotifications.size()); + assertEquals("Hello", mNotifications.get(1)); + assertEquals("World2", mObservable.get("Hello")); + + mObservable.put("World", "Hello"); + assertEquals(3, mNotifications.size()); + assertEquals("World", mNotifications.get(2)); + assertEquals("Hello", mObservable.get("World")); + } + + public void testPutAll() { + Map<String, String> toAdd = new ArrayMap<>(); + toAdd.put("Hello", "World"); + toAdd.put("Goodbye", "Cruel World"); + mObservable.put("Cruel", "World"); + mObservable.addOnMapChangedListener(mListener); + mObservable.putAll(toAdd); + assertEquals(3, mObservable.size()); + assertEquals("World", mObservable.get("Hello")); + assertEquals("Cruel World", mObservable.get("Goodbye")); + assertEquals(2, mNotifications.size()); + // order is not guaranteed + assertTrue(mNotifications.contains("Hello")); + assertTrue(mNotifications.contains("Goodbye")); + } + + public void testPutAllSimpleArrayMap() { + SimpleArrayMap<String, String> toAdd = new ArrayMap<>(); + toAdd.put("Hello", "World"); + toAdd.put("Goodbye", "Cruel World"); + mObservable.put("Cruel", "World"); + mObservable.addOnMapChangedListener(mListener); + mObservable.putAll(toAdd); + assertEquals(3, mObservable.size()); + assertEquals("World", mObservable.get("Hello")); + assertEquals("Cruel World", mObservable.get("Goodbye")); + assertEquals(2, mNotifications.size()); + // order is not guaranteed + assertTrue(mNotifications.contains("Hello")); + assertTrue(mNotifications.contains("Goodbye")); + } + + public void testRemove() { + mObservable.put("Hello", "World"); + mObservable.put("Goodbye", "Cruel World"); + mObservable.addOnMapChangedListener(mListener); + assertEquals("World", mObservable.remove("Hello")); + assertEquals(1, mNotifications.size()); + assertEquals("Hello", mNotifications.get(0)); + + assertNull(mObservable.remove("Hello")); + // nothing removed, don't notify + assertEquals(1, mNotifications.size()); + } + + public void testRemoveAll() { + ArrayList<String> toRemove = new ArrayList<>(); + toRemove.add("Hello"); + toRemove.add("Goodbye"); + mObservable.put("Hello", "World"); + mObservable.put("Goodbye", "Cruel World"); + mObservable.put("Cruel", "World"); + mObservable.addOnMapChangedListener(mListener); + assertTrue(mObservable.removeAll(toRemove)); + assertEquals(2, mNotifications.size()); + // order is not guaranteed + assertTrue(mNotifications.contains("Hello")); + assertTrue(mNotifications.contains("Goodbye")); + + assertTrue(mObservable.containsKey("Cruel")); + + // Test nothing removed + assertFalse(mObservable.removeAll(toRemove)); + assertEquals(2, mNotifications.size()); + } + + public void testRetainAll() { + ArrayList<String> toRetain = new ArrayList<>(); + toRetain.add("Hello"); + toRetain.add("Goodbye"); + mObservable.put("Hello", "World"); + mObservable.put("Goodbye", "Cruel World"); + mObservable.put("Cruel", "World"); + mObservable.addOnMapChangedListener(mListener); + assertTrue(mObservable.retainAll(toRetain)); + assertEquals(1, mNotifications.size()); + assertEquals("Cruel", mNotifications.get(0)); + assertTrue(mObservable.containsKey("Hello")); + assertTrue(mObservable.containsKey("Goodbye")); + + // Test nothing removed + assertFalse(mObservable.retainAll(toRetain)); + assertEquals(1, mNotifications.size()); + } + + public void testRemoveAt() { + mObservable.put("Hello", "World"); + mObservable.put("Goodbye", "Cruel World"); + mObservable.addOnMapChangedListener(mListener); + String key = mObservable.keyAt(0); + String value = mObservable.valueAt(0); + assertTrue("Hello".equals(key) || "Goodbye".equals(key)); + assertEquals(value, mObservable.removeAt(0)); + assertEquals(1, mNotifications.size()); + assertEquals(key, mNotifications.get(0)); + } + + public void testSetValueAt() { + mObservable.put("Hello", "World"); + mObservable.addOnMapChangedListener(mListener); + assertEquals("World", mObservable.setValueAt(0, "Cruel World")); + assertEquals(1, mNotifications.size()); + assertEquals("Hello", mNotifications.get(0)); + } +} diff --git a/tools/data-binding/integration-tests/TestApp/app/src/androidTest/java/android/databinding/testapp/ObservableFieldTest.java b/tools/data-binding/integration-tests/TestApp/app/src/androidTest/java/android/databinding/testapp/ObservableFieldTest.java new file mode 100644 index 0000000..7b29122 --- /dev/null +++ b/tools/data-binding/integration-tests/TestApp/app/src/androidTest/java/android/databinding/testapp/ObservableFieldTest.java @@ -0,0 +1,144 @@ +/* + * 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 android.databinding.testapp; + +import android.databinding.testapp.databinding.ObservableFieldTestBinding; +import android.databinding.testapp.vo.ObservableFieldBindingObject; + +import android.test.UiThreadTest; +import android.widget.TextView; + +public class ObservableFieldTest extends BaseDataBinderTest<ObservableFieldTestBinding> { + private ObservableFieldBindingObject mObj; + + public ObservableFieldTest() { + super(ObservableFieldTestBinding.class); + } + + @Override + protected void setUp() throws Exception { + super.setUp(); + try { + runTestOnUiThread(new Runnable() { + @Override + public void run() { + mObj = new ObservableFieldBindingObject(); + mBinder.setObj(mObj); + mBinder.executePendingBindings(); + } + }); + } catch (Throwable throwable) { + throw new Exception(throwable); + } + } + + @UiThreadTest + public void testBoolean() { + TextView view = mBinder.bField; + assertEquals("false", view.getText()); + + mObj.bField.set(true); + mBinder.executePendingBindings(); + + assertEquals("true", view.getText()); + } + + @UiThreadTest + public void testByte() { + TextView view = mBinder.tField; + assertEquals("0", view.getText()); + + mObj.tField.set((byte) 1); + mBinder.executePendingBindings(); + + assertEquals("1", view.getText()); + } + + @UiThreadTest + public void testShort() { + TextView view = mBinder.sField; + assertEquals("0", view.getText()); + + mObj.sField.set((short) 1); + mBinder.executePendingBindings(); + + assertEquals("1", view.getText()); + } + + @UiThreadTest + public void testChar() { + TextView view = mBinder.cField; + assertEquals("\u0000", view.getText()); + + mObj.cField.set('A'); + mBinder.executePendingBindings(); + + assertEquals("A", view.getText()); + } + + @UiThreadTest + public void testInt() { + TextView view = mBinder.iField; + assertEquals("0", view.getText()); + + mObj.iField.set(1); + mBinder.executePendingBindings(); + + assertEquals("1", view.getText()); + } + + @UiThreadTest + public void testLong() { + TextView view = mBinder.lField; + assertEquals("0", view.getText()); + + mObj.lField.set(1); + mBinder.executePendingBindings(); + + assertEquals("1", view.getText()); + } + + @UiThreadTest + public void testFloat() { + TextView view = mBinder.fField; + assertEquals("0.0", view.getText()); + + mObj.fField.set(1); + mBinder.executePendingBindings(); + + assertEquals("1.0", view.getText()); + } + + @UiThreadTest + public void testDouble() { + TextView view = mBinder.dField; + assertEquals("0.0", view.getText()); + + mObj.dField.set(1); + mBinder.executePendingBindings(); + + assertEquals("1.0", view.getText()); + } + + @UiThreadTest + public void testObject() { + TextView view = mBinder.oField; + assertEquals("Hello", view.getText()); + + mObj.oField.set("World"); + mBinder.executePendingBindings(); + + assertEquals("World", view.getText()); + } +} diff --git a/tools/data-binding/integration-tests/TestApp/app/src/androidTest/java/android/databinding/testapp/ObservableWithNotBindableFieldObjectTest.java b/tools/data-binding/integration-tests/TestApp/app/src/androidTest/java/android/databinding/testapp/ObservableWithNotBindableFieldObjectTest.java new file mode 100644 index 0000000..b14f413 --- /dev/null +++ b/tools/data-binding/integration-tests/TestApp/app/src/androidTest/java/android/databinding/testapp/ObservableWithNotBindableFieldObjectTest.java @@ -0,0 +1,40 @@ +/* + * 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 android.databinding.testapp; + +import android.databinding.testapp.BaseDataBinderTest; +import android.databinding.testapp.R; +import android.databinding.testapp.databinding.ObservableWithNotBindableFieldBinding; +import android.databinding.testapp.vo.ObservableWithNotBindableFieldObject; + +import android.test.UiThreadTest; + +public class ObservableWithNotBindableFieldObjectTest extends BaseDataBinderTest<ObservableWithNotBindableFieldBinding> { + + + public ObservableWithNotBindableFieldObjectTest() { + super(ObservableWithNotBindableFieldBinding.class); + } + + @UiThreadTest + public void testSimple() { + ObservableWithNotBindableFieldObject obj = new ObservableWithNotBindableFieldObject(); + mBinder.setObj(obj); + mBinder.executePendingBindings(); + assertEquals("", mBinder.textView.getText().toString()); + obj.update("100"); + mBinder.executePendingBindings(); + assertEquals("100", mBinder.textView.getText().toString()); + } +} diff --git a/tools/data-binding/integration-tests/TestApp/app/src/androidTest/java/android/databinding/testapp/ProcessBindableTest.java b/tools/data-binding/integration-tests/TestApp/app/src/androidTest/java/android/databinding/testapp/ProcessBindableTest.java new file mode 100644 index 0000000..4325213 --- /dev/null +++ b/tools/data-binding/integration-tests/TestApp/app/src/androidTest/java/android/databinding/testapp/ProcessBindableTest.java @@ -0,0 +1,64 @@ +/* + * Copyright (C) 2015 The Android Open Source Project + * + * Licensed under the Apache License, Version 2.0 (the "License"); + * you may not use this file except in compliance with the License. + * You may obtain a copy of the License at + * + * http://www.apache.org/licenses/LICENSE-2.0 + * + * Unless required by applicable law or agreed to in writing, software + * distributed under the License is distributed on an "AS IS" BASIS, + * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. + * See the License for the specific language governing permissions and + * limitations under the License. + */ +package android.databinding.testapp; + +import android.databinding.testapp.databinding.BasicBindingBinding; + +import android.util.ArrayMap; + +import java.lang.reflect.Field; +import java.lang.reflect.Modifier; +import java.util.HashSet; +import android.databinding.testapp.BR; +public class ProcessBindableTest extends BaseDataBinderTest<BasicBindingBinding> { + private static String[] EXPECTED_BINDING_NAMES = { + "bindableField1", + "bindableField2", + "bindableField3", + "bindableField4", + "mbindableField5", + "bindableField6", + "bindableField7", + "bindableField8", + }; + + public ProcessBindableTest() { + super(BasicBindingBinding.class); + } + + public void testFieldsGenerated() throws IllegalAccessException { + Field[] fields = BR.class.getFields(); + + ArrayMap<String, Integer> fieldValues = new ArrayMap<>(); + int modifiers = Modifier.PUBLIC | Modifier.STATIC | Modifier.FINAL; + for (Field field: fields) { + assertTrue(field.getModifiers() == modifiers); + String name = field.getName(); + fieldValues.put(name, field.getInt(null)); + } + + assertTrue(fieldValues.containsKey("_all")); + assertEquals(0, (int) fieldValues.get("_all")); + HashSet<Integer> values = new HashSet<>(); + values.add(0); + + for (String fieldName : EXPECTED_BINDING_NAMES) { + assertTrue("missing field: " + fieldName, fieldValues.containsKey(fieldName)); + assertFalse(values.contains(fieldValues.get(fieldName))); + values.add(fieldValues.get(fieldName)); + } + } +} diff --git a/tools/data-binding/integration-tests/TestApp/app/src/androidTest/java/android/databinding/testapp/ProgressBarBindingAdapterTest.java b/tools/data-binding/integration-tests/TestApp/app/src/androidTest/java/android/databinding/testapp/ProgressBarBindingAdapterTest.java new file mode 100644 index 0000000..52d558f --- /dev/null +++ b/tools/data-binding/integration-tests/TestApp/app/src/androidTest/java/android/databinding/testapp/ProgressBarBindingAdapterTest.java @@ -0,0 +1,59 @@ +/* + * 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 android.databinding.testapp; + +import android.databinding.testapp.databinding.ProgressBarAdapterTestBinding; +import android.databinding.testapp.vo.ProgressBarBindingObject; + +import android.os.Build; +import android.widget.ProgressBar; + +public class ProgressBarBindingAdapterTest + extends BindingAdapterTestBase<ProgressBarAdapterTestBinding, ProgressBarBindingObject> { + + ProgressBar mView; + + public ProgressBarBindingAdapterTest() { + super(ProgressBarAdapterTestBinding.class, ProgressBarBindingObject.class, + R.layout.progress_bar_adapter_test); + } + + @Override + protected void setUp() throws Exception { + super.setUp(); + mView = mBinder.view; + } + + public void testTint() throws Throwable { + if (Build.VERSION.SDK_INT >= Build.VERSION_CODES.LOLLIPOP) { + assertEquals(mBindingObject.getIndeterminateTint(), + mView.getIndeterminateTintList().getDefaultColor()); + assertEquals(mBindingObject.getProgressTint(), + mView.getProgressTintList().getDefaultColor()); + assertEquals(mBindingObject.getSecondaryProgressTint(), + mView.getSecondaryProgressTintList().getDefaultColor()); + + changeValues(); + + assertEquals(mBindingObject.getIndeterminateTint(), + mView.getIndeterminateTintList().getDefaultColor()); + assertEquals(mBindingObject.getProgressTint(), + mView.getProgressTintList().getDefaultColor()); + assertEquals(mBindingObject.getSecondaryProgressTint(), + mView.getSecondaryProgressTintList().getDefaultColor()); + } + } +} diff --git a/tools/data-binding/integration-tests/TestApp/app/src/androidTest/java/android/databinding/testapp/PropertyChangeRegistryTest.java b/tools/data-binding/integration-tests/TestApp/app/src/androidTest/java/android/databinding/testapp/PropertyChangeRegistryTest.java new file mode 100644 index 0000000..44b7dcf --- /dev/null +++ b/tools/data-binding/integration-tests/TestApp/app/src/androidTest/java/android/databinding/testapp/PropertyChangeRegistryTest.java @@ -0,0 +1,62 @@ +/* + * 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 android.databinding.testapp; + +import android.databinding.PropertyChangeRegistry; +import android.databinding.testapp.databinding.BasicBindingBinding; + +import android.databinding.Observable; +import android.databinding.OnPropertyChangedListener; + +public class PropertyChangeRegistryTest extends BaseDataBinderTest<BasicBindingBinding> { + + private int notificationCount = 0; + + public PropertyChangeRegistryTest() { + super(BasicBindingBinding.class); + } + + public void testNotifyChanged() { + PropertyChangeRegistry propertyChangeRegistry = new PropertyChangeRegistry(); + + final Observable observableObj = new Observable() { + @Override + public void addOnPropertyChangedListener( + OnPropertyChangedListener onPropertyChangedListener) { + } + + @Override + public void removeOnPropertyChangedListener( + OnPropertyChangedListener onPropertyChangedListener) { + } + }; + + final int expectedId = 100; + OnPropertyChangedListener listener = new OnPropertyChangedListener() { + @Override + public void onPropertyChanged(Observable observable, int id) { + notificationCount++; + assertEquals(expectedId, id); + assertEquals(observableObj, observable); + } + }; + propertyChangeRegistry.add(listener); + + assertEquals(0, notificationCount); + propertyChangeRegistry.notifyChange(observableObj, expectedId); + assertEquals(1, notificationCount); + } +} diff --git a/tools/data-binding/integration-tests/TestApp/app/src/androidTest/java/android/databinding/testapp/RadioGroupBindingAdapterTest.java b/tools/data-binding/integration-tests/TestApp/app/src/androidTest/java/android/databinding/testapp/RadioGroupBindingAdapterTest.java new file mode 100644 index 0000000..7ab4a0d --- /dev/null +++ b/tools/data-binding/integration-tests/TestApp/app/src/androidTest/java/android/databinding/testapp/RadioGroupBindingAdapterTest.java @@ -0,0 +1,46 @@ +/* + * 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 android.databinding.testapp; + +import android.databinding.testapp.databinding.RadioGroupAdapterTestBinding; +import android.databinding.testapp.vo.RadioGroupBindingObject; + +import android.widget.RadioGroup; + +public class RadioGroupBindingAdapterTest + extends BindingAdapterTestBase<RadioGroupAdapterTestBinding, RadioGroupBindingObject> { + + RadioGroup mView; + + public RadioGroupBindingAdapterTest() { + super(RadioGroupAdapterTestBinding.class, RadioGroupBindingObject.class, + R.layout.radio_group_adapter_test); + } + + @Override + protected void setUp() throws Exception { + super.setUp(); + mView = mBinder.view; + } + + public void testRadioGroup() throws Throwable { + assertEquals(mBindingObject.getCheckedButton(), mView.getCheckedRadioButtonId()); + + changeValues(); + + assertEquals(mBindingObject.getCheckedButton(), mView.getCheckedRadioButtonId()); + } +} diff --git a/tools/data-binding/integration-tests/TestApp/app/src/androidTest/java/android/databinding/testapp/ReadComplexTernaryTest.java b/tools/data-binding/integration-tests/TestApp/app/src/androidTest/java/android/databinding/testapp/ReadComplexTernaryTest.java new file mode 100644 index 0000000..6970075 --- /dev/null +++ b/tools/data-binding/integration-tests/TestApp/app/src/androidTest/java/android/databinding/testapp/ReadComplexTernaryTest.java @@ -0,0 +1,22 @@ +package android.databinding.testapp; + +import android.test.UiThreadTest; +import android.databinding.testapp.databinding.ReadComplexTernaryBinding; + +import android.databinding.testapp.vo.User; + +public class ReadComplexTernaryTest extends BaseDataBinderTest<ReadComplexTernaryBinding> { + public ReadComplexTernaryTest() { + super(ReadComplexTernaryBinding.class); + } + + @UiThreadTest + public void testWhenNull() { + User user = new User(); + user.setName("a"); + user.setFullName("a b"); + mBinder.setUser(user); + mBinder.executePendingBindings(); + assertEquals("?", mBinder.textView.getText().toString()); + } +} diff --git a/tools/data-binding/integration-tests/TestApp/app/src/androidTest/java/android/databinding/testapp/ResourceTest.java b/tools/data-binding/integration-tests/TestApp/app/src/androidTest/java/android/databinding/testapp/ResourceTest.java new file mode 100644 index 0000000..f48bb2b --- /dev/null +++ b/tools/data-binding/integration-tests/TestApp/app/src/androidTest/java/android/databinding/testapp/ResourceTest.java @@ -0,0 +1,86 @@ +/* + * 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 android.databinding.testapp; + +import android.databinding.testapp.databinding.ResourceTestBinding; + +import android.test.UiThreadTest; +import android.widget.TextView; + +public class ResourceTest extends BaseDataBinderTest<ResourceTestBinding> { + + public ResourceTest() { + super(ResourceTestBinding.class); + } + + @Override + protected void setUp() throws Exception { + super.setUp(); + mBinder.setCount(0); + mBinder.setTitle("Mrs."); + mBinder.setLastName("Doubtfire"); + mBinder.setBase(2); + mBinder.setPbase(3); + try { + runTestOnUiThread(new Runnable() { + @Override + public void run() { + mBinder.executePendingBindings(); + } + }); + } catch (Throwable throwable) { + throw new Exception(throwable); + } + } + + @UiThreadTest + public void testStringFormat() throws Throwable { + TextView view = mBinder.textView0; + assertEquals("Mrs. Doubtfire", view.getText().toString()); + + mBinder.setTitle("Mr."); + mBinder.executePendingBindings(); + assertEquals("Mr. Doubtfire", view.getText().toString()); + } + + @UiThreadTest + public void testQuantityString() throws Throwable { + TextView view = mBinder.textView1; + assertEquals("oranges", view.getText().toString()); + + mBinder.setCount(1); + mBinder.executePendingBindings(); + assertEquals("orange", view.getText().toString()); + } + + @UiThreadTest + public void testFractionNoParameters() throws Throwable { + TextView view = mBinder.fractionNoParameters; + assertEquals("1.5", view.getText().toString()); + } + + @UiThreadTest + public void testFractionOneParameter() throws Throwable { + TextView view = mBinder.fractionOneParameter; + assertEquals("3.0", view.getText().toString()); + } + + @UiThreadTest + public void testFractionTwoParameters() throws Throwable { + TextView view = mBinder.fractionTwoParameters; + assertEquals("9.0", view.getText().toString()); + } +} diff --git a/tools/data-binding/integration-tests/TestApp/app/src/androidTest/java/android/databinding/testapp/SpinnerBindingAdapterTest.java b/tools/data-binding/integration-tests/TestApp/app/src/androidTest/java/android/databinding/testapp/SpinnerBindingAdapterTest.java new file mode 100644 index 0000000..384ff21 --- /dev/null +++ b/tools/data-binding/integration-tests/TestApp/app/src/androidTest/java/android/databinding/testapp/SpinnerBindingAdapterTest.java @@ -0,0 +1,52 @@ +/* + * 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 android.databinding.testapp; + +import android.databinding.testapp.databinding.SpinnerAdapterTestBinding; +import android.databinding.testapp.vo.SpinnerBindingObject; + +import android.graphics.drawable.ColorDrawable; +import android.os.Build; +import android.widget.Spinner; + +public class SpinnerBindingAdapterTest + extends BindingAdapterTestBase<SpinnerAdapterTestBinding, SpinnerBindingObject> { + + Spinner mView; + + public SpinnerBindingAdapterTest() { + super(SpinnerAdapterTestBinding.class, SpinnerBindingObject.class, + R.layout.spinner_adapter_test); + } + + @Override + protected void setUp() throws Exception { + super.setUp(); + mView = mBinder.view; + } + + public void testSpinner() throws Throwable { + if (Build.VERSION.SDK_INT >= Build.VERSION_CODES.JELLY_BEAN) { + assertEquals(mBindingObject.getPopupBackground(), + ((ColorDrawable) mView.getPopupBackground()).getColor()); + + changeValues(); + + assertEquals(mBindingObject.getPopupBackground(), + ((ColorDrawable) mView.getPopupBackground()).getColor()); + } + } +} diff --git a/tools/data-binding/integration-tests/TestApp/app/src/androidTest/java/android/databinding/testapp/SwitchBindingAdapterTest.java b/tools/data-binding/integration-tests/TestApp/app/src/androidTest/java/android/databinding/testapp/SwitchBindingAdapterTest.java new file mode 100644 index 0000000..65f38ba --- /dev/null +++ b/tools/data-binding/integration-tests/TestApp/app/src/androidTest/java/android/databinding/testapp/SwitchBindingAdapterTest.java @@ -0,0 +1,56 @@ +/* + * 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 android.databinding.testapp; + +import android.databinding.testapp.databinding.SwitchAdapterTestBinding; +import android.databinding.testapp.vo.SwitchBindingObject; + +import android.graphics.drawable.ColorDrawable; +import android.os.Build; +import android.widget.Switch; + +public class SwitchBindingAdapterTest + extends BindingAdapterTestBase<SwitchAdapterTestBinding, SwitchBindingObject> { + + Switch mView; + + public SwitchBindingAdapterTest() { + super(SwitchAdapterTestBinding.class, SwitchBindingObject.class, + R.layout.switch_adapter_test); + } + + @Override + protected void setUp() throws Exception { + super.setUp(); + mView = mBinder.view; + } + + public void testSwitch() throws Throwable { + if (Build.VERSION.SDK_INT >= Build.VERSION_CODES.JELLY_BEAN) { + assertEquals(mBindingObject.getThumb(), + ((ColorDrawable) mView.getThumbDrawable()).getColor()); + assertEquals(mBindingObject.getTrack(), + ((ColorDrawable) mView.getTrackDrawable()).getColor()); + + changeValues(); + + assertEquals(mBindingObject.getThumb(), + ((ColorDrawable) mView.getThumbDrawable()).getColor()); + assertEquals(mBindingObject.getTrack(), + ((ColorDrawable) mView.getTrackDrawable()).getColor()); + } + } +} diff --git a/tools/data-binding/integration-tests/TestApp/app/src/androidTest/java/android/databinding/testapp/TabWidgetBindingAdapterTest.java b/tools/data-binding/integration-tests/TestApp/app/src/androidTest/java/android/databinding/testapp/TabWidgetBindingAdapterTest.java new file mode 100644 index 0000000..c72010b --- /dev/null +++ b/tools/data-binding/integration-tests/TestApp/app/src/androidTest/java/android/databinding/testapp/TabWidgetBindingAdapterTest.java @@ -0,0 +1,54 @@ +/* + * 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 android.databinding.testapp; + +import android.databinding.testapp.databinding.TabWidgetAdapterTestBinding; +import android.databinding.testapp.vo.TabWidgetBindingObject; + +import android.graphics.drawable.ColorDrawable; +import android.os.Build; +import android.widget.TabWidget; + +public class TabWidgetBindingAdapterTest + extends BindingAdapterTestBase<TabWidgetAdapterTestBinding, TabWidgetBindingObject> { + + TabWidget mView; + + public TabWidgetBindingAdapterTest() { + super(TabWidgetAdapterTestBinding.class, TabWidgetBindingObject.class, + R.layout.tab_widget_adapter_test); + } + + @Override + protected void setUp() throws Exception { + super.setUp(); + mView = mBinder.view; + } + + public void testStrip() throws Throwable { + if (Build.VERSION.SDK_INT >= Build.VERSION_CODES.JELLY_BEAN) { + assertEquals(mBindingObject.getDivider().getColor(), + ((ColorDrawable) mView.getDividerDrawable()).getColor()); + assertEquals(mBindingObject.isTabStripEnabled(), mView.isStripEnabled()); + + changeValues(); + + assertEquals(mBindingObject.getDivider().getColor(), + ((ColorDrawable) mView.getDividerDrawable()).getColor()); + assertEquals(mBindingObject.isTabStripEnabled(), mView.isStripEnabled()); + } + } +} diff --git a/tools/data-binding/integration-tests/TestApp/app/src/androidTest/java/android/databinding/testapp/TableLayoutBindingAdapterTest.java b/tools/data-binding/integration-tests/TestApp/app/src/androidTest/java/android/databinding/testapp/TableLayoutBindingAdapterTest.java new file mode 100644 index 0000000..4f00623 --- /dev/null +++ b/tools/data-binding/integration-tests/TestApp/app/src/androidTest/java/android/databinding/testapp/TableLayoutBindingAdapterTest.java @@ -0,0 +1,78 @@ +/* + * 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 android.databinding.testapp; + +import android.databinding.testapp.databinding.TableLayoutAdapterTestBinding; +import android.databinding.testapp.vo.TableLayoutBindingObject; + +import android.graphics.drawable.ColorDrawable; +import android.os.Build; +import android.widget.TableLayout; + +public class TableLayoutBindingAdapterTest + extends BindingAdapterTestBase<TableLayoutAdapterTestBinding, TableLayoutBindingObject> { + + TableLayout mView; + + public TableLayoutBindingAdapterTest() { + super(TableLayoutAdapterTestBinding.class, TableLayoutBindingObject.class, + R.layout.table_layout_adapter_test); + } + + @Override + protected void setUp() throws Exception { + super.setUp(); + mView = mBinder.view; + } + + public void testDivider() throws Throwable { + if (Build.VERSION.SDK_INT >= Build.VERSION_CODES.JELLY_BEAN) { + assertEquals(mBindingObject.getDivider(), + ((ColorDrawable) mView.getDividerDrawable()).getColor()); + changeValues(); + assertEquals(mBindingObject.getDivider(), + ((ColorDrawable) mView.getDividerDrawable()).getColor()); + } + } + + public void testColumns() throws Throwable { + assertFalse(mView.isColumnCollapsed(0)); + assertTrue(mView.isColumnCollapsed(1)); + assertFalse(mView.isColumnCollapsed(2)); + + assertFalse(mView.isColumnShrinkable(0)); + assertTrue(mView.isColumnShrinkable(1)); + assertFalse(mView.isColumnShrinkable(2)); + + assertFalse(mView.isColumnStretchable(0)); + assertTrue(mView.isColumnStretchable(1)); + assertFalse(mView.isColumnStretchable(2)); + + changeValues(); + + assertFalse(mView.isColumnCollapsed(0)); + assertFalse(mView.isColumnCollapsed(1)); + assertFalse(mView.isColumnCollapsed(2)); + + assertTrue(mView.isColumnShrinkable(0)); + assertTrue(mView.isColumnShrinkable(1)); + assertFalse(mView.isColumnShrinkable(2)); + + assertTrue(mView.isColumnStretchable(0)); + assertTrue(mView.isColumnStretchable(1)); + assertTrue(mView.isColumnStretchable(2)); + } +} diff --git a/tools/data-binding/integration-tests/TestApp/app/src/androidTest/java/android/databinding/testapp/TextViewBindingAdapterTest.java b/tools/data-binding/integration-tests/TestApp/app/src/androidTest/java/android/databinding/testapp/TextViewBindingAdapterTest.java new file mode 100644 index 0000000..98272dd --- /dev/null +++ b/tools/data-binding/integration-tests/TestApp/app/src/androidTest/java/android/databinding/testapp/TextViewBindingAdapterTest.java @@ -0,0 +1,304 @@ +/* + * 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 android.databinding.testapp; + +import android.databinding.testapp.databinding.TextViewAdapterTestBinding; +import android.databinding.testapp.vo.TextViewBindingObject; + +import android.annotation.TargetApi; +import android.databinding.adapters.TextViewBindingAdapter; +import android.graphics.drawable.ColorDrawable; +import android.os.Build; +import android.text.Editable; +import android.text.InputFilter; +import android.text.InputType; +import android.text.Spannable; +import android.text.method.DialerKeyListener; +import android.text.method.DigitsKeyListener; +import android.text.method.KeyListener; +import android.text.method.TextKeyListener; +import android.widget.TextView; + +public class TextViewBindingAdapterTest + extends BindingAdapterTestBase<TextViewAdapterTestBinding, TextViewBindingObject> { + + public TextViewBindingAdapterTest() { + super(TextViewAdapterTestBinding.class, TextViewBindingObject.class, + R.layout.text_view_adapter_test); + } + + public void testNumeric() throws Throwable { + TextView view = mBinder.numericText; + assertTrue(view.getKeyListener() instanceof DigitsKeyListener); + DigitsKeyListener listener = (DigitsKeyListener) view.getKeyListener(); + assertEquals(getExpectedNumericType(), listener.getInputType()); + + changeValues(); + + assertTrue(view.getKeyListener() instanceof DigitsKeyListener); + listener = (DigitsKeyListener) view.getKeyListener(); + assertEquals(getExpectedNumericType(), listener.getInputType()); + } + + private int getExpectedNumericType() { + int expectedType = InputType.TYPE_CLASS_NUMBER; + if ((mBindingObject.getNumeric() & TextViewBindingAdapter.SIGNED) != 0) { + expectedType |= InputType.TYPE_NUMBER_FLAG_SIGNED; + } + if ((mBindingObject.getNumeric() & TextViewBindingAdapter.DECIMAL) != 0) { + expectedType |= InputType.TYPE_NUMBER_FLAG_DECIMAL; + } + return expectedType; + } + + public void testDrawables() throws Throwable { + if (Build.VERSION.SDK_INT >= Build.VERSION_CODES.HONEYCOMB) { + TextView view = mBinder.textDrawableNormal; + assertEquals(mBindingObject.getDrawableLeft(), + ((ColorDrawable) view.getCompoundDrawables()[0]).getColor()); + assertEquals(mBindingObject.getDrawableTop(), + ((ColorDrawable) view.getCompoundDrawables()[1]).getColor()); + assertEquals(mBindingObject.getDrawableRight(), + ((ColorDrawable) view.getCompoundDrawables()[2]).getColor()); + assertEquals(mBindingObject.getDrawableBottom(), + ((ColorDrawable) view.getCompoundDrawables()[3]).getColor()); + + changeValues(); + + assertEquals(mBindingObject.getDrawableLeft(), + ((ColorDrawable) view.getCompoundDrawables()[0]).getColor()); + assertEquals(mBindingObject.getDrawableTop(), + ((ColorDrawable) view.getCompoundDrawables()[1]).getColor()); + assertEquals(mBindingObject.getDrawableRight(), + ((ColorDrawable) view.getCompoundDrawables()[2]).getColor()); + assertEquals(mBindingObject.getDrawableBottom(), + ((ColorDrawable) view.getCompoundDrawables()[3]).getColor()); + } + } + + public void testDrawableStartEnd() throws Throwable { + if (Build.VERSION.SDK_INT >= Build.VERSION_CODES.JELLY_BEAN_MR1) { + TextView view = mBinder.textDrawableStartEnd; + assertEquals(mBindingObject.getDrawableStart(), + ((ColorDrawable) view.getCompoundDrawablesRelative()[0]).getColor()); + assertEquals(mBindingObject.getDrawableEnd(), + ((ColorDrawable) view.getCompoundDrawablesRelative()[2]).getColor()); + + changeValues(); + + assertEquals(mBindingObject.getDrawableStart(), + ((ColorDrawable) view.getCompoundDrawablesRelative()[0]).getColor()); + assertEquals(mBindingObject.getDrawableEnd(), + ((ColorDrawable) view.getCompoundDrawablesRelative()[2]).getColor()); + } + } + + public void testSimpleProperties() throws Throwable { + TextView view = mBinder.textView; + + assertEquals(mBindingObject.getAutoLink(), view.getAutoLinkMask()); + assertEquals(mBindingObject.getDrawablePadding(), view.getCompoundDrawablePadding()); + assertEquals(mBindingObject.getTextSize(), view.getTextSize()); + assertEquals(mBindingObject.getTextColorHint(), view.getHintTextColors().getDefaultColor()); + assertEquals(mBindingObject.getTextColorLink(), view.getLinkTextColors().getDefaultColor()); + assertEquals(mBindingObject.isAutoText(), isAutoTextEnabled(view)); + assertEquals(mBindingObject.getCapitalize(), getCapitalization(view)); + assertEquals(mBindingObject.getImeActionLabel(), view.getImeActionLabel()); + assertEquals(mBindingObject.getImeActionId(), view.getImeActionId()); + if (Build.VERSION.SDK_INT >= Build.VERSION_CODES.JELLY_BEAN) { + assertEquals(mBindingObject.getTextColorHighlight(), view.getHighlightColor()); + assertEquals(mBindingObject.getLineSpacingExtra(), view.getLineSpacingExtra()); + assertEquals(mBindingObject.getLineSpacingMultiplier(), + view.getLineSpacingMultiplier()); + assertEquals(mBindingObject.getShadowColor(), view.getShadowColor()); + assertEquals(mBindingObject.getShadowDx(), view.getShadowDx()); + assertEquals(mBindingObject.getShadowDy(), view.getShadowDy()); + assertEquals(mBindingObject.getShadowRadius(), view.getShadowRadius()); + if (Build.VERSION.SDK_INT >= Build.VERSION_CODES.LOLLIPOP) { + assertEquals(mBindingObject.getMaxLength(), getMaxLength(view)); + } + } + + changeValues(); + + assertEquals(mBindingObject.getAutoLink(), view.getAutoLinkMask()); + assertEquals(mBindingObject.getDrawablePadding(), view.getCompoundDrawablePadding()); + assertEquals(mBindingObject.getTextSize(), view.getTextSize()); + assertEquals(mBindingObject.getTextColorHint(), view.getHintTextColors().getDefaultColor()); + assertEquals(mBindingObject.getTextColorLink(), view.getLinkTextColors().getDefaultColor()); + assertEquals(mBindingObject.isAutoText(), isAutoTextEnabled(view)); + assertEquals(mBindingObject.getCapitalize(), getCapitalization(view)); + assertEquals(mBindingObject.getImeActionLabel(), view.getImeActionLabel()); + assertEquals(mBindingObject.getImeActionId(), view.getImeActionId()); + if (Build.VERSION.SDK_INT >= Build.VERSION_CODES.JELLY_BEAN) { + assertEquals(mBindingObject.getTextColorHighlight(), view.getHighlightColor()); + assertEquals(mBindingObject.getLineSpacingExtra(), view.getLineSpacingExtra()); + assertEquals(mBindingObject.getLineSpacingMultiplier(), + view.getLineSpacingMultiplier()); + assertEquals(mBindingObject.getShadowColor(), view.getShadowColor()); + assertEquals(mBindingObject.getShadowDx(), view.getShadowDx()); + assertEquals(mBindingObject.getShadowDy(), view.getShadowDy()); + assertEquals(mBindingObject.getShadowRadius(), view.getShadowRadius()); + if (Build.VERSION.SDK_INT >= Build.VERSION_CODES.LOLLIPOP) { + assertEquals(mBindingObject.getMaxLength(), getMaxLength(view)); + } + } + + runTestOnUiThread(new Runnable() { + @Override + public void run() { + mBindingObject.setCapitalize(TextKeyListener.Capitalize.CHARACTERS); + mBinder.executePendingBindings(); + } + }); + + assertEquals(mBindingObject.getCapitalize(), getCapitalization(view)); + + runTestOnUiThread(new Runnable() { + @Override + public void run() { + mBindingObject.setCapitalize(TextKeyListener.Capitalize.WORDS); + mBinder.executePendingBindings(); + } + }); + + assertEquals(mBindingObject.getCapitalize(), getCapitalization(view)); + } + + private static boolean isAutoTextEnabled(TextView view) { + KeyListener keyListener = view.getKeyListener(); + if (keyListener == null) { + return false; + } + if (!(keyListener instanceof TextKeyListener)) { + return false; + } + TextKeyListener textKeyListener = (TextKeyListener) keyListener; + return ((textKeyListener.getInputType() & InputType.TYPE_TEXT_FLAG_AUTO_CORRECT) != 0); + } + + private static TextKeyListener.Capitalize getCapitalization(TextView view) { + KeyListener keyListener = view.getKeyListener(); + if (keyListener == null) { + return TextKeyListener.Capitalize.NONE; + } + int inputType = keyListener.getInputType(); + if ((inputType & InputType.TYPE_TEXT_FLAG_CAP_CHARACTERS) != 0) { + return TextKeyListener.Capitalize.CHARACTERS; + } else if ((inputType & InputType.TYPE_TEXT_FLAG_CAP_WORDS) != 0) { + return TextKeyListener.Capitalize.WORDS; + } else if ((inputType & InputType.TYPE_TEXT_FLAG_CAP_SENTENCES) != 0) { + return TextKeyListener.Capitalize.SENTENCES; + } else { + return TextKeyListener.Capitalize.NONE; + } + } + + @TargetApi(Build.VERSION_CODES.LOLLIPOP) + private static int getMaxLength(TextView view) { + InputFilter[] filters = view.getFilters(); + for (InputFilter filter : filters) { + if (filter instanceof InputFilter.LengthFilter) { + InputFilter.LengthFilter lengthFilter = (InputFilter.LengthFilter) filter; + return lengthFilter.getMax(); + } + } + return -1; + } + + public void testAllCaps() throws Throwable { + TextView view = mBinder.textAllCaps; + + assertEquals(mBindingObject.isTextAllCaps(), view.getTransformationMethod() != null); + if (view.getTransformationMethod() != null) { + assertEquals("ALL CAPS", + view.getTransformationMethod().getTransformation("all caps", view)); + } + + changeValues(); + + assertEquals(mBindingObject.isTextAllCaps(), view.getTransformationMethod() != null); + if (view.getTransformationMethod() != null) { + assertEquals("ALL CAPS", + view.getTransformationMethod().getTransformation("all caps", view)); + } + } + + public void testBufferType() throws Throwable { + TextView view = mBinder.textBufferType; + + assertEquals(mBindingObject.getBufferType(), getBufferType(view)); + changeValues(); + assertEquals(mBindingObject.getBufferType(), getBufferType(view)); + } + + private static TextView.BufferType getBufferType(TextView view) { + CharSequence text = view.getText(); + if (text instanceof Editable) { + return TextView.BufferType.EDITABLE; + } + if (text instanceof Spannable) { + return TextView.BufferType.SPANNABLE; + } + return TextView.BufferType.NORMAL; + } + + public void testInputType() throws Throwable { + TextView view = mBinder.textInputType; + assertEquals(mBindingObject.getInputType(), view.getInputType()); + changeValues(); + assertEquals(mBindingObject.getInputType(), view.getInputType()); + } + + public void testDigits() throws Throwable { + TextView view = mBinder.textDigits; + assertEquals(mBindingObject.getDigits(), getDigits(view)); + changeValues(); + assertEquals(mBindingObject.getDigits(), getDigits(view)); + } + + private static String getDigits(TextView textView) { + KeyListener keyListener = textView.getKeyListener(); + if (!(keyListener instanceof DigitsKeyListener)) { + return null; + } + DigitsKeyListener digitsKeyListener = (DigitsKeyListener) keyListener; + String input = "abcdefghijklmnopqrstuvwxyz"; + Spannable spannable = Spannable.Factory.getInstance().newSpannable(input); + return digitsKeyListener.filter(input, 0, input.length(), spannable, 0, input.length()) + .toString(); + } + + public void testPhoneNumber() throws Throwable { + TextView textView = mBinder.textPhoneNumber; + assertEquals(mBindingObject.isPhoneNumber(), isPhoneNumber(textView)); + changeValues(); + assertEquals(mBindingObject.isPhoneNumber(), isPhoneNumber(textView)); + } + + private static boolean isPhoneNumber(TextView view) { + KeyListener keyListener = view.getKeyListener(); + return (keyListener instanceof DialerKeyListener); + } + + public void testInputMethod() throws Throwable { + TextView textView = mBinder.textInputMethod; + assertTrue(TextViewBindingObject.KeyListener1.class.isInstance(textView.getKeyListener())); + changeValues(); + assertTrue(TextViewBindingObject.KeyListener2.class.isInstance(textView.getKeyListener())); + } + +} diff --git a/tools/data-binding/integration-tests/TestApp/app/src/androidTest/java/android/databinding/testapp/ViewBindingAdapterTest.java b/tools/data-binding/integration-tests/TestApp/app/src/androidTest/java/android/databinding/testapp/ViewBindingAdapterTest.java new file mode 100644 index 0000000..c9bbb35 --- /dev/null +++ b/tools/data-binding/integration-tests/TestApp/app/src/androidTest/java/android/databinding/testapp/ViewBindingAdapterTest.java @@ -0,0 +1,180 @@ +/* + * 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 android.databinding.testapp; + +import android.databinding.testapp.databinding.ViewAdapterTestBinding; +import android.databinding.testapp.vo.ViewBindingObject; + +import android.content.res.ColorStateList; +import android.os.Build; +import android.test.UiThreadTest; +import android.view.View; + +public class ViewBindingAdapterTest extends BindingAdapterTestBase<ViewAdapterTestBinding, ViewBindingObject> { + + public ViewBindingAdapterTest() { + super(ViewAdapterTestBinding.class, ViewBindingObject.class, R.layout.view_adapter_test); + } + + @Override + protected void setUp() throws Exception { + super.setUp(); + } + + public void testPadding() throws Throwable { + View view = mBinder.padding; + assertEquals(mBindingObject.getPadding(), view.getPaddingBottom()); + assertEquals(mBindingObject.getPadding(), view.getPaddingTop()); + assertEquals(mBindingObject.getPadding(), view.getPaddingRight()); + assertEquals(mBindingObject.getPadding(), view.getPaddingLeft()); + + changeValues(); + + assertEquals(mBindingObject.getPadding(), view.getPaddingBottom()); + assertEquals(mBindingObject.getPadding(), view.getPaddingTop()); + assertEquals(mBindingObject.getPadding(), view.getPaddingRight()); + assertEquals(mBindingObject.getPadding(), view.getPaddingLeft()); + } + + public void testPaddingLeftRight() throws Throwable { + View view = mBinder.paddingLeftRight; + assertEquals(mBindingObject.getPaddingLeft(), view.getPaddingLeft()); + assertEquals(mBindingObject.getPaddingRight(), view.getPaddingRight()); + + changeValues(); + + assertEquals(mBindingObject.getPaddingLeft(), view.getPaddingLeft()); + assertEquals(mBindingObject.getPaddingRight(), view.getPaddingRight()); + } + + public void testPaddingStartEnd() throws Throwable { + if (Build.VERSION.SDK_INT >= Build.VERSION_CODES.JELLY_BEAN_MR1) { + View view = mBinder.paddingStartEnd; + assertEquals(mBindingObject.getPaddingStart(), view.getPaddingStart()); + assertEquals(mBindingObject.getPaddingEnd(), view.getPaddingEnd()); + + changeValues(); + + assertEquals(mBindingObject.getPaddingStart(), view.getPaddingStart()); + assertEquals(mBindingObject.getPaddingEnd(), view.getPaddingEnd()); + } + } + + public void testPaddingTopBottom() throws Throwable { + View view = mBinder.paddingTopBottom; + assertEquals(mBindingObject.getPaddingTop(), view.getPaddingTop()); + assertEquals(mBindingObject.getPaddingBottom(), view.getPaddingBottom()); + + changeValues(); + + assertEquals(mBindingObject.getPaddingTop(), view.getPaddingTop()); + assertEquals(mBindingObject.getPaddingBottom(), view.getPaddingBottom()); + } + + public void testBackgroundTint() throws Throwable { + if (Build.VERSION.SDK_INT >= Build.VERSION_CODES.LOLLIPOP) { + View view = mBinder.backgroundTint; + assertNotNull(view.getBackgroundTintList()); + ColorStateList colorStateList = view.getBackgroundTintList(); + assertEquals(mBindingObject.getBackgroundTint(), colorStateList.getDefaultColor()); + + changeValues(); + + assertNotNull(view.getBackgroundTintList()); + colorStateList = view.getBackgroundTintList(); + assertEquals(mBindingObject.getBackgroundTint(), colorStateList.getDefaultColor()); + } + } + + public void testFadeScrollbars() throws Throwable { + View view = mBinder.fadeScrollbars; + assertEquals(mBindingObject.getFadeScrollbars(), view.isScrollbarFadingEnabled()); + + changeValues(); + + assertEquals(mBindingObject.getFadeScrollbars(), view.isScrollbarFadingEnabled()); + } + + public void testNextFocus() throws Throwable { + View view = mBinder.nextFocus; + + assertEquals(mBindingObject.getNextFocusDown(), view.getNextFocusDownId()); + assertEquals(mBindingObject.getNextFocusUp(), view.getNextFocusUpId()); + assertEquals(mBindingObject.getNextFocusLeft(), view.getNextFocusLeftId()); + assertEquals(mBindingObject.getNextFocusRight(), view.getNextFocusRightId()); + if (Build.VERSION.SDK_INT >= Build.VERSION_CODES.HONEYCOMB) { + assertEquals(mBindingObject.getNextFocusForward(), view.getNextFocusForwardId()); + } + + changeValues(); + + assertEquals(mBindingObject.getNextFocusDown(), view.getNextFocusDownId()); + assertEquals(mBindingObject.getNextFocusUp(), view.getNextFocusUpId()); + assertEquals(mBindingObject.getNextFocusLeft(), view.getNextFocusLeftId()); + assertEquals(mBindingObject.getNextFocusRight(), view.getNextFocusRightId()); + if (Build.VERSION.SDK_INT >= Build.VERSION_CODES.HONEYCOMB) { + assertEquals(mBindingObject.getNextFocusForward(), view.getNextFocusForwardId()); + } + } + + public void testRequiresFadingEdge() throws Throwable { + View view = mBinder.requiresFadingEdge; + + assertTrue(view.isVerticalFadingEdgeEnabled()); + assertFalse(view.isHorizontalFadingEdgeEnabled()); + + changeValues(); + + assertFalse(view.isVerticalFadingEdgeEnabled()); + assertTrue(view.isHorizontalFadingEdgeEnabled()); + } + + public void testScrollbar() throws Throwable { + View view = mBinder.scrollbar; + + if (Build.VERSION.SDK_INT >= Build.VERSION_CODES.JELLY_BEAN) { + assertEquals(mBindingObject.getScrollbarDefaultDelayBeforeFade(), + view.getScrollBarDefaultDelayBeforeFade()); + assertEquals(mBindingObject.getScrollbarFadeDuration(), view.getScrollBarFadeDuration()); + assertEquals(mBindingObject.getScrollbarSize(), view.getScrollBarSize()); + } + assertEquals(mBindingObject.getScrollbarStyle(), view.getScrollBarStyle()); + + changeValues(); + + if (Build.VERSION.SDK_INT >= Build.VERSION_CODES.JELLY_BEAN) { + assertEquals(mBindingObject.getScrollbarDefaultDelayBeforeFade(), + view.getScrollBarDefaultDelayBeforeFade()); + assertEquals(mBindingObject.getScrollbarFadeDuration(), view.getScrollBarFadeDuration()); + assertEquals(mBindingObject.getScrollbarSize(), view.getScrollBarSize()); + } + assertEquals(mBindingObject.getScrollbarStyle(), view.getScrollBarStyle()); + } + + public void testTransformPivot() throws Throwable { + if (Build.VERSION.SDK_INT >= Build.VERSION_CODES.HONEYCOMB) { + View view = mBinder.transformPivot; + + assertEquals(mBindingObject.getTransformPivotX(), view.getPivotX()); + assertEquals(mBindingObject.getTransformPivotY(), view.getPivotY()); + + changeValues(); + + assertEquals(mBindingObject.getTransformPivotX(), view.getPivotX()); + assertEquals(mBindingObject.getTransformPivotY(), view.getPivotY()); + } + } +} diff --git a/tools/data-binding/integration-tests/TestApp/app/src/androidTest/java/android/databinding/testapp/ViewGroupBindingAdapterTest.java b/tools/data-binding/integration-tests/TestApp/app/src/androidTest/java/android/databinding/testapp/ViewGroupBindingAdapterTest.java new file mode 100644 index 0000000..981495d --- /dev/null +++ b/tools/data-binding/integration-tests/TestApp/app/src/androidTest/java/android/databinding/testapp/ViewGroupBindingAdapterTest.java @@ -0,0 +1,81 @@ +/* + * 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 android.databinding.testapp; + +import android.databinding.testapp.databinding.ViewGroupAdapterTestBinding; +import android.databinding.testapp.vo.ViewGroupBindingObject; + +import android.os.Build; +import android.view.ViewGroup; + +public class ViewGroupBindingAdapterTest + extends BindingAdapterTestBase<ViewGroupAdapterTestBinding, ViewGroupBindingObject> { + + ViewGroup mView; + + public ViewGroupBindingAdapterTest() { + super(ViewGroupAdapterTestBinding.class, ViewGroupBindingObject.class, + R.layout.view_group_adapter_test); + } + + @Override + protected void setUp() throws Exception { + super.setUp(); + mView = mBinder.view; + } + + public void testDrawnWithCache() throws Throwable { + assertEquals(mBindingObject.isAlwaysDrawnWithCache(), + mView.isAlwaysDrawnWithCacheEnabled()); + + changeValues(); + + assertEquals(mBindingObject.isAlwaysDrawnWithCache(), + mView.isAlwaysDrawnWithCacheEnabled()); + } + + public void testAnimationCache() throws Throwable { + assertEquals(mBindingObject.isAnimationCache(), mView.isAnimationCacheEnabled()); + + changeValues(); + + assertEquals(mBindingObject.isAnimationCache(), mView.isAnimationCacheEnabled()); + } + + public void testSplitMotionEvents() throws Throwable { + if (Build.VERSION.SDK_INT >= Build.VERSION_CODES.HONEYCOMB) { + assertEquals(mBindingObject.isSplitMotionEvents(), + mView.isMotionEventSplittingEnabled()); + + changeValues(); + + assertEquals(mBindingObject.isSplitMotionEvents(), + mView.isMotionEventSplittingEnabled()); + } + } + + public void testAnimateLayoutChanges() throws Throwable { + if (Build.VERSION.SDK_INT >= Build.VERSION_CODES.HONEYCOMB) { + assertEquals(mBindingObject.isAnimateLayoutChanges(), + mView.getLayoutTransition() != null); + + changeValues(); + + assertEquals(mBindingObject.isAnimateLayoutChanges(), + mView.getLayoutTransition() != null); + } + } +} diff --git a/tools/data-binding/integration-tests/TestApp/app/src/androidTest/java/android/databinding/testapp/ViewStubBindingAdapterTest.java b/tools/data-binding/integration-tests/TestApp/app/src/androidTest/java/android/databinding/testapp/ViewStubBindingAdapterTest.java new file mode 100644 index 0000000..87cb5b2 --- /dev/null +++ b/tools/data-binding/integration-tests/TestApp/app/src/androidTest/java/android/databinding/testapp/ViewStubBindingAdapterTest.java @@ -0,0 +1,46 @@ +/* + * 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 android.databinding.testapp; + +import android.databinding.testapp.databinding.ViewStubAdapterTestBinding; +import android.databinding.testapp.vo.ViewStubBindingObject; + +import android.view.ViewStub; + +public class ViewStubBindingAdapterTest + extends BindingAdapterTestBase<ViewStubAdapterTestBinding, ViewStubBindingObject> { + + ViewStub mView; + + public ViewStubBindingAdapterTest() { + super(ViewStubAdapterTestBinding.class, ViewStubBindingObject.class, + R.layout.view_stub_adapter_test); + } + + @Override + protected void setUp() throws Exception { + super.setUp(); + mView = mBinder.view.getViewStub(); + } + + public void testLayout() throws Throwable { + assertEquals(mBindingObject.getLayout(), mView.getLayoutResource()); + + changeValues(); + + assertEquals(mBindingObject.getLayout(), mView.getLayoutResource()); + } +} diff --git a/tools/data-binding/integration-tests/TestApp/app/src/androidTest/java/android/databinding/testapp/ViewStubTest.java b/tools/data-binding/integration-tests/TestApp/app/src/androidTest/java/android/databinding/testapp/ViewStubTest.java new file mode 100644 index 0000000..a78bda3 --- /dev/null +++ b/tools/data-binding/integration-tests/TestApp/app/src/androidTest/java/android/databinding/testapp/ViewStubTest.java @@ -0,0 +1,87 @@ +/* + * 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 android.databinding.testapp; + +import android.databinding.testapp.databinding.ViewStubBinding; +import android.databinding.testapp.databinding.ViewStubContentsBinding; +import android.databinding.ViewStubProxy; +import android.support.v4.util.ArrayMap; +import android.test.UiThreadTest; +import android.view.View; +import android.widget.TextView; + +import java.util.ArrayList; + +public class ViewStubTest extends BaseDataBinderTest<ViewStubBinding> { + + public ViewStubTest() { + super(ViewStubBinding.class); + } + + @Override + protected void setUp() throws Exception { + super.setUp(); + mBinder.setViewStubVisibility(View.GONE); + mBinder.setFirstName("Hello"); + mBinder.setLastName("World"); + try { + runTestOnUiThread(new Runnable() { + @Override + public void run() { + mBinder.executePendingBindings(); + } + }); + } catch (Exception e) { + throw e; + } catch (Throwable t) { + throw new Exception(t); + } + } + + @UiThreadTest + public void testInflation() throws Throwable { + ViewStubProxy viewStubProxy = mBinder.viewStub; + assertFalse(viewStubProxy.isInflated()); + assertNull(viewStubProxy.getBinding()); + assertNotNull(viewStubProxy.getViewStub()); + assertNull(mBinder.getRoot().findViewById(R.id.firstNameContents)); + assertNull(mBinder.getRoot().findViewById(R.id.lastNameContents)); + mBinder.setViewStubVisibility(View.VISIBLE); + mBinder.executePendingBindings(); + assertTrue(viewStubProxy.isInflated()); + assertNotNull(viewStubProxy.getBinding()); + assertNull(viewStubProxy.getViewStub()); + ViewStubContentsBinding contentsBinding = (ViewStubContentsBinding) + viewStubProxy.getBinding(); + assertNotNull(contentsBinding.firstNameContents); + assertNotNull(contentsBinding.lastNameContents); + assertEquals("Hello", contentsBinding.firstNameContents.getText().toString()); + assertEquals("World", contentsBinding.lastNameContents.getText().toString()); + } + + @UiThreadTest + public void testChangeValues() throws Throwable { + ViewStubProxy viewStubProxy = mBinder.viewStub; + mBinder.setViewStubVisibility(View.VISIBLE); + mBinder.executePendingBindings(); + ViewStubContentsBinding contentsBinding = (ViewStubContentsBinding) + viewStubProxy.getBinding(); + assertEquals("Hello", contentsBinding.firstNameContents.getText().toString()); + mBinder.setFirstName("Goodbye"); + mBinder.executePendingBindings(); + assertEquals("Goodbye", contentsBinding.firstNameContents.getText().toString()); + } +} diff --git a/tools/data-binding/integration-tests/TestApp/app/src/androidTest/java/android/databinding/testapp/multiconfig/LandscapeConfigTest.java b/tools/data-binding/integration-tests/TestApp/app/src/androidTest/java/android/databinding/testapp/multiconfig/LandscapeConfigTest.java new file mode 100644 index 0000000..7f38f3b --- /dev/null +++ b/tools/data-binding/integration-tests/TestApp/app/src/androidTest/java/android/databinding/testapp/multiconfig/LandscapeConfigTest.java @@ -0,0 +1,51 @@ +/* + * 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 android.databinding.testapp.multiconfig; + +import android.databinding.ViewDataBinding; +import android.databinding.testapp.BaseLandDataBinderTest; +import android.databinding.testapp.R; +import android.databinding.testapp.databinding.BasicBindingBinding; +import android.databinding.testapp.databinding.ConditionalBindingBinding; +import android.databinding.testapp.databinding.IncludedLayoutBinding; +import android.databinding.testapp.databinding.MultiResLayoutBinding; +import android.databinding.testapp.vo.NotBindableVo; + +import android.content.pm.ActivityInfo; +import android.view.View; +import android.widget.TextView; + +public class LandscapeConfigTest extends BaseLandDataBinderTest<MultiResLayoutBinding> { + + public LandscapeConfigTest() { + super(MultiResLayoutBinding.class, ActivityInfo.SCREEN_ORIENTATION_LANDSCAPE); + } + + public void testSharedViewIdAndVariableInheritance() + throws InterruptedException, NoSuchMethodException, NoSuchFieldException { + assertEquals("MultiResLayoutBindingLandImpl", mBinder.getClass().getSimpleName()); + assertPublicField(TextView.class, "objectInLandTextView"); + assertPublicField(TextView.class, "objectInDefaultTextView"); + assertPublicField(View.class, "objectInDefaultTextView2"); + + assertField(NotBindableVo.class, "mObjectInLand"); + assertField(NotBindableVo.class, "mObjectInDefault"); + + // includes + assertPublicField(ViewDataBinding.class, "includedLayoutConflict"); + assertPublicField(BasicBindingBinding.class, "includedLayoutShared"); + assertPublicField(ConditionalBindingBinding.class, "includedLayoutPort"); + assertPublicField(ConditionalBindingBinding.class, "includedLayoutLand"); + } +} diff --git a/tools/data-binding/integration-tests/TestApp/app/src/androidTest/java/android/databinding/testapp/multiconfig/PortraitConfigTest.java b/tools/data-binding/integration-tests/TestApp/app/src/androidTest/java/android/databinding/testapp/multiconfig/PortraitConfigTest.java new file mode 100644 index 0000000..a4e4f2f --- /dev/null +++ b/tools/data-binding/integration-tests/TestApp/app/src/androidTest/java/android/databinding/testapp/multiconfig/PortraitConfigTest.java @@ -0,0 +1,49 @@ +/* + * 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 android.databinding.testapp.multiconfig; + +import android.databinding.ViewDataBinding; +import android.databinding.testapp.BaseDataBinderTest; +import android.databinding.testapp.databinding.BasicBindingBinding; +import android.databinding.testapp.databinding.ConditionalBindingBinding; +import android.databinding.testapp.databinding.IncludedLayoutBinding; +import android.databinding.testapp.databinding.MultiResLayoutBinding; +import android.databinding.testapp.vo.NotBindableVo; + +import android.content.pm.ActivityInfo; +import android.view.View; +import android.widget.EditText; +import android.widget.TextView; + +public class PortraitConfigTest extends BaseDataBinderTest<MultiResLayoutBinding> { + public PortraitConfigTest() { + super(MultiResLayoutBinding.class, ActivityInfo.SCREEN_ORIENTATION_PORTRAIT); + } + + public void testSharedViewIdAndVariableInheritance() + throws InterruptedException, NoSuchMethodException, NoSuchFieldException { + assertEquals("MultiResLayoutBindingImpl", mBinder.getClass().getSimpleName()); + assertPublicField(TextView.class, "objectInLandTextView"); + assertPublicField(TextView.class, "objectInDefaultTextView"); + assertPublicField(View.class, "objectInDefaultTextView2"); + + assertField(NotBindableVo.class, "mObjectInDefault"); + + // includes + assertPublicField(ViewDataBinding.class, "includedLayoutConflict"); + assertPublicField(BasicBindingBinding.class, "includedLayoutShared"); + assertPublicField(ConditionalBindingBinding.class, "includedLayoutPort"); + assertPublicField(ConditionalBindingBinding.class, "includedLayoutLand"); + } +} diff --git a/tools/data-binding/integration-tests/TestApp/app/src/main/AndroidManifest.xml b/tools/data-binding/integration-tests/TestApp/app/src/main/AndroidManifest.xml new file mode 100644 index 0000000..c6f719e --- /dev/null +++ b/tools/data-binding/integration-tests/TestApp/app/src/main/AndroidManifest.xml @@ -0,0 +1,25 @@ +<!-- + ~ 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. + --> + +<manifest xmlns:android="http://schemas.android.com/apk/res/android" + package="android.databinding.testapp"> + + <application android:allowBackup="true" + android:label="@string/app_name" + android:icon="@drawable/ic_launcher" + > + <activity android:name=".TestActivity" + android:screenOrientation="portrait"/> + </application> + +</manifest> diff --git a/tools/data-binding/integration-tests/TestApp/app/src/main/java/android/databinding/testapp/TestActivity.java b/tools/data-binding/integration-tests/TestApp/app/src/main/java/android/databinding/testapp/TestActivity.java new file mode 100644 index 0000000..d5f95e6 --- /dev/null +++ b/tools/data-binding/integration-tests/TestApp/app/src/main/java/android/databinding/testapp/TestActivity.java @@ -0,0 +1,30 @@ +/* + * Copyright (C) 2015 The Android Open Source Project + * + * Licensed under the Apache License, Version 2.0 (the "License"); + * you may not use this file except in compliance with the License. + * You may obtain a copy of the License at + * + * http://www.apache.org/licenses/LICENSE-2.0 + * + * Unless required by applicable law or agreed to in writing, software + * distributed under the License is distributed on an "AS IS" BASIS, + * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. + * See the License for the specific language governing permissions and + * limitations under the License. + */ + +package android.databinding.testapp; + +import android.app.Activity; +import android.os.Bundle; +import android.view.WindowManager; + +public class TestActivity extends Activity { + + @Override + protected void onCreate(Bundle savedInstanceState) { + super.onCreate(savedInstanceState); + getWindow().addFlags(WindowManager.LayoutParams.FLAG_KEEP_SCREEN_ON); + } +} diff --git a/tools/data-binding/integration-tests/TestApp/app/src/main/java/android/databinding/testapp/vo/AbsListViewBindingObject.java b/tools/data-binding/integration-tests/TestApp/app/src/main/java/android/databinding/testapp/vo/AbsListViewBindingObject.java new file mode 100644 index 0000000..24bc067 --- /dev/null +++ b/tools/data-binding/integration-tests/TestApp/app/src/main/java/android/databinding/testapp/vo/AbsListViewBindingObject.java @@ -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 android.databinding.testapp.vo; + +import android.databinding.Bindable; +import android.graphics.drawable.ColorDrawable; + +public class AbsListViewBindingObject extends BindingAdapterBindingObject { + @Bindable + private ColorDrawable mListSelector = new ColorDrawable(0xFFFF0000); + @Bindable + private boolean mScrollingCache; + @Bindable + private boolean mSmoothScrollbar; + + public ColorDrawable getListSelector() { + return mListSelector; + } + + public boolean isScrollingCache() { + return mScrollingCache; + } + + public boolean isSmoothScrollbar() { + return mSmoothScrollbar; + } + + public void changeValues() { + mListSelector = new ColorDrawable(0xFFFFFFFF); + mScrollingCache = true; + mSmoothScrollbar = true; + notifyChange(); + } +} diff --git a/tools/data-binding/integration-tests/TestApp/app/src/main/java/android/databinding/testapp/vo/AbsSeekBarBindingObject.java b/tools/data-binding/integration-tests/TestApp/app/src/main/java/android/databinding/testapp/vo/AbsSeekBarBindingObject.java new file mode 100644 index 0000000..bc8c3cb --- /dev/null +++ b/tools/data-binding/integration-tests/TestApp/app/src/main/java/android/databinding/testapp/vo/AbsSeekBarBindingObject.java @@ -0,0 +1,32 @@ +/* + * 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 android.databinding.testapp.vo; + +import android.databinding.Bindable; + +public class AbsSeekBarBindingObject extends BindingAdapterBindingObject { + @Bindable + private int mThumbTint = 0xFFFF0000; + + public int getThumbTint() { + return mThumbTint; + } + + public void changeValues() { + mThumbTint = 0xFF00FF00; + notifyChange(); + } +} diff --git a/tools/data-binding/integration-tests/TestApp/app/src/main/java/android/databinding/testapp/vo/AbsSpinnerBindingObject.java b/tools/data-binding/integration-tests/TestApp/app/src/main/java/android/databinding/testapp/vo/AbsSpinnerBindingObject.java new file mode 100644 index 0000000..05d7ed7 --- /dev/null +++ b/tools/data-binding/integration-tests/TestApp/app/src/main/java/android/databinding/testapp/vo/AbsSpinnerBindingObject.java @@ -0,0 +1,41 @@ +/* + * 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 android.databinding.testapp.vo; + +import android.databinding.Bindable; + +public class AbsSpinnerBindingObject extends BindingAdapterBindingObject { + @Bindable + private CharSequence[] mEntries = { + "hello", + "world", + }; + + private static final CharSequence[] CHANGED_VALUES = { + "goodbye", + "cruel", + "world" + }; + + public CharSequence[] getEntries() { + return mEntries; + } + + public void changeValues() { + mEntries = CHANGED_VALUES; + notifyChange(); + } +} diff --git a/tools/data-binding/integration-tests/TestApp/app/src/main/java/android/databinding/testapp/vo/AutoCompleteTextViewBindingObject.java b/tools/data-binding/integration-tests/TestApp/app/src/main/java/android/databinding/testapp/vo/AutoCompleteTextViewBindingObject.java new file mode 100644 index 0000000..eef46d6 --- /dev/null +++ b/tools/data-binding/integration-tests/TestApp/app/src/main/java/android/databinding/testapp/vo/AutoCompleteTextViewBindingObject.java @@ -0,0 +1,40 @@ +/* + * 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 android.databinding.testapp.vo; + +import android.databinding.Bindable; + +public class AutoCompleteTextViewBindingObject extends BindingAdapterBindingObject { + @Bindable + private int mPopupBackground; + + @Bindable + private int mCompletionThreshold = 1; + + public int getCompletionThreshold() { + return mCompletionThreshold; + } + + public int getPopupBackground() { + return mPopupBackground; + } + + public void changeValues() { + mPopupBackground = 0xFF23456; + mCompletionThreshold = 5; + notifyChange(); + } +} diff --git a/tools/data-binding/integration-tests/TestApp/app/src/main/java/android/databinding/testapp/vo/BasicObject.java b/tools/data-binding/integration-tests/TestApp/app/src/main/java/android/databinding/testapp/vo/BasicObject.java new file mode 100644 index 0000000..450f7fb --- /dev/null +++ b/tools/data-binding/integration-tests/TestApp/app/src/main/java/android/databinding/testapp/vo/BasicObject.java @@ -0,0 +1,45 @@ +/* + * 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 android.databinding.testapp.vo; +import android.databinding.BaseObservable; +import android.databinding.Bindable; +import android.databinding.testapp.BR; + +public class BasicObject extends BaseObservable { + @Bindable + private String mField1; + @Bindable + private String mField2; + + public String getField1() { + return mField1; + } + + public void setField1(String field1) { + this.mField1 = field1; + notifyPropertyChanged(BR.field1); + } + + public String getField2() { + return mField2; + } + + public void setField2(String field2) { + this.mField2 = field2; + notifyPropertyChanged(BR.field1); + } +} diff --git a/tools/data-binding/integration-tests/TestApp/app/src/main/java/android/databinding/testapp/vo/BindableTestObject.java b/tools/data-binding/integration-tests/TestApp/app/src/main/java/android/databinding/testapp/vo/BindableTestObject.java new file mode 100644 index 0000000..d297f8a --- /dev/null +++ b/tools/data-binding/integration-tests/TestApp/app/src/main/java/android/databinding/testapp/vo/BindableTestObject.java @@ -0,0 +1,52 @@ +/* + * 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 android.databinding.testapp.vo; + +import android.databinding.Bindable; + +public class BindableTestObject { + @Bindable + public int bindableField1; + + @Bindable + private int bindableField2; + + private int bindableField3; + + @Bindable + public int m_bindableField4; + + @Bindable + public int mbindableField5; + + @Bindable + public int _bindableField6; + + @Bindable + public int _BindableField7; + + @Bindable + public int mBindableField8; + + public int getBindableField2() { + return bindableField2; + } + + @Bindable + public int getBindableField3() { + return bindableField3; + } +} diff --git a/tools/data-binding/integration-tests/TestApp/app/src/main/java/android/databinding/testapp/vo/BindingAdapterBindingObject.java b/tools/data-binding/integration-tests/TestApp/app/src/main/java/android/databinding/testapp/vo/BindingAdapterBindingObject.java new file mode 100644 index 0000000..404e104 --- /dev/null +++ b/tools/data-binding/integration-tests/TestApp/app/src/main/java/android/databinding/testapp/vo/BindingAdapterBindingObject.java @@ -0,0 +1,23 @@ +/* + * 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 android.databinding.testapp.vo; + +import android.databinding.BaseObservable; + +public abstract class BindingAdapterBindingObject extends BaseObservable { + + public abstract void changeValues(); +} diff --git a/tools/data-binding/integration-tests/TestApp/app/src/main/java/android/databinding/testapp/vo/CheckedTextViewBindingObject.java b/tools/data-binding/integration-tests/TestApp/app/src/main/java/android/databinding/testapp/vo/CheckedTextViewBindingObject.java new file mode 100644 index 0000000..75eba50 --- /dev/null +++ b/tools/data-binding/integration-tests/TestApp/app/src/main/java/android/databinding/testapp/vo/CheckedTextViewBindingObject.java @@ -0,0 +1,41 @@ +/* + * 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 android.databinding.testapp.vo; + +import android.databinding.Bindable; +import android.graphics.drawable.ColorDrawable; + +public class CheckedTextViewBindingObject extends BindingAdapterBindingObject { + @Bindable + private ColorDrawable mCheckMark = new ColorDrawable(0xFF123456); + + @Bindable + private int mCheckMarkTint = 0xDead_Beef; + + public ColorDrawable getCheckMark() { + return mCheckMark; + } + + public int getCheckMarkTint() { + return mCheckMarkTint; + } + + public void changeValues() { + mCheckMark = new ColorDrawable(0xFF111111); + mCheckMarkTint = 0xFF222222; + notifyChange(); + } +} diff --git a/tools/data-binding/integration-tests/TestApp/app/src/main/java/android/databinding/testapp/vo/CompoundButtonBindingObject.java b/tools/data-binding/integration-tests/TestApp/app/src/main/java/android/databinding/testapp/vo/CompoundButtonBindingObject.java new file mode 100644 index 0000000..bb13201 --- /dev/null +++ b/tools/data-binding/integration-tests/TestApp/app/src/main/java/android/databinding/testapp/vo/CompoundButtonBindingObject.java @@ -0,0 +1,32 @@ +/* + * 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 android.databinding.testapp.vo; + +import android.databinding.Bindable; + +public class CompoundButtonBindingObject extends BindingAdapterBindingObject { + @Bindable + private int mButtonTint; + + public int getButtonTint() { + return mButtonTint; + } + + public void changeValues() { + mButtonTint = 0xFF111111; + notifyChange(); + } +} diff --git a/tools/data-binding/integration-tests/TestApp/app/src/main/java/android/databinding/testapp/vo/FindMethodBindingObject.java b/tools/data-binding/integration-tests/TestApp/app/src/main/java/android/databinding/testapp/vo/FindMethodBindingObject.java new file mode 100644 index 0000000..2281e22 --- /dev/null +++ b/tools/data-binding/integration-tests/TestApp/app/src/main/java/android/databinding/testapp/vo/FindMethodBindingObject.java @@ -0,0 +1,67 @@ +/* + * 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 android.databinding.testapp.vo; + +import android.util.ArrayMap; + +import java.util.ArrayList; +import java.util.List; +import java.util.Map; + +public class FindMethodBindingObject extends FindMethodBindingObjectBase { + public String method() { return "no arg"; } + + public String method(int i) { return String.valueOf(i); } + + public String method(float f) { return String.valueOf(f); } + + public String method(String value) { return value; } + + public static String staticMethod() { return "world"; } + + public static Foo foo = new Foo(); + + public static Bar<String> bar = new Bar<>(); + + public float confusingParam(int i) { return i; } + public String confusingParam(Object o) { return o.toString(); } + + public int confusingPrimitive(int i) { return i; } + public String confusingPrimitive(Integer i) { return i.toString(); } + + public float confusingInheritance(Object o) { return 0; } + public String confusingInheritance(String s) { return s; } + public int confusingInheritance(Integer i) { return i; } + + public int confusingTypeArgs(List<String> s) { return 0; } + public String confusingTypeArgs(Map<String, String> s) { return "yay"; } + + public ArrayMap<String, String> getMap() { return null; } + + public List getList() { + ArrayList<String> vals = new ArrayList<>(); + vals.add("hello"); + return vals; + } + + public static class Foo { + public final String bar = "hello world"; + } + + public static class Bar<T> { + public T method(T value) { return value; } + } +} diff --git a/tools/data-binding/integration-tests/TestApp/app/src/main/java/android/databinding/testapp/vo/FindMethodBindingObjectBase.java b/tools/data-binding/integration-tests/TestApp/app/src/main/java/android/databinding/testapp/vo/FindMethodBindingObjectBase.java new file mode 100644 index 0000000..8678e1e --- /dev/null +++ b/tools/data-binding/integration-tests/TestApp/app/src/main/java/android/databinding/testapp/vo/FindMethodBindingObjectBase.java @@ -0,0 +1,30 @@ +/* + * Copyright (C) 2015 The Android Open Source Project + * + * Licensed under the Apache License, Version 2.0 (the "License"); + * you may not use this file except in compliance with the License. + * You may obtain a copy of the License at + * + * http://www.apache.org/licenses/LICENSE-2.0 + * + * Unless required by applicable law or agreed to in writing, software + * distributed under the License is distributed on an "AS IS" BASIS, + * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. + * See the License for the specific language governing permissions and + * limitations under the License. + */ +package android.databinding.testapp.vo; + +public class FindMethodBindingObjectBase extends BindingAdapterBindingObject { + public String inheritedMethod() { + return "base"; + } + + public String inheritedMethod(int i) { + return "base " + i; + } + + @Override + public void changeValues() { + } +} diff --git a/tools/data-binding/integration-tests/TestApp/app/src/main/java/android/databinding/testapp/vo/FrameLayoutBindingObject.java b/tools/data-binding/integration-tests/TestApp/app/src/main/java/android/databinding/testapp/vo/FrameLayoutBindingObject.java new file mode 100644 index 0000000..a0fa6b7 --- /dev/null +++ b/tools/data-binding/integration-tests/TestApp/app/src/main/java/android/databinding/testapp/vo/FrameLayoutBindingObject.java @@ -0,0 +1,32 @@ +/* + * 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 android.databinding.testapp.vo; + +import android.databinding.Bindable; + +public class FrameLayoutBindingObject extends BindingAdapterBindingObject { + @Bindable + private int foregroundTint; + + public int getForegroundTint() { + return foregroundTint; + } + + public void changeValues() { + foregroundTint = 0xFF111111; + notifyChange(); + } +} diff --git a/tools/data-binding/integration-tests/TestApp/app/src/main/java/android/databinding/testapp/vo/ImageViewBindingObject.java b/tools/data-binding/integration-tests/TestApp/app/src/main/java/android/databinding/testapp/vo/ImageViewBindingObject.java new file mode 100644 index 0000000..926617e --- /dev/null +++ b/tools/data-binding/integration-tests/TestApp/app/src/main/java/android/databinding/testapp/vo/ImageViewBindingObject.java @@ -0,0 +1,51 @@ +/* + * 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 android.databinding.testapp.vo; + +import android.databinding.Bindable; +import android.graphics.PorterDuff; +import android.graphics.drawable.ColorDrawable; +import android.graphics.drawable.Drawable; + +public class ImageViewBindingObject extends BindingAdapterBindingObject { + @Bindable + private int mTint; + + @Bindable + private Drawable mSrc; + + @Bindable + private PorterDuff.Mode mTintMode = PorterDuff.Mode.DARKEN; + + public int getTint() { + return mTint; + } + + public Drawable getSrc() { + return mSrc; + } + + public PorterDuff.Mode getTintMode() { + return mTintMode; + } + + public void changeValues() { + mTint = 0xFF111111; + mSrc = new ColorDrawable(0xFF00FF00); + mTintMode = PorterDuff.Mode.LIGHTEN; + notifyChange(); + } +} diff --git a/tools/data-binding/integration-tests/TestApp/app/src/main/java/android/databinding/testapp/vo/LinearLayoutBindingObject.java b/tools/data-binding/integration-tests/TestApp/app/src/main/java/android/databinding/testapp/vo/LinearLayoutBindingObject.java new file mode 100644 index 0000000..f9e07c3 --- /dev/null +++ b/tools/data-binding/integration-tests/TestApp/app/src/main/java/android/databinding/testapp/vo/LinearLayoutBindingObject.java @@ -0,0 +1,40 @@ +/* + * 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 android.databinding.testapp.vo; + +import android.databinding.Bindable; + +public class LinearLayoutBindingObject extends BindingAdapterBindingObject { + @Bindable + private int mDivider; + + @Bindable + private boolean mMeasureWithLargestChild; + + public int getDivider() { + return mDivider; + } + + public boolean isMeasureWithLargestChild() { + return mMeasureWithLargestChild; + } + + public void changeValues() { + mDivider = 0xFF111111; + mMeasureWithLargestChild = true; + notifyChange(); + } +} diff --git a/tools/data-binding/integration-tests/TestApp/app/src/main/java/android/databinding/testapp/vo/NotBindableVo.java b/tools/data-binding/integration-tests/TestApp/app/src/main/java/android/databinding/testapp/vo/NotBindableVo.java new file mode 100644 index 0000000..64d1a48 --- /dev/null +++ b/tools/data-binding/integration-tests/TestApp/app/src/main/java/android/databinding/testapp/vo/NotBindableVo.java @@ -0,0 +1,88 @@ +/* + * Copyright (C) 2015 The Android Open Source Project + * Licensed under the Apache License, Version 2.0 (the "License"); + * you may not use this file except in compliance with the License. + * You may obtain a copy of the License at + * http://www.apache.org/licenses/LICENSE-2.0 + * Unless required by applicable law or agreed to in writing, software + * distributed under the License is distributed on an "AS IS" BASIS, + * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. + * See the License for the specific language governing permissions and + * limitations under the License. + */ + +package android.databinding.testapp.vo; + +public class NotBindableVo { + private int mIntValue; + private int mIntValueGetCount; + private boolean mBoolValue; + private int mBoolValueGetCount; + private String mStringValue; + private int mStringValueGetCount; + private final String mFinalString = "this has final content"; + public final int publicField = 3; + + public NotBindableVo() { + } + + public NotBindableVo(int intValue) { + this.mIntValue = intValue; + } + + public NotBindableVo(String stringValue) { + this.mStringValue = stringValue; + } + + public NotBindableVo(int intValue, String stringValue) { + this.mIntValue = intValue; + this.mStringValue = stringValue; + } + + public int getIntValue() { + mIntValueGetCount ++; + return mIntValue; + } + + public String getFinalString() { + return mFinalString; + } + + public void setIntValue(int intValue) { + this.mIntValue = intValue; + } + + public String getStringValue() { + mStringValueGetCount ++; + return mStringValue; + } + + public void setStringValue(String stringValue) { + this.mStringValue = stringValue; + } + + public String mergeStringFields(NotBindableVo other) { + return mStringValue + (other == null ? "" : other.mStringValue); + } + + public boolean getBoolValue() { + mBoolValueGetCount ++; + return mBoolValue; + } + + public void setBoolValue(boolean boolValue) { + mBoolValue = boolValue; + } + + public int getIntValueGetCount() { + return mIntValueGetCount; + } + + public int getBoolValueGetCount() { + return mBoolValueGetCount; + } + + public int getStringValueGetCount() { + return mStringValueGetCount; + } +} diff --git a/tools/data-binding/integration-tests/TestApp/app/src/main/java/android/databinding/testapp/vo/ObservableFieldBindingObject.java b/tools/data-binding/integration-tests/TestApp/app/src/main/java/android/databinding/testapp/vo/ObservableFieldBindingObject.java new file mode 100644 index 0000000..87127fe --- /dev/null +++ b/tools/data-binding/integration-tests/TestApp/app/src/main/java/android/databinding/testapp/vo/ObservableFieldBindingObject.java @@ -0,0 +1,42 @@ +/* + * 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 android.databinding.testapp.vo; + +import android.databinding.ObservableBoolean; +import android.databinding.ObservableByte; +import android.databinding.ObservableChar; +import android.databinding.ObservableDouble; +import android.databinding.ObservableField; +import android.databinding.ObservableFloat; +import android.databinding.ObservableInt; +import android.databinding.ObservableLong; +import android.databinding.ObservableShort; + +public class ObservableFieldBindingObject { + public final ObservableBoolean bField = new ObservableBoolean(); + public final ObservableByte tField = new ObservableByte(); + public final ObservableShort sField = new ObservableShort(); + public final ObservableChar cField = new ObservableChar(); + public final ObservableInt iField = new ObservableInt(); + public final ObservableLong lField = new ObservableLong(); + public final ObservableFloat fField = new ObservableFloat(); + public final ObservableDouble dField = new ObservableDouble(); + public final ObservableField<String> oField = new ObservableField<>(); + + public ObservableFieldBindingObject() { + oField.set("Hello"); + } +} diff --git a/tools/data-binding/integration-tests/TestApp/app/src/main/java/android/databinding/testapp/vo/ObservableWithNotBindableFieldObject.java b/tools/data-binding/integration-tests/TestApp/app/src/main/java/android/databinding/testapp/vo/ObservableWithNotBindableFieldObject.java new file mode 100644 index 0000000..ba33539 --- /dev/null +++ b/tools/data-binding/integration-tests/TestApp/app/src/main/java/android/databinding/testapp/vo/ObservableWithNotBindableFieldObject.java @@ -0,0 +1,28 @@ +/* + * 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 android.databinding.testapp.vo; + +import android.databinding.BaseObservable; + +public class ObservableWithNotBindableFieldObject extends BaseObservable { + private String data; + public void update(String data) { + this.data = data; + notifyChange(); + } + + public String getData() { + return data; + } +} diff --git a/tools/data-binding/integration-tests/TestApp/app/src/main/java/android/databinding/testapp/vo/ProgressBarBindingObject.java b/tools/data-binding/integration-tests/TestApp/app/src/main/java/android/databinding/testapp/vo/ProgressBarBindingObject.java new file mode 100644 index 0000000..8a18aba --- /dev/null +++ b/tools/data-binding/integration-tests/TestApp/app/src/main/java/android/databinding/testapp/vo/ProgressBarBindingObject.java @@ -0,0 +1,48 @@ +/* + * 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 android.databinding.testapp.vo; + +import android.databinding.Bindable; + +public class ProgressBarBindingObject extends BindingAdapterBindingObject { + @Bindable + private int mIndeterminateTint; + + @Bindable + private int mProgressTint; + + @Bindable + private int mSecondaryProgressTint; + + public int getIndeterminateTint() { + return mIndeterminateTint; + } + + public int getProgressTint() { + return mProgressTint; + } + + public int getSecondaryProgressTint() { + return mSecondaryProgressTint; + } + + public void changeValues() { + mIndeterminateTint = 0xFF111111; + mProgressTint = 0xFF222222; + mSecondaryProgressTint = 0xFF333333; + notifyChange(); + } +} diff --git a/tools/data-binding/integration-tests/TestApp/app/src/main/java/android/databinding/testapp/vo/PublicFinalTestVo.java b/tools/data-binding/integration-tests/TestApp/app/src/main/java/android/databinding/testapp/vo/PublicFinalTestVo.java new file mode 100644 index 0000000..03135e3 --- /dev/null +++ b/tools/data-binding/integration-tests/TestApp/app/src/main/java/android/databinding/testapp/vo/PublicFinalTestVo.java @@ -0,0 +1,21 @@ +/* + * 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 android.databinding.testapp.vo; + +public class PublicFinalTestVo { + public final int myField; + public PublicFinalTestVo(int field) { + myField = field; + } +} diff --git a/tools/data-binding/integration-tests/TestApp/app/src/main/java/android/databinding/testapp/vo/PublicFinalWithObservableTestVo.java b/tools/data-binding/integration-tests/TestApp/app/src/main/java/android/databinding/testapp/vo/PublicFinalWithObservableTestVo.java new file mode 100644 index 0000000..8d6f431 --- /dev/null +++ b/tools/data-binding/integration-tests/TestApp/app/src/main/java/android/databinding/testapp/vo/PublicFinalWithObservableTestVo.java @@ -0,0 +1,42 @@ +/* + * 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 android.databinding.testapp.vo; + +import android.databinding.BaseObservable; +import android.databinding.Bindable; +import android.databinding.testapp.BR; +import android.databinding.testapp.R; + +public class PublicFinalWithObservableTestVo { + public final int myField; + public final MyVo myFinalVo = new MyVo(); + + public PublicFinalWithObservableTestVo(int field) { + myField = field; + } + + public static class MyVo extends BaseObservable { + @Bindable + private int val = R.string.app_name; + + public int getVal() { + return val; + } + + public void setVal(int val) { + this.val = val; + notifyPropertyChanged(BR.val); + } + } +} diff --git a/tools/data-binding/integration-tests/TestApp/app/src/main/java/android/databinding/testapp/vo/RadioGroupBindingObject.java b/tools/data-binding/integration-tests/TestApp/app/src/main/java/android/databinding/testapp/vo/RadioGroupBindingObject.java new file mode 100644 index 0000000..ceacd20 --- /dev/null +++ b/tools/data-binding/integration-tests/TestApp/app/src/main/java/android/databinding/testapp/vo/RadioGroupBindingObject.java @@ -0,0 +1,33 @@ +/* + * 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 android.databinding.testapp.vo; + +import android.databinding.Bindable; +import android.databinding.testapp.R; + +public class RadioGroupBindingObject extends BindingAdapterBindingObject { + @Bindable + private int mCheckedButton = R.id.choiceOne; + + public int getCheckedButton() { + return mCheckedButton; + } + + public void changeValues() { + mCheckedButton = R.id.choiceTwo; + notifyChange(); + } +} diff --git a/tools/data-binding/integration-tests/TestApp/app/src/main/java/android/databinding/testapp/vo/SpinnerBindingObject.java b/tools/data-binding/integration-tests/TestApp/app/src/main/java/android/databinding/testapp/vo/SpinnerBindingObject.java new file mode 100644 index 0000000..c2ac72f --- /dev/null +++ b/tools/data-binding/integration-tests/TestApp/app/src/main/java/android/databinding/testapp/vo/SpinnerBindingObject.java @@ -0,0 +1,32 @@ +/* + * 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 android.databinding.testapp.vo; + +import android.databinding.Bindable; + +public class SpinnerBindingObject extends BindingAdapterBindingObject { + @Bindable + private int mPopupBackground = 0xFF123456; + + public int getPopupBackground() { + return mPopupBackground; + } + + public void changeValues() { + mPopupBackground = 0xFF111111; + notifyChange(); + } +} diff --git a/tools/data-binding/integration-tests/TestApp/app/src/main/java/android/databinding/testapp/vo/SwitchBindingObject.java b/tools/data-binding/integration-tests/TestApp/app/src/main/java/android/databinding/testapp/vo/SwitchBindingObject.java new file mode 100644 index 0000000..86ea227 --- /dev/null +++ b/tools/data-binding/integration-tests/TestApp/app/src/main/java/android/databinding/testapp/vo/SwitchBindingObject.java @@ -0,0 +1,39 @@ +/* + * 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 android.databinding.testapp.vo; + +import android.databinding.Bindable; + +public class SwitchBindingObject extends BindingAdapterBindingObject { + @Bindable + private int mThumb; + @Bindable + private int mTrack; + + public int getThumb() { + return mThumb; + } + + public int getTrack() { + return mTrack; + } + + public void changeValues() { + mThumb = 0xFF111111; + mTrack = 0xFF333333; + notifyChange(); + } +} diff --git a/tools/data-binding/integration-tests/TestApp/app/src/main/java/android/databinding/testapp/vo/TabWidgetBindingObject.java b/tools/data-binding/integration-tests/TestApp/app/src/main/java/android/databinding/testapp/vo/TabWidgetBindingObject.java new file mode 100644 index 0000000..2ce8681 --- /dev/null +++ b/tools/data-binding/integration-tests/TestApp/app/src/main/java/android/databinding/testapp/vo/TabWidgetBindingObject.java @@ -0,0 +1,54 @@ +/* + * 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 android.databinding.testapp.vo; + +import android.databinding.Bindable; +import android.graphics.drawable.ColorDrawable; + +public class TabWidgetBindingObject extends BindingAdapterBindingObject { + @Bindable + private ColorDrawable mDivider = new ColorDrawable(0xFF0000FF); + @Bindable + private boolean mTabStripEnabled; + @Bindable + private ColorDrawable mTabStripLeft = new ColorDrawable(0xFF00FF00); + @Bindable + private ColorDrawable mTabStripRight = new ColorDrawable(0xFFFF0000); + + public ColorDrawable getDivider() { + return mDivider; + } + + public ColorDrawable getTabStripLeft() { + return mTabStripLeft; + } + + public ColorDrawable getTabStripRight() { + return mTabStripRight; + } + + public boolean isTabStripEnabled() { + return mTabStripEnabled; + } + + public void changeValues() { + mDivider = new ColorDrawable(0xFF111111); + mTabStripEnabled = true; + mTabStripLeft = new ColorDrawable(0xFF222222); + mTabStripRight = new ColorDrawable(0xFF333333); + notifyChange(); + } +} diff --git a/tools/data-binding/integration-tests/TestApp/app/src/main/java/android/databinding/testapp/vo/TableLayoutBindingObject.java b/tools/data-binding/integration-tests/TestApp/app/src/main/java/android/databinding/testapp/vo/TableLayoutBindingObject.java new file mode 100644 index 0000000..34f7e8d --- /dev/null +++ b/tools/data-binding/integration-tests/TestApp/app/src/main/java/android/databinding/testapp/vo/TableLayoutBindingObject.java @@ -0,0 +1,53 @@ +/* + * 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 android.databinding.testapp.vo; + +import android.databinding.Bindable; + +public class TableLayoutBindingObject extends BindingAdapterBindingObject { + @Bindable + private String mCollapseColumns = "1"; + @Bindable + private String mShrinkColumns = "1"; + @Bindable + private String mStretchColumns = "1"; + @Bindable + private int mDivider = 0xFF112233; + + public String getCollapseColumns() { + return mCollapseColumns; + } + + public String getShrinkColumns() { + return mShrinkColumns; + } + + public String getStretchColumns() { + return mStretchColumns; + } + + public int getDivider() { + return mDivider; + } + + public void changeValues() { + mCollapseColumns = ""; + mShrinkColumns = "1,0"; + mStretchColumns = "*"; + mDivider = 0xFF445566; + notifyChange(); + } +} diff --git a/tools/data-binding/integration-tests/TestApp/app/src/main/java/android/databinding/testapp/vo/TextViewBindingObject.java b/tools/data-binding/integration-tests/TestApp/app/src/main/java/android/databinding/testapp/vo/TextViewBindingObject.java new file mode 100644 index 0000000..b1d04b6 --- /dev/null +++ b/tools/data-binding/integration-tests/TestApp/app/src/main/java/android/databinding/testapp/vo/TextViewBindingObject.java @@ -0,0 +1,320 @@ +/* + * 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 android.databinding.testapp.vo; + +import android.databinding.Bindable; +import android.databinding.adapters.TextViewBindingAdapter; +import android.databinding.testapp.BR; +import android.text.Editable; +import android.text.InputType; +import android.text.method.KeyListener; +import android.text.method.TextKeyListener; +import android.text.util.Linkify; +import android.view.KeyEvent; +import android.view.View; +import android.widget.TextView; + +public class TextViewBindingObject extends BindingAdapterBindingObject { + + @Bindable + private int mAutoLink = Linkify.WEB_URLS; + + @Bindable + private int mDrawablePadding; + + @Bindable + private int mInputType = InputType.TYPE_CLASS_PHONE; + + @Bindable + private boolean mScrollHorizontally; + + @Bindable + private boolean mTextAllCaps; + + @Bindable + private int mTextColorHighlight; + + @Bindable + private int mTextColorHint; + + @Bindable + private int mTextColorLink; + + @Bindable + private boolean mAutoText; + + @Bindable + private TextKeyListener.Capitalize mCapitalize = TextKeyListener.Capitalize.NONE; + + @Bindable + private TextView.BufferType mBufferType = TextView.BufferType.NORMAL; + + @Bindable + private String mDigits = "abcdefg"; + + @Bindable + private int mNumeric = TextViewBindingAdapter.DECIMAL; + + @Bindable + private boolean mPhoneNumber; + + @Bindable + private int mDrawableBottom; + + @Bindable + private int mDrawableTop; + + @Bindable + private int mDrawableLeft; + + @Bindable + private int mDrawableRight; + + @Bindable + private int mDrawableStart; + + @Bindable + private int mDrawableEnd; + + @Bindable + private String mImeActionLabel; + + @Bindable + private int mImeActionId; + + @Bindable + private String mInputMethod + = "android.databinding.testapp.vo.TextViewBindingObject$KeyListener1"; + + @Bindable + private float mLineSpacingExtra; + + @Bindable + private float mLineSpacingMultiplier; + + @Bindable + private int mMaxLength; + + @Bindable + private int mShadowColor; + + @Bindable + private float mShadowDx; + + @Bindable + private float mShadowDy; + + @Bindable + private float mShadowRadius; + + @Bindable + private float mTextSize = 10f; + + public TextView.BufferType getBufferType() { + return mBufferType; + } + + public float getLineSpacingExtra() { + return mLineSpacingExtra; + } + + public float getLineSpacingMultiplier() { + return mLineSpacingMultiplier; + } + + public float getShadowDx() { + return mShadowDx; + } + + public float getShadowDy() { + return mShadowDy; + } + + public float getShadowRadius() { + return mShadowRadius; + } + + public float getTextSize() { + return mTextSize; + } + + public int getAutoLink() { + return mAutoLink; + } + + public int getDrawableBottom() { + return mDrawableBottom; + } + + public int getDrawableEnd() { + return mDrawableEnd; + } + + public int getDrawableLeft() { + return mDrawableLeft; + } + + public int getDrawablePadding() { + return mDrawablePadding; + } + + public int getDrawableRight() { + return mDrawableRight; + } + + public int getDrawableStart() { + return mDrawableStart; + } + + public int getDrawableTop() { + return mDrawableTop; + } + + public int getImeActionId() { + return mImeActionId; + } + + public int getInputType() { + return mInputType; + } + + public int getMaxLength() { + return mMaxLength; + } + + public int getNumeric() { + return mNumeric; + } + + public int getShadowColor() { + return mShadowColor; + } + + public int getTextColorHighlight() { + return mTextColorHighlight; + } + + public int getTextColorHint() { + return mTextColorHint; + } + + public int getTextColorLink() { + return mTextColorLink; + } + + public String getDigits() { + return mDigits; + } + + public String getImeActionLabel() { + return mImeActionLabel; + } + + public String getInputMethod() { + return mInputMethod; + } + + public boolean isAutoText() { + return mAutoText; + } + + public TextKeyListener.Capitalize getCapitalize() { + return mCapitalize; + } + + public void setCapitalize(TextKeyListener.Capitalize capitalize) { + mCapitalize = capitalize; + notifyPropertyChanged(BR.capitalize); + } + + public boolean isPhoneNumber() { + return mPhoneNumber; + } + + public boolean isScrollHorizontally() { + return mScrollHorizontally; + } + + public boolean isTextAllCaps() { + return mTextAllCaps; + } + + public void changeValues() { + mAutoLink = Linkify.EMAIL_ADDRESSES; + mDrawablePadding = 10; + mInputType = InputType.TYPE_CLASS_TEXT | InputType.TYPE_TEXT_FLAG_CAP_WORDS; + mScrollHorizontally = true; + mTextAllCaps = true; + mTextColorHighlight = 0xFF00FF00; + mTextColorHint = 0xFFFF0000; + mTextColorLink = 0xFF0000FF; + mAutoText = true; + mCapitalize = TextKeyListener.Capitalize.SENTENCES; + mBufferType = TextView.BufferType.SPANNABLE; + mDigits = "hijklmno"; + mNumeric = TextViewBindingAdapter.SIGNED; + mPhoneNumber = true; + mDrawableBottom = 0xFF880088; + mDrawableTop = 0xFF111111; + mDrawableLeft = 0xFF222222; + mDrawableRight = 0xFF333333; + mDrawableStart = 0xFF444444; + mDrawableEnd = 0xFF555555; + mImeActionLabel = "Hello World"; + mImeActionId = 3; + mInputMethod = "android.databinding.testapp.vo.TextViewBindingObject$KeyListener2"; + mLineSpacingExtra = 2; + mLineSpacingMultiplier = 3; + mMaxLength = 100; + mShadowColor = 0xFF666666; + mShadowDx = 2; + mShadowDy = 3; + mShadowRadius = 4; + mTextSize = 20f; + notifyChange(); + } + + public static class KeyListener1 implements KeyListener { + + @Override + public int getInputType() { + return InputType.TYPE_CLASS_TEXT; + } + + @Override + public boolean onKeyDown(View view, Editable text, int keyCode, KeyEvent event) { + return false; + } + + @Override + public boolean onKeyUp(View view, Editable text, int keyCode, KeyEvent event) { + return false; + } + + @Override + public boolean onKeyOther(View view, Editable text, KeyEvent event) { + return false; + } + + @Override + public void clearMetaKeyState(View view, Editable content, int states) { + } + } + + public static class KeyListener2 extends KeyListener1 { + + } +} diff --git a/tools/data-binding/integration-tests/TestApp/app/src/main/java/android/databinding/testapp/vo/User.java b/tools/data-binding/integration-tests/TestApp/app/src/main/java/android/databinding/testapp/vo/User.java new file mode 100644 index 0000000..1107265 --- /dev/null +++ b/tools/data-binding/integration-tests/TestApp/app/src/main/java/android/databinding/testapp/vo/User.java @@ -0,0 +1,36 @@ +package android.databinding.testapp.vo; + +import android.databinding.Bindable; + +public class User { + @Bindable + private User friend; + @Bindable + private String name; + @Bindable + private String fullName; + + public User getFriend() { + return friend; + } + + public void setFriend(User friend) { + this.friend = friend; + } + + public String getName() { + return name; + } + + public void setName(String name) { + this.name = name; + } + + public String getFullName() { + return fullName; + } + + public void setFullName(String fullName) { + this.fullName = fullName; + } +} diff --git a/tools/data-binding/integration-tests/TestApp/app/src/main/java/android/databinding/testapp/vo/ViewBindingObject.java b/tools/data-binding/integration-tests/TestApp/app/src/main/java/android/databinding/testapp/vo/ViewBindingObject.java new file mode 100644 index 0000000..a955e81 --- /dev/null +++ b/tools/data-binding/integration-tests/TestApp/app/src/main/java/android/databinding/testapp/vo/ViewBindingObject.java @@ -0,0 +1,175 @@ +/* + * 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 android.databinding.testapp.vo; + +import android.databinding.Bindable; +import android.databinding.adapters.ViewBindingAdapter; +import android.databinding.testapp.R; +import android.view.View; + +public class ViewBindingObject extends BindingAdapterBindingObject { + @Bindable + private int mBackgroundTint = 0xFF00FF00; + @Bindable + private boolean mFadeScrollbars = false; + @Bindable + private int mNextFocusForward = R.id.padding; + @Bindable + private int mNextFocusLeft = R.id.paddingStartEnd; + @Bindable + private int mNextFocusRight = R.id.paddingTopBottom; + @Bindable + private int mNextFocusUp = R.id.backgroundTint; + @Bindable + private int mNextFocusDown = R.id.fadeScrollbars; + @Bindable + private int mRequiresFadingEdge = ViewBindingAdapter.FADING_EDGE_VERTICAL; + @Bindable + private int mScrollbarDefaultDelayBeforeFade = 300; + @Bindable + private int mScrollbarFadeDuration = 400; + @Bindable + private int mScrollbarSize = 10; + @Bindable + private int mScrollbarStyle = View.SCROLLBARS_INSIDE_OVERLAY; + @Bindable + private float mTransformPivotX = 9; + @Bindable + private float mTransformPivotY = 8; + @Bindable + private int mPadding = 11; + @Bindable + private int mPaddingBottom = 12; + @Bindable + private int mPaddingTop = 13; + @Bindable + private int mPaddingLeft = 14; + @Bindable + private int mPaddingRight = 15; + @Bindable + private int mPaddingStart = 16; + @Bindable + private int mPaddingEnd = 17; + + public int getBackgroundTint() { + return mBackgroundTint; + } + + public int getScrollbarFadeDuration() { + return mScrollbarFadeDuration; + } + + public boolean getFadeScrollbars() { + return mFadeScrollbars; + } + + public int getNextFocusDown() { + return mNextFocusDown; + } + + public int getNextFocusForward() { + return mNextFocusForward; + } + + public int getNextFocusLeft() { + return mNextFocusLeft; + } + + public int getNextFocusRight() { + return mNextFocusRight; + } + + public int getNextFocusUp() { + return mNextFocusUp; + } + + public int getRequiresFadingEdge() { + return mRequiresFadingEdge; + } + + public int getScrollbarDefaultDelayBeforeFade() { + return mScrollbarDefaultDelayBeforeFade; + } + + public int getScrollbarSize() { + return mScrollbarSize; + } + + public int getScrollbarStyle() { + return mScrollbarStyle; + } + + public float getTransformPivotX() { + return mTransformPivotX; + } + + public float getTransformPivotY() { + return mTransformPivotY; + } + + public int getPadding() { + return mPadding; + } + + public int getPaddingBottom() { + return mPaddingBottom; + } + + public int getPaddingEnd() { + return mPaddingEnd; + } + + public int getPaddingLeft() { + return mPaddingLeft; + } + + public int getPaddingRight() { + return mPaddingRight; + } + + public int getPaddingStart() { + return mPaddingStart; + } + + public int getPaddingTop() { + return mPaddingTop; + } + + public void changeValues() { + mBackgroundTint = 0xFFFF0000; + mFadeScrollbars = true; + mNextFocusForward = R.id.paddingStartEnd; + mNextFocusLeft = R.id.paddingTopBottom; + mNextFocusRight = R.id.backgroundTint; + mNextFocusUp = R.id.fadeScrollbars; + mNextFocusDown = R.id.padding; + mRequiresFadingEdge = ViewBindingAdapter.FADING_EDGE_HORIZONTAL; + mScrollbarDefaultDelayBeforeFade = 400; + mScrollbarFadeDuration = 500; + mScrollbarSize = 11; + mScrollbarStyle = View.SCROLLBARS_INSIDE_INSET; + mTransformPivotX = 7; + mTransformPivotY = 6; + mPadding = 110; + mPaddingBottom = 120; + mPaddingTop = 130; + mPaddingLeft = 140; + mPaddingRight = 150; + mPaddingStart = 160; + mPaddingEnd = 170; + notifyChange(); + } +} diff --git a/tools/data-binding/integration-tests/TestApp/app/src/main/java/android/databinding/testapp/vo/ViewGroupBindingObject.java b/tools/data-binding/integration-tests/TestApp/app/src/main/java/android/databinding/testapp/vo/ViewGroupBindingObject.java new file mode 100644 index 0000000..107fec6 --- /dev/null +++ b/tools/data-binding/integration-tests/TestApp/app/src/main/java/android/databinding/testapp/vo/ViewGroupBindingObject.java @@ -0,0 +1,53 @@ +/* + * 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 android.databinding.testapp.vo; + +import android.databinding.Bindable; + +public class ViewGroupBindingObject extends BindingAdapterBindingObject { + @Bindable + private boolean mAlwaysDrawnWithCache; + @Bindable + private boolean mAnimationCache; + @Bindable + private boolean mSplitMotionEvents; + @Bindable + private boolean mAnimateLayoutChanges; + + public boolean isAlwaysDrawnWithCache() { + return mAlwaysDrawnWithCache; + } + + public boolean isAnimationCache() { + return mAnimationCache; + } + + public boolean isSplitMotionEvents() { + return mSplitMotionEvents; + } + + public boolean isAnimateLayoutChanges() { + return mAnimateLayoutChanges; + } + + public void changeValues() { + mAlwaysDrawnWithCache = true; + mAnimationCache = true; + mAnimateLayoutChanges = true; + mSplitMotionEvents = true; + notifyChange(); + } +} diff --git a/tools/data-binding/integration-tests/TestApp/app/src/main/java/android/databinding/testapp/vo/ViewStubBindingObject.java b/tools/data-binding/integration-tests/TestApp/app/src/main/java/android/databinding/testapp/vo/ViewStubBindingObject.java new file mode 100644 index 0000000..3e88e91 --- /dev/null +++ b/tools/data-binding/integration-tests/TestApp/app/src/main/java/android/databinding/testapp/vo/ViewStubBindingObject.java @@ -0,0 +1,33 @@ +/* + * 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 android.databinding.testapp.vo; + +import android.databinding.Bindable; +import android.databinding.testapp.R; + +public class ViewStubBindingObject extends BindingAdapterBindingObject { + @Bindable + private int mLayout = R.layout.table_layout_adapter_test; + + public int getLayout() { + return mLayout; + } + + public void changeValues() { + mLayout = R.layout.auto_complete_text_view_adapter_test; + notifyChange(); + } +} diff --git a/tools/data-binding/integration-tests/TestApp/app/src/main/res/drawable-hdpi/ic_launcher.png b/tools/data-binding/integration-tests/TestApp/app/src/main/res/drawable-hdpi/ic_launcher.png Binary files differnew file mode 100644 index 0000000..96a442e --- /dev/null +++ b/tools/data-binding/integration-tests/TestApp/app/src/main/res/drawable-hdpi/ic_launcher.png diff --git a/tools/data-binding/integration-tests/TestApp/app/src/main/res/drawable-mdpi/ic_launcher.png b/tools/data-binding/integration-tests/TestApp/app/src/main/res/drawable-mdpi/ic_launcher.png Binary files differnew file mode 100644 index 0000000..359047d --- /dev/null +++ b/tools/data-binding/integration-tests/TestApp/app/src/main/res/drawable-mdpi/ic_launcher.png diff --git a/tools/data-binding/integration-tests/TestApp/app/src/main/res/drawable-xhdpi/ic_launcher.png b/tools/data-binding/integration-tests/TestApp/app/src/main/res/drawable-xhdpi/ic_launcher.png Binary files differnew file mode 100644 index 0000000..71c6d76 --- /dev/null +++ b/tools/data-binding/integration-tests/TestApp/app/src/main/res/drawable-xhdpi/ic_launcher.png diff --git a/tools/data-binding/integration-tests/TestApp/app/src/main/res/drawable-xxhdpi/ic_launcher.png b/tools/data-binding/integration-tests/TestApp/app/src/main/res/drawable-xxhdpi/ic_launcher.png Binary files differnew file mode 100644 index 0000000..4df1894 --- /dev/null +++ b/tools/data-binding/integration-tests/TestApp/app/src/main/res/drawable-xxhdpi/ic_launcher.png diff --git a/tools/data-binding/integration-tests/TestApp/app/src/main/res/layout-land/multi_res_layout.xml b/tools/data-binding/integration-tests/TestApp/app/src/main/res/layout-land/multi_res_layout.xml new file mode 100644 index 0000000..594c3fe --- /dev/null +++ b/tools/data-binding/integration-tests/TestApp/app/src/main/res/layout-land/multi_res_layout.xml @@ -0,0 +1,40 @@ +<?xml version="1.0" encoding="utf-8"?> +<!-- + ~ 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. + --> + +<LinearLayout xmlns:android="http://schemas.android.com/apk/res/android" + xmlns:bind="http://schemas.android.com/apk/res-auto" + android:orientation="vertical" + android:layout_width="match_parent" + android:layout_height="match_parent"> + <variable name="objectInLand" type="android.databinding.testapp.vo.NotBindableVo"/> + <TextView android:layout_width="wrap_content" android:layout_height="wrap_content" + android:id="@+id/objectInLandTextView" + android:text="@{objectInLand.stringValue}"/> + <TextView android:layout_width="wrap_content" android:layout_height="wrap_content" + android:id="@+id/objectInDefaultTextView" + android:text="@{objectInDefault.stringValue}"/> + <TextView android:layout_width="wrap_content" android:layout_height="wrap_content" + android:id="@+id/objectInDefaultTextView2" + android:text="@{objectInDefault.stringValue}"/> + + <include layout="@layout/included_layout" android:id="@+id/includedLayoutConflict" + bind:innerObject="@{objectInLand}" + bind:innerValue='@{"modified " + objectInLand.intValue}' /> + <include layout="@layout/basic_binding" android:id="@+id/includedLayoutShared" + bind:a="@{objectInDefault.stringValue}" + /> + <include layout="@layout/conditional_binding" android:id="@+id/includedLayoutLand" + bind:obj2="@{objectInDefault}" + /> +</LinearLayout> diff --git a/tools/data-binding/integration-tests/TestApp/app/src/main/res/layout/abs_list_view_adapter_test.xml b/tools/data-binding/integration-tests/TestApp/app/src/main/res/layout/abs_list_view_adapter_test.xml new file mode 100644 index 0000000..4ff0ba8 --- /dev/null +++ b/tools/data-binding/integration-tests/TestApp/app/src/main/res/layout/abs_list_view_adapter_test.xml @@ -0,0 +1,11 @@ +<?xml version="1.0" encoding="utf-8"?> +<ListView xmlns:android="http://schemas.android.com/apk/res/android" + android:id="@+id/view" + android:layout_width="match_parent" + android:layout_height="match_parent" + android:listSelector="@{obj.listSelector}" + android:scrollingCache="@{obj.scrollingCache}" + android:smoothScrollbar="@{obj.smoothScrollbar}" + > + <variable name="obj" type="android.databinding.testapp.vo.AbsListViewBindingObject"/> +</ListView> diff --git a/tools/data-binding/integration-tests/TestApp/app/src/main/res/layout/abs_seek_bar_adapter_test.xml b/tools/data-binding/integration-tests/TestApp/app/src/main/res/layout/abs_seek_bar_adapter_test.xml new file mode 100644 index 0000000..0fafb2d --- /dev/null +++ b/tools/data-binding/integration-tests/TestApp/app/src/main/res/layout/abs_seek_bar_adapter_test.xml @@ -0,0 +1,10 @@ +<?xml version="1.0" encoding="utf-8"?> +<LinearLayout xmlns:android="http://schemas.android.com/apk/res/android" + android:orientation="vertical" + android:layout_width="match_parent" + android:layout_height="match_parent"> + <variable name="obj" type="android.databinding.testapp.vo.AbsSeekBarBindingObject"/> + <SeekBar android:layout_width="match_parent" android:layout_height="match_parent" + android:id="@+id/view" + android:thumbTint="@{obj.thumbTint}"/> +</LinearLayout> diff --git a/tools/data-binding/integration-tests/TestApp/app/src/main/res/layout/abs_spinner_adapter_test.xml b/tools/data-binding/integration-tests/TestApp/app/src/main/res/layout/abs_spinner_adapter_test.xml new file mode 100644 index 0000000..047b107 --- /dev/null +++ b/tools/data-binding/integration-tests/TestApp/app/src/main/res/layout/abs_spinner_adapter_test.xml @@ -0,0 +1,10 @@ +<?xml version="1.0" encoding="utf-8"?> +<LinearLayout xmlns:android="http://schemas.android.com/apk/res/android" + android:orientation="vertical" + android:layout_width="match_parent" + android:layout_height="match_parent"> + <variable name="obj" type="android.databinding.testapp.vo.AbsSpinnerBindingObject"/> + <Spinner android:layout_width="match_parent" android:layout_height="match_parent" + android:id="@+id/view" + android:entries="@{obj.entries}"/> +</LinearLayout> diff --git a/tools/data-binding/integration-tests/TestApp/app/src/main/res/layout/auto_complete_text_view_adapter_test.xml b/tools/data-binding/integration-tests/TestApp/app/src/main/res/layout/auto_complete_text_view_adapter_test.xml new file mode 100644 index 0000000..b999841 --- /dev/null +++ b/tools/data-binding/integration-tests/TestApp/app/src/main/res/layout/auto_complete_text_view_adapter_test.xml @@ -0,0 +1,11 @@ +<?xml version="1.0" encoding="utf-8"?> +<LinearLayout xmlns:android="http://schemas.android.com/apk/res/android" + android:orientation="vertical" + android:layout_width="match_parent" + android:layout_height="match_parent"> + <variable name="obj" type="android.databinding.testapp.vo.AutoCompleteTextViewBindingObject"/> + <AutoCompleteTextView android:layout_width="match_parent" android:layout_height="match_parent" + android:id="@+id/view" + android:completionThreshold="@{obj.completionThreshold}" + android:popupBackground="@{obj.popupBackground}"/> +</LinearLayout> diff --git a/tools/data-binding/integration-tests/TestApp/app/src/main/res/layout/basic_binding.xml b/tools/data-binding/integration-tests/TestApp/app/src/main/res/layout/basic_binding.xml new file mode 100644 index 0000000..6e75fb2 --- /dev/null +++ b/tools/data-binding/integration-tests/TestApp/app/src/main/res/layout/basic_binding.xml @@ -0,0 +1,27 @@ +<?xml version="1.0" encoding="utf-8"?> +<!-- + ~ 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. + --> + +<LinearLayout xmlns:android="http://schemas.android.com/apk/res/android" + xmlns:bind="http://schemas.android.com/apk/res-auto" + android:orientation="vertical" + android:layout_width="match_parent" + android:layout_height="match_parent"> + <variable name="a" type="String"/> + <variable name="b" type="String"/> + <TextView + android:id="@+id/textView" + android:text="@{a + b}" + android:layout_width="wrap_content" + android:layout_height="wrap_content"/> +</LinearLayout> diff --git a/tools/data-binding/integration-tests/TestApp/app/src/main/res/layout/basic_dependant_binding.xml b/tools/data-binding/integration-tests/TestApp/app/src/main/res/layout/basic_dependant_binding.xml new file mode 100644 index 0000000..453348a --- /dev/null +++ b/tools/data-binding/integration-tests/TestApp/app/src/main/res/layout/basic_dependant_binding.xml @@ -0,0 +1,48 @@ +<?xml version="1.0" encoding="utf-8"?> +<!-- + ~ 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. + --> + +<LinearLayout xmlns:android="http://schemas.android.com/apk/res/android" + xmlns:bind="http://schemas.android.com/apk/res/android" + android:orientation="vertical" + android:layout_width="match_parent" + android:layout_height="match_parent"> + <variable name="obj1" type="android.databinding.testapp.vo.NotBindableVo"/> + <variable name="obj2" type="android.databinding.testapp.vo.NotBindableVo"/> + <TextView + android:id="@+id/textView1" + android:text="@{obj1.stringValue}" + android:layout_width="wrap_content" + android:layout_height="wrap_content"/> + <TextView + android:id="@+id/textView2" + android:text="@{obj2.stringValue}" + android:layout_width="wrap_content" + android:layout_height="wrap_content"/> + <TextView + android:id="@+id/mergedTextView1" + android:text="@{obj1.mergeStringFields(obj2)}" + android:layout_width="wrap_content" + android:layout_height="wrap_content"/> + <TextView + android:id="@+id/mergedTextView2" + android:text="@{obj2.mergeStringFields(obj1)}" + android:layout_width="wrap_content" + android:layout_height="wrap_content"/> + + <TextView + android:id="@+id/rawStringMerge" + android:text="@{obj1.stringValue + obj2.stringValue}" + android:layout_width="wrap_content" + android:layout_height="wrap_content"/> +</LinearLayout> diff --git a/tools/data-binding/integration-tests/TestApp/app/src/main/res/layout/bind_to_final.xml b/tools/data-binding/integration-tests/TestApp/app/src/main/res/layout/bind_to_final.xml new file mode 100644 index 0000000..f578585 --- /dev/null +++ b/tools/data-binding/integration-tests/TestApp/app/src/main/res/layout/bind_to_final.xml @@ -0,0 +1,23 @@ +<?xml version="1.0" encoding="utf-8"?> +<!-- + ~ 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. + --> + +<LinearLayout xmlns:android="http://schemas.android.com/apk/res/android" + android:orientation="vertical" + android:layout_width="match_parent" + android:layout_height="match_parent"> + <variable name="obj" type="android.databinding.testapp.vo.PublicFinalTestVo"/> + <TextView android:layout_width="wrap_content" android:layout_height="wrap_content" + android:id="@+id/text_view" + android:text="@{obj.myField}"/> +</LinearLayout> diff --git a/tools/data-binding/integration-tests/TestApp/app/src/main/res/layout/bind_to_final_observable.xml b/tools/data-binding/integration-tests/TestApp/app/src/main/res/layout/bind_to_final_observable.xml new file mode 100644 index 0000000..15d62fe --- /dev/null +++ b/tools/data-binding/integration-tests/TestApp/app/src/main/res/layout/bind_to_final_observable.xml @@ -0,0 +1,23 @@ +<?xml version="1.0" encoding="utf-8"?> +<!-- + ~ 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. + --> + +<LinearLayout xmlns:android="http://schemas.android.com/apk/res/android" + android:orientation="vertical" + android:layout_width="match_parent" + android:layout_height="match_parent"> + <variable name="obj" type="android.databinding.testapp.vo.PublicFinalWithObservableTestVo"/> + <TextView android:layout_width="wrap_content" android:layout_height="wrap_content" + android:id="@+id/text_view" + android:text="@{obj.myFinalVo.val}"/> +</LinearLayout> diff --git a/tools/data-binding/integration-tests/TestApp/app/src/main/res/layout/bracket_test.xml b/tools/data-binding/integration-tests/TestApp/app/src/main/res/layout/bracket_test.xml new file mode 100644 index 0000000..49c2872 --- /dev/null +++ b/tools/data-binding/integration-tests/TestApp/app/src/main/res/layout/bracket_test.xml @@ -0,0 +1,44 @@ +<?xml version="1.0" encoding="utf-8"?> +<LinearLayout xmlns:android="http://schemas.android.com/apk/res/android" + android:layout_width="match_parent" + android:layout_height="match_parent" + android:orientation="vertical" + > + <variable name="array" type="String[]"/> + <variable name="sparseArray" type="android.util.SparseArray<String>"/> + <variable name="sparseBooleanArray" type="android.util.SparseBooleanArray"/> + <variable name="sparseIntArray" type="android.util.SparseIntArray"/> + <variable name="sparseLongArray" type="android.util.SparseLongArray"/> + <variable name="longSparseArray" type="android.util.LongSparseArray<String>"/> + + <TextView android:layout_width="wrap_content" + android:layout_height="wrap_content" + android:id="@+id/arrayText" + android:text="@{array[0]}"/> + + <TextView android:layout_width="wrap_content" + android:layout_height="wrap_content" + android:id="@+id/sparseArrayText" + android:text='@{sparseArray[0]}'/> + + <TextView android:layout_width="wrap_content" + android:layout_height="wrap_content" + android:id="@+id/sparseBooleanArrayText" + android:text='@{"" + sparseBooleanArray[0]}'/> + + <TextView android:layout_width="wrap_content" + android:layout_height="wrap_content" + android:id="@+id/sparseIntArrayText" + android:text='@{"" + sparseIntArray[0]}'/> + + <TextView android:layout_width="wrap_content" + android:layout_height="wrap_content" + android:id="@+id/sparseLongArrayText" + android:text='@{"" + sparseLongArray[0]}'/> + + <TextView android:layout_width="wrap_content" + android:layout_height="wrap_content" + android:id="@+id/longSparseArrayText" + android:text='@{longSparseArray[0]}'/> + +</LinearLayout> diff --git a/tools/data-binding/integration-tests/TestApp/app/src/main/res/layout/cast_test.xml b/tools/data-binding/integration-tests/TestApp/app/src/main/res/layout/cast_test.xml new file mode 100644 index 0000000..803b5ee --- /dev/null +++ b/tools/data-binding/integration-tests/TestApp/app/src/main/res/layout/cast_test.xml @@ -0,0 +1,20 @@ +<?xml version="1.0" encoding="utf-8"?> +<LinearLayout xmlns:android="http://schemas.android.com/apk/res/android" + android:orientation="vertical" + android:layout_width="match_parent" + android:layout_height="match_parent"> + <import type="java.util.Collection"/> + <import type="java.util.ArrayList"/> + <import type="java.util.Map"/> + <variable name="list" type="Collection<String>"/> + <variable name="map" type="Object"/> + + <TextView + android:id="@+id/textView0" + android:layout_width="wrap_content" android:layout_height="wrap_content" + android:text="@{((ArrayList<String>)list)[0]}"/> + <TextView + android:id="@+id/textView1" + android:layout_width="wrap_content" android:layout_height="wrap_content" + android:text="@{((Map<String, String>)map)[`hello`]}"/> +</LinearLayout> diff --git a/tools/data-binding/integration-tests/TestApp/app/src/main/res/layout/checked_text_view_adapter_test.xml b/tools/data-binding/integration-tests/TestApp/app/src/main/res/layout/checked_text_view_adapter_test.xml new file mode 100644 index 0000000..02c50c8 --- /dev/null +++ b/tools/data-binding/integration-tests/TestApp/app/src/main/res/layout/checked_text_view_adapter_test.xml @@ -0,0 +1,13 @@ +<?xml version="1.0" encoding="utf-8"?> +<LinearLayout xmlns:android="http://schemas.android.com/apk/res/android" + android:orientation="vertical" + android:layout_width="match_parent" + android:layout_height="match_parent"> + <variable name="obj" type="android.databinding.testapp.vo.CheckedTextViewBindingObject"/> + <CheckedTextView + android:layout_width="match_parent" android:layout_height="match_parent" + android:id="@+id/view" + android:checkMark="@{obj.checkMark}" + android:checkMarkTint="@{obj.checkMarkTint}"/> + +</LinearLayout> diff --git a/tools/data-binding/integration-tests/TestApp/app/src/main/res/layout/compound_button_adapter_test.xml b/tools/data-binding/integration-tests/TestApp/app/src/main/res/layout/compound_button_adapter_test.xml new file mode 100644 index 0000000..6f61825 --- /dev/null +++ b/tools/data-binding/integration-tests/TestApp/app/src/main/res/layout/compound_button_adapter_test.xml @@ -0,0 +1,11 @@ +<?xml version="1.0" encoding="utf-8"?> +<LinearLayout xmlns:android="http://schemas.android.com/apk/res/android" + android:orientation="vertical" + android:layout_width="match_parent" + android:layout_height="match_parent"> + <variable name="obj" type="android.databinding.testapp.vo.CompoundButtonBindingObject"/> + <CheckBox + android:layout_width="match_parent" android:layout_height="match_parent" + android:id="@+id/view" + android:buttonTint="@{obj.buttonTint}"/> +</LinearLayout> diff --git a/tools/data-binding/integration-tests/TestApp/app/src/main/res/layout/conditional_binding.xml b/tools/data-binding/integration-tests/TestApp/app/src/main/res/layout/conditional_binding.xml new file mode 100644 index 0000000..70376a0 --- /dev/null +++ b/tools/data-binding/integration-tests/TestApp/app/src/main/res/layout/conditional_binding.xml @@ -0,0 +1,17 @@ +<?xml version="1.0" encoding="utf-8"?> +<LinearLayout xmlns:android="http://schemas.android.com/apk/res/android" + xmlns:bind="http://schemas.android.com/apk/res-auto" + android:orientation="vertical" + android:layout_width="match_parent" + android:layout_height="match_parent"> + <variable name="obj1" type="android.databinding.testapp.vo.NotBindableVo"/> + <variable name="obj2" type="android.databinding.testapp.vo.NotBindableVo"/> + <variable name="obj3" type="android.databinding.testapp.vo.NotBindableVo"/> + <variable name="cond1" type="boolean"/> + <variable name="cond2" type="boolean"/> + <TextView + android:id="@+id/textView" + android:text="@{cond1 ? cond2 ? obj1.stringValue : obj2.stringValue : obj3.stringValue}" + android:layout_width="wrap_content" + android:layout_height="wrap_content"/> +</LinearLayout> diff --git a/tools/data-binding/integration-tests/TestApp/app/src/main/res/layout/find_method_test.xml b/tools/data-binding/integration-tests/TestApp/app/src/main/res/layout/find_method_test.xml new file mode 100644 index 0000000..33a3746 --- /dev/null +++ b/tools/data-binding/integration-tests/TestApp/app/src/main/res/layout/find_method_test.xml @@ -0,0 +1,102 @@ +<?xml version="1.0" encoding="utf-8"?> +<LinearLayout xmlns:android="http://schemas.android.com/apk/res/android" + android:orientation="vertical" + android:layout_width="match_parent" + android:layout_height="match_parent"> + <variable name="obj" type="android.databinding.testapp.vo.FindMethodBindingObject"/> + <import type="android.databinding.testapp.vo.FindMethodBindingObject.Bar"/> + <variable name="obj2" type="Bar<String>"/> + <import type="android.databinding.testapp.vo.FindMethodBindingObject"/> + <import type="android.databinding.testapp.vo.FindMethodBindingObject" alias="FMBO"/> + <TextView + android:id="@+id/textView0" + android:layout_width="wrap_content" android:layout_height="wrap_content" + android:text="@{obj.method(1)}"/> + <TextView + android:id="@+id/textView1" + android:layout_width="wrap_content" android:layout_height="wrap_content" + android:text="@{obj.method(1.25f)}"/> + <TextView + android:id="@+id/textView2" + android:layout_width="wrap_content" android:layout_height="wrap_content" + android:text="@{obj.method(`hello`)}"/> + <TextView + android:id="@+id/textView3" + android:layout_width="wrap_content" android:layout_height="wrap_content" + android:text="@{obj.method((java.lang.Integer) 1)}"/> + <TextView + android:id="@+id/textView4" + android:layout_width="wrap_content" android:layout_height="wrap_content" + android:text="@{obj.inheritedMethod()}"/> + <TextView + android:id="@+id/textView5" + android:layout_width="wrap_content" android:layout_height="wrap_content" + android:text="@{obj.inheritedMethod(2)}"/> + <TextView + android:id="@+id/textView6" + android:layout_width="wrap_content" android:layout_height="wrap_content" + android:text="@{obj.method()}"/> + <TextView + android:id="@+id/textView7" + android:layout_width="wrap_content" android:layout_height="wrap_content" + android:text="@{android.databinding.testapp.vo.FindMethodBindingObject.staticMethod()}"/> + <TextView + android:id="@+id/textView8" + android:layout_width="wrap_content" android:layout_height="wrap_content" + android:text="@{android.databinding.testapp.vo.FindMethodBindingObject.foo.bar}"/> + <TextView + android:id="@+id/textView9" + android:layout_width="wrap_content" android:layout_height="wrap_content" + android:text="@{FindMethodBindingObject.staticMethod()}"/> + <TextView + android:id="@+id/textView10" + android:layout_width="wrap_content" android:layout_height="wrap_content" + android:text="@{FindMethodBindingObject.foo.bar}"/> + <TextView + android:id="@+id/textView11" + android:layout_width="wrap_content" android:layout_height="wrap_content" + android:text="@{FMBO.staticMethod()}"/> + <TextView + android:id="@+id/textView12" + android:layout_width="wrap_content" android:layout_height="wrap_content" + android:text="@{FMBO.foo.bar}"/> + <!-- The following are just to test duplicate expressions --> + <TextView + android:id="@+id/textView13" + android:layout_width="wrap_content" android:layout_height="wrap_content" + android:text="@{FMBO.staticMethod()}"/> + <TextView + android:id="@+id/textView14" + android:layout_width="wrap_content" android:layout_height="wrap_content" + android:text="@{FMBO.foo.bar}"/> + <!-- Imported classes --> + <TextView + android:id="@+id/textView15" + android:layout_width="wrap_content" android:layout_height="wrap_content" + android:text="@{obj2.method(`hello`)}"/> + <!-- confusing parameters may interfere with compile step --> + <TextView + android:id="@+id/textView16" + android:layout_width="wrap_content" android:layout_height="wrap_content" + android:text="@{obj.confusingPrimitive((Integer)1)}"/> + <TextView + android:id="@+id/textView17" + android:layout_width="wrap_content" android:layout_height="wrap_content" + android:text="@{`` + (obj.confusingPrimitive(2) / 2)}"/> + <TextView + android:id="@+id/textView18" + android:layout_width="wrap_content" android:layout_height="wrap_content" + android:text="@{obj.confusingInheritance(`hello`)}"/> + <TextView + android:id="@+id/textView19" + android:layout_width="wrap_content" android:layout_height="wrap_content" + android:text="@{obj.confusingTypeArgs(obj.getMap())}"/> + <TextView + android:id="@+id/textView20" + android:layout_width="wrap_content" android:layout_height="wrap_content" + android:text="@{obj.confusingParam(`hello`)}"/> + <TextView + android:id="@+id/textView21" + android:layout_width="wrap_content" android:layout_height="wrap_content" + android:text="@{obj.getList()[0]}"/> +</LinearLayout> diff --git a/tools/data-binding/integration-tests/TestApp/app/src/main/res/layout/frame_layout_adapter_test.xml b/tools/data-binding/integration-tests/TestApp/app/src/main/res/layout/frame_layout_adapter_test.xml new file mode 100644 index 0000000..7c9b860 --- /dev/null +++ b/tools/data-binding/integration-tests/TestApp/app/src/main/res/layout/frame_layout_adapter_test.xml @@ -0,0 +1,11 @@ +<?xml version="1.0" encoding="utf-8"?> +<LinearLayout xmlns:android="http://schemas.android.com/apk/res/android" + android:orientation="vertical" + android:layout_width="match_parent" + android:layout_height="match_parent"> + <variable name="obj" type="android.databinding.testapp.vo.FrameLayoutBindingObject"/> + <FrameLayout + android:layout_width="match_parent" android:layout_height="match_parent" + android:id="@+id/view" + android:foregroundTint="@{obj.foregroundTint}"/> +</LinearLayout> diff --git a/tools/data-binding/integration-tests/TestApp/app/src/main/res/layout/image_view_adapter_test.xml b/tools/data-binding/integration-tests/TestApp/app/src/main/res/layout/image_view_adapter_test.xml new file mode 100644 index 0000000..a3ce7f4 --- /dev/null +++ b/tools/data-binding/integration-tests/TestApp/app/src/main/res/layout/image_view_adapter_test.xml @@ -0,0 +1,14 @@ +<?xml version="1.0" encoding="utf-8"?> +<LinearLayout xmlns:android="http://schemas.android.com/apk/res/android" + android:orientation="vertical" + android:layout_width="match_parent" + android:layout_height="match_parent"> + <variable name="obj" type="android.databinding.testapp.vo.ImageViewBindingObject"/> + <ImageView + android:layout_width="match_parent" android:layout_height="match_parent" + android:id="@+id/view" + android:src="@{obj.src}" + android:tint="@{obj.tint}" + android:tintMode="@{obj.tintMode}" + /> +</LinearLayout> diff --git a/tools/data-binding/integration-tests/TestApp/app/src/main/res/layout/included_layout.xml b/tools/data-binding/integration-tests/TestApp/app/src/main/res/layout/included_layout.xml new file mode 100644 index 0000000..5b0bf4c --- /dev/null +++ b/tools/data-binding/integration-tests/TestApp/app/src/main/res/layout/included_layout.xml @@ -0,0 +1,26 @@ +<?xml version="1.0" encoding="utf-8"?> +<!-- + ~ 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. + --> + +<LinearLayout xmlns:android="http://schemas.android.com/apk/res/android" + xmlns:bind="http://schemas.android.com/apk/res-auto" + android:orientation="vertical" + android:layout_width="match_parent" + android:layout_height="match_parent"> + <variable name="innerObject" type="android.databinding.testapp.vo.NotBindableVo"/> + <variable name="innerValue" type="java.lang.String"/> + <TextView android:layout_width="wrap_content" android:layout_height="wrap_content" + android:id="@+id/innerTextView" + android:text="@{innerValue + innerObject.stringValue}" + /> +</LinearLayout> diff --git a/tools/data-binding/integration-tests/TestApp/app/src/main/res/layout/inner_cannot_read_dependency.xml b/tools/data-binding/integration-tests/TestApp/app/src/main/res/layout/inner_cannot_read_dependency.xml new file mode 100644 index 0000000..2ad1980 --- /dev/null +++ b/tools/data-binding/integration-tests/TestApp/app/src/main/res/layout/inner_cannot_read_dependency.xml @@ -0,0 +1,11 @@ +<?xml version="1.0" encoding="utf-8"?> +<LinearLayout xmlns:android="http://schemas.android.com/apk/res/android" + android:orientation="vertical" android:layout_width="match_parent" + android:layout_height="match_parent"> + <variable name="obj" type="android.databinding.testapp.vo.BasicObject"/> + <TextView + android:id="@+id/text_view" + android:text='@{obj.field1 + " " + (obj.field2 ?? "")}' + android:layout_width="wrap_content" + android:layout_height="wrap_content" /> +</LinearLayout>
\ No newline at end of file diff --git a/tools/data-binding/integration-tests/TestApp/app/src/main/res/layout/layout_with_include.xml b/tools/data-binding/integration-tests/TestApp/app/src/main/res/layout/layout_with_include.xml new file mode 100644 index 0000000..6504e4c --- /dev/null +++ b/tools/data-binding/integration-tests/TestApp/app/src/main/res/layout/layout_with_include.xml @@ -0,0 +1,30 @@ +<?xml version="1.0" encoding="utf-8"?> +<!-- + ~ 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. + --> + +<LinearLayout xmlns:android="http://schemas.android.com/apk/res/android" + xmlns:bind="http://schemas.android.com/apk/res-auto" + android:orientation="vertical" + android:layout_width="match_parent" + android:layout_height="match_parent"> + <variable name="outerObject" type="android.databinding.testapp.vo.NotBindableVo"/> + <TextView android:layout_width="wrap_content" android:layout_height="wrap_content" + android:id="@+id/outerTextView" + android:text="@{outerObject.stringValue}"/> + <!-- TODO test id collision--> + <include layout="@layout/included_layout" android:id="@+id/includedLayout" + bind:innerObject="@{outerObject}" + bind:innerValue="@{`modified ` + outerObject.intValue}" + /> + <include layout="@layout/plain_layout" android:id="@+id/plainLayout"/> +</LinearLayout> diff --git a/tools/data-binding/integration-tests/TestApp/app/src/main/res/layout/leak_test.xml b/tools/data-binding/integration-tests/TestApp/app/src/main/res/layout/leak_test.xml new file mode 100644 index 0000000..3dbf2f5 --- /dev/null +++ b/tools/data-binding/integration-tests/TestApp/app/src/main/res/layout/leak_test.xml @@ -0,0 +1,24 @@ +<?xml version="1.0" encoding="utf-8"?> +<!-- + ~ 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. + --> + +<LinearLayout xmlns:android="http://schemas.android.com/apk/res/android" + android:orientation="vertical" + android:layout_width="match_parent" + android:layout_height="match_parent"> + <variable name="name" type="String"/> + <TextView + android:id="@+id/textView" + android:layout_width="wrap_content" android:layout_height="wrap_content" + android:text="@{name}"/> +</LinearLayout>
\ No newline at end of file diff --git a/tools/data-binding/integration-tests/TestApp/app/src/main/res/layout/linear_layout_adapter_test.xml b/tools/data-binding/integration-tests/TestApp/app/src/main/res/layout/linear_layout_adapter_test.xml new file mode 100644 index 0000000..0b08f17 --- /dev/null +++ b/tools/data-binding/integration-tests/TestApp/app/src/main/res/layout/linear_layout_adapter_test.xml @@ -0,0 +1,14 @@ +<?xml version="1.0" encoding="utf-8"?> +<LinearLayout xmlns:android="http://schemas.android.com/apk/res/android" + android:orientation="vertical" + android:layout_width="match_parent" + android:layout_height="match_parent"> + <variable name="obj" type="android.databinding.testapp.vo.LinearLayoutBindingObject"/> + <LinearLayout + android:layout_width="match_parent" android:layout_height="match_parent" + android:id="@+id/view" + android:orientation="horizontal" + android:divider="@{obj.divider}" + android:measureWithLargestChild="@{obj.measureWithLargestChild}" + /> +</LinearLayout> diff --git a/tools/data-binding/integration-tests/TestApp/app/src/main/res/layout/multi_res_layout.xml b/tools/data-binding/integration-tests/TestApp/app/src/main/res/layout/multi_res_layout.xml new file mode 100644 index 0000000..99c7d81 --- /dev/null +++ b/tools/data-binding/integration-tests/TestApp/app/src/main/res/layout/multi_res_layout.xml @@ -0,0 +1,38 @@ +<?xml version="1.0" encoding="utf-8"?> +<!-- + ~ 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. + --> + +<LinearLayout xmlns:android="http://schemas.android.com/apk/res/android" + xmlns:bind="http://schemas.android.com/apk/res-auto" + android:orientation="vertical" + android:layout_width="match_parent" + android:layout_height="match_parent"> + <variable name="objectInDefault" type="android.databinding.testapp.vo.NotBindableVo"/> + <TextView android:layout_width="wrap_content" android:layout_height="wrap_content" + android:id="@+id/objectInDefaultTextView" + android:text="@{objectInDefault.stringValue}"/> + <EditText android:layout_width="wrap_content" android:layout_height="wrap_content" + android:id="@+id/objectInDefaultTextView2" + android:text="@{objectInDefault.stringValue}"/> + + <include layout="@layout/basic_binding" android:id="@+id/includedLayoutConflict" + bind:a="@{objectInDefault.stringValue}" + /> + <include layout="@layout/basic_binding" android:id="@+id/includedLayoutShared" + bind:a="@{objectInDefault.stringValue}" + /> + <include layout="@layout/conditional_binding" android:id="@+id/includedLayoutPort" + bind:cond1="@{objectInDefault == null}" + /> + +</LinearLayout> diff --git a/tools/data-binding/integration-tests/TestApp/app/src/main/res/layout/new_api_layout.xml b/tools/data-binding/integration-tests/TestApp/app/src/main/res/layout/new_api_layout.xml new file mode 100644 index 0000000..26c4e95 --- /dev/null +++ b/tools/data-binding/integration-tests/TestApp/app/src/main/res/layout/new_api_layout.xml @@ -0,0 +1,27 @@ +<?xml version="1.0" encoding="utf-8"?> +<!-- + ~ 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. + --> + +<LinearLayout xmlns:android="http://schemas.android.com/apk/res/android" + android:id="@+id/myContainer" + android:addChildrenForAccessibility="@{children}" + android:layout_width="match_parent" + android:layout_height="match_parent"> + <variable name="elevation" type="float"/> + <variable name="name" type="java.lang.String"/> + <variable name="children" type="java.util.ArrayList<android.view.View>"/> + <TextView android:layout_width="wrap_content" + android:layout_height="wrap_content" + android:id="@+id/textView" + android:text="@{name}" android:elevation="@{elevation}"/> +</LinearLayout> diff --git a/tools/data-binding/integration-tests/TestApp/app/src/main/res/layout/no_id_test.xml b/tools/data-binding/integration-tests/TestApp/app/src/main/res/layout/no_id_test.xml new file mode 100644 index 0000000..32f06f4 --- /dev/null +++ b/tools/data-binding/integration-tests/TestApp/app/src/main/res/layout/no_id_test.xml @@ -0,0 +1,21 @@ +<?xml version="1.0" encoding="utf-8"?> +<LinearLayout xmlns:android="http://schemas.android.com/apk/res/android" + android:layout_width="match_parent" + android:layout_height="match_parent" + android:orientation="@{orientation}" + > + <variable name="name" type="String"/> + <variable name="orientation" type="int"/> + <TextView android:layout_width="wrap_content" android:layout_height="wrap_content" + android:text="@{name}" android:tag="hello world"/> + <TextView android:layout_width="wrap_content" android:layout_height="wrap_content" + android:text="@{name}"/> + <TextView android:layout_width="wrap_content" android:layout_height="wrap_content" + android:text="@{name}" android:tag="@string/app_name"/> + <TextView android:layout_width="wrap_content" android:layout_height="wrap_content" + android:text="@{name}" android:tag="@android:string/ok"/> + <TextView android:id="@+id/textView" + android:layout_width="wrap_content" + android:layout_height="wrap_content" + android:text="hello"/> +</LinearLayout> diff --git a/tools/data-binding/integration-tests/TestApp/app/src/main/res/layout/observable_field_test.xml b/tools/data-binding/integration-tests/TestApp/app/src/main/res/layout/observable_field_test.xml new file mode 100644 index 0000000..1db044f --- /dev/null +++ b/tools/data-binding/integration-tests/TestApp/app/src/main/res/layout/observable_field_test.xml @@ -0,0 +1,52 @@ +<?xml version="1.0" encoding="utf-8"?> +<LinearLayout xmlns:android="http://schemas.android.com/apk/res/android" + android:orientation="vertical" + android:layout_width="match_parent" + android:layout_height="match_parent"> + <variable name="obj" type="android.databinding.testapp.vo.ObservableFieldBindingObject"/> + <TextView + android:id="@+id/bField" + android:layout_width="wrap_content" + android:layout_height="wrap_content" + android:text="@{`` + obj.bField}"/> + <TextView + android:id="@+id/tField" + android:layout_width="wrap_content" + android:layout_height="wrap_content" + android:text="@{`` + obj.tField}"/> + <TextView + android:id="@+id/sField" + android:layout_width="wrap_content" + android:layout_height="wrap_content" + android:text="@{`` + obj.sField}"/> + <TextView + android:id="@+id/cField" + android:layout_width="wrap_content" + android:layout_height="wrap_content" + android:text="@{`` + obj.cField}"/> + <TextView + android:id="@+id/iField" + android:layout_width="wrap_content" + android:layout_height="wrap_content" + android:text="@{`` + obj.iField}"/> + <TextView + android:id="@+id/lField" + android:layout_width="wrap_content" + android:layout_height="wrap_content" + android:text="@{`` + obj.lField}"/> + <TextView + android:id="@+id/fField" + android:layout_width="wrap_content" + android:layout_height="wrap_content" + android:text="@{`` + obj.fField}"/> + <TextView + android:id="@+id/dField" + android:layout_width="wrap_content" + android:layout_height="wrap_content" + android:text="@{`` + obj.dField}"/> + <TextView + android:id="@+id/oField" + android:layout_width="wrap_content" + android:layout_height="wrap_content" + android:text="@{obj.oField}"/> +</LinearLayout> diff --git a/tools/data-binding/integration-tests/TestApp/app/src/main/res/layout/observable_with_not_bindable_field.xml b/tools/data-binding/integration-tests/TestApp/app/src/main/res/layout/observable_with_not_bindable_field.xml new file mode 100644 index 0000000..bde2dd4 --- /dev/null +++ b/tools/data-binding/integration-tests/TestApp/app/src/main/res/layout/observable_with_not_bindable_field.xml @@ -0,0 +1,23 @@ +<?xml version="1.0" encoding="utf-8"?> +<!-- + ~ 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. + --> + +<LinearLayout xmlns:android="http://schemas.android.com/apk/res/android" + android:orientation="vertical" + android:layout_width="match_parent" + android:layout_height="match_parent"> + <variable name="obj" type="android.databinding.testapp.vo.ObservableWithNotBindableFieldObject"/> + <TextView android:layout_width="wrap_content" android:layout_height="wrap_content" + android:id="@+id/text_view" + android:text="@{obj.data}"/> +</LinearLayout> diff --git a/tools/data-binding/integration-tests/TestApp/app/src/main/res/layout/plain_layout.xml b/tools/data-binding/integration-tests/TestApp/app/src/main/res/layout/plain_layout.xml new file mode 100644 index 0000000..2132370 --- /dev/null +++ b/tools/data-binding/integration-tests/TestApp/app/src/main/res/layout/plain_layout.xml @@ -0,0 +1,17 @@ +<?xml version="1.0" encoding="utf-8"?> +<!-- + ~ 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. + --> + +<FrameLayout xmlns:android="http://schemas.android.com/apk/res/android" + android:layout_width="match_parent" + android:layout_height="match_parent"/> diff --git a/tools/data-binding/integration-tests/TestApp/app/src/main/res/layout/progress_bar_adapter_test.xml b/tools/data-binding/integration-tests/TestApp/app/src/main/res/layout/progress_bar_adapter_test.xml new file mode 100644 index 0000000..4fb8235 --- /dev/null +++ b/tools/data-binding/integration-tests/TestApp/app/src/main/res/layout/progress_bar_adapter_test.xml @@ -0,0 +1,14 @@ +<?xml version="1.0" encoding="utf-8"?> +<LinearLayout xmlns:android="http://schemas.android.com/apk/res/android" + android:orientation="vertical" + android:layout_width="match_parent" + android:layout_height="match_parent"> + <variable name="obj" type="android.databinding.testapp.vo.ProgressBarBindingObject"/> + <ProgressBar + android:layout_width="match_parent" android:layout_height="match_parent" + android:id="@+id/view" + android:indeterminateTint="@{obj.indeterminateTint}" + android:progressTint="@{obj.progressTint}" + android:secondaryProgressTint="@{obj.secondaryProgressTint}" + /> +</LinearLayout> diff --git a/tools/data-binding/integration-tests/TestApp/app/src/main/res/layout/radio_group_adapter_test.xml b/tools/data-binding/integration-tests/TestApp/app/src/main/res/layout/radio_group_adapter_test.xml new file mode 100644 index 0000000..efb6038 --- /dev/null +++ b/tools/data-binding/integration-tests/TestApp/app/src/main/res/layout/radio_group_adapter_test.xml @@ -0,0 +1,18 @@ +<?xml version="1.0" encoding="utf-8"?> +<LinearLayout xmlns:android="http://schemas.android.com/apk/res/android" + android:orientation="vertical" + android:layout_width="match_parent" + android:layout_height="match_parent"> + <variable name="obj" type="android.databinding.testapp.vo.RadioGroupBindingObject"/> + <RadioGroup + android:layout_width="match_parent" android:layout_height="match_parent" + android:id="@+id/view" + android:checkedButton="@{obj.checkedButton}" + > + <RadioButton android:layout_width="match_parent" android:layout_height="wrap_content" + android:text="One" android:id="@+id/choiceOne"/> + <RadioButton android:layout_width="match_parent" android:layout_height="wrap_content" + android:text="Two" android:id="@+id/choiceTwo"/> + </RadioGroup> + +</LinearLayout> diff --git a/tools/data-binding/integration-tests/TestApp/app/src/main/res/layout/read_complex_ternary.xml b/tools/data-binding/integration-tests/TestApp/app/src/main/res/layout/read_complex_ternary.xml new file mode 100644 index 0000000..a50cda2 --- /dev/null +++ b/tools/data-binding/integration-tests/TestApp/app/src/main/res/layout/read_complex_ternary.xml @@ -0,0 +1,12 @@ +<?xml version="1.0" encoding="utf-8"?> +<LinearLayout xmlns:android="http://schemas.android.com/apk/res/android" + android:orientation="vertical" android:layout_width="match_parent" + android:layout_height="match_parent"> + <variable name="user" type="android.databinding.testapp.vo.User"/> + <TextView + android:id="@+id/text_view" + android:text='@{user.friend == null ? "?" : (user.friend.name + "is friend of " + user.name)}' + android:layout_width="wrap_content" + android:layout_height="wrap_content"/> + +</LinearLayout>
\ No newline at end of file diff --git a/tools/data-binding/integration-tests/TestApp/app/src/main/res/layout/resource_test.xml b/tools/data-binding/integration-tests/TestApp/app/src/main/res/layout/resource_test.xml new file mode 100644 index 0000000..59ecc35 --- /dev/null +++ b/tools/data-binding/integration-tests/TestApp/app/src/main/res/layout/resource_test.xml @@ -0,0 +1,40 @@ +<?xml version="1.0" encoding="utf-8"?> +<LinearLayout xmlns:android="http://schemas.android.com/apk/res/android" + android:id="@+id/view" + android:layout_width="match_parent" + android:layout_height="match_parent" + android:orientation="vertical" + > + <variable name="count" type="int"/> + <variable name="title" type="String"/> + <variable name="lastName" type="String"/> + <variable name="base" type="int"/> + <variable name="pbase" type="int"/> + + <TextView + android:id="@+id/textView0" + android:layout_width="wrap_content" + android:layout_height="wrap_content" + android:text="@{@string/nameWithTitle(title, lastName)}"/> + + <TextView + android:id="@+id/textView1" + android:layout_width="wrap_content" + android:layout_height="wrap_content" + android:text="@{@plurals/orange(count)}"/> + <TextView + android:id="@+id/fractionNoParameters" + android:layout_width="wrap_content" + android:layout_height="wrap_content" + android:text="@{`` + @fraction/myFraction}"/> + <TextView + android:id="@+id/fractionOneParameter" + android:layout_width="wrap_content" + android:layout_height="wrap_content" + android:text="@{`` + @fraction/myFraction(base)}"/> + <TextView + android:id="@+id/fractionTwoParameters" + android:layout_width="wrap_content" + android:layout_height="wrap_content" + android:text="@{`` + @fraction/myParentFraction(base, pbase)}"/> +</LinearLayout> diff --git a/tools/data-binding/integration-tests/TestApp/app/src/main/res/layout/spinner_adapter_test.xml b/tools/data-binding/integration-tests/TestApp/app/src/main/res/layout/spinner_adapter_test.xml new file mode 100644 index 0000000..a82e7cea --- /dev/null +++ b/tools/data-binding/integration-tests/TestApp/app/src/main/res/layout/spinner_adapter_test.xml @@ -0,0 +1,12 @@ +<?xml version="1.0" encoding="utf-8"?> +<LinearLayout xmlns:android="http://schemas.android.com/apk/res/android" + android:orientation="vertical" + android:layout_width="match_parent" + android:layout_height="match_parent"> + <variable name="obj" type="android.databinding.testapp.vo.SpinnerBindingObject"/> + <Spinner + android:layout_width="match_parent" android:layout_height="match_parent" + android:id="@+id/view" + android:popupBackground="@{obj.popupBackground}" + /> +</LinearLayout> diff --git a/tools/data-binding/integration-tests/TestApp/app/src/main/res/layout/switch_adapter_test.xml b/tools/data-binding/integration-tests/TestApp/app/src/main/res/layout/switch_adapter_test.xml new file mode 100644 index 0000000..4bb1b84 --- /dev/null +++ b/tools/data-binding/integration-tests/TestApp/app/src/main/res/layout/switch_adapter_test.xml @@ -0,0 +1,13 @@ +<?xml version="1.0" encoding="utf-8"?> +<LinearLayout xmlns:android="http://schemas.android.com/apk/res/android" + android:orientation="vertical" + android:layout_width="match_parent" + android:layout_height="match_parent"> + <variable name="obj" type="android.databinding.testapp.vo.SwitchBindingObject"/> + <Switch + android:layout_width="match_parent" android:layout_height="match_parent" + android:id="@+id/view" + android:thumb="@{obj.thumb}" + android:track="@{obj.track}" + /> +</LinearLayout> diff --git a/tools/data-binding/integration-tests/TestApp/app/src/main/res/layout/tab_widget_adapter_test.xml b/tools/data-binding/integration-tests/TestApp/app/src/main/res/layout/tab_widget_adapter_test.xml new file mode 100644 index 0000000..4167ae4 --- /dev/null +++ b/tools/data-binding/integration-tests/TestApp/app/src/main/res/layout/tab_widget_adapter_test.xml @@ -0,0 +1,14 @@ +<?xml version="1.0" encoding="utf-8"?> +<LinearLayout xmlns:android="http://schemas.android.com/apk/res/android" + android:orientation="vertical" + android:layout_width="match_parent" + android:layout_height="match_parent"> + <variable name="obj" type="android.databinding.testapp.vo.TabWidgetBindingObject"/> + <TabWidget android:layout_width="match_parent" android:layout_height="match_parent" + android:id="@+id/view" + android:divider="@{obj.divider}" + android:tabStripEnabled="@{obj.tabStripEnabled}" + android:tabStripLeft="@{obj.tabStripLeft}" + android:tabStripRight="@{obj.tabStripRight}" + /> +</LinearLayout> diff --git a/tools/data-binding/integration-tests/TestApp/app/src/main/res/layout/table_layout_adapter_test.xml b/tools/data-binding/integration-tests/TestApp/app/src/main/res/layout/table_layout_adapter_test.xml new file mode 100644 index 0000000..8d53bce --- /dev/null +++ b/tools/data-binding/integration-tests/TestApp/app/src/main/res/layout/table_layout_adapter_test.xml @@ -0,0 +1,32 @@ +<?xml version="1.0" encoding="utf-8"?> +<LinearLayout xmlns:android="http://schemas.android.com/apk/res/android" + android:orientation="vertical" + android:layout_width="match_parent" + android:layout_height="match_parent"> + <variable name="obj" type="android.databinding.testapp.vo.TableLayoutBindingObject"/> + <TableLayout + android:layout_width="match_parent" android:layout_height="match_parent" + android:id="@+id/view" + android:divider="@{obj.divider}" + android:collapseColumns="@{obj.collapseColumns}" + android:shrinkColumns="@{obj.shrinkColumns}" + android:stretchColumns="@{obj.stretchColumns}" + > + <TableRow android:layout_width="wrap_content" android:layout_height="wrap_content"> + <TextView android:layout_width="wrap_content" android:layout_height="wrap_content" + android:text="Hello"/> + <TextView android:layout_width="wrap_content" android:layout_height="wrap_content" + android:text="Happy"/> + <TextView android:layout_width="wrap_content" android:layout_height="wrap_content" + android:text="World"/> + </TableRow> + <TableRow android:layout_width="wrap_content" android:layout_height="wrap_content"> + <TextView android:layout_width="wrap_content" android:layout_height="wrap_content" + android:text="Goodbye"/> + <TextView android:layout_width="wrap_content" android:layout_height="wrap_content" + android:text="Cruel"/> + <TextView android:layout_width="wrap_content" android:layout_height="wrap_content" + android:text="World"/> + </TableRow> + </TableLayout> +</LinearLayout> diff --git a/tools/data-binding/integration-tests/TestApp/app/src/main/res/layout/text_view_adapter_test.xml b/tools/data-binding/integration-tests/TestApp/app/src/main/res/layout/text_view_adapter_test.xml new file mode 100644 index 0000000..dc6fd29 --- /dev/null +++ b/tools/data-binding/integration-tests/TestApp/app/src/main/res/layout/text_view_adapter_test.xml @@ -0,0 +1,69 @@ +<?xml version="1.0" encoding="utf-8"?> +<LinearLayout xmlns:android="http://schemas.android.com/apk/res/android" + android:orientation="vertical" + android:layout_width="match_parent" + android:layout_height="match_parent"> + <variable name="obj" type="android.databinding.testapp.vo.TextViewBindingObject"/> + <TextView android:layout_width="match_parent" android:layout_height="match_parent" + android:id="@+id/numericText" + android:numeric="@{obj.numeric}" + /> + <TextView android:layout_width="match_parent" android:layout_height="match_parent" + android:id="@+id/textDrawableNormal" + android:drawableBottom="@{obj.drawableBottom}" + android:drawableLeft="@{obj.drawableLeft}" + android:drawableRight="@{obj.drawableRight}" + android:drawableTop="@{obj.drawableTop}" + /> + <TextView android:layout_width="match_parent" android:layout_height="match_parent" + android:id="@+id/textDrawableStartEnd" + android:drawableStart="@{obj.drawableStart}" + android:drawableEnd="@{obj.drawableEnd}" + /> + <TextView android:layout_width="match_parent" android:layout_height="match_parent" + android:id="@+id/textView" + android:autoLink="@{obj.autoLink}" + android:drawablePadding="@{obj.drawablePadding}" + android:scrollHorizontally="@{obj.scrollHorizontally}" + android:textColorHighlight="@{obj.textColorHighlight}" + android:textColorHint="@{obj.textColorHint}" + android:textColorLink="@{obj.textColorLink}" + android:autoText="@{obj.autoText}" + android:capitalize="@{obj.capitalize}" + android:imeActionLabel="@{obj.imeActionLabel}" + android:imeActionId="@{obj.imeActionId}" + android:lineSpacingExtra="@{obj.lineSpacingExtra}" + android:lineSpacingMultiplier="@{obj.lineSpacingMultiplier}" + android:maxLength="@{obj.maxLength}" + android:shadowColor="@{obj.shadowColor}" + android:shadowDx="@{obj.shadowDx}" + android:shadowDy="@{obj.shadowDy}" + android:shadowRadius="@{obj.shadowRadius}" + android:textSize="@{obj.textSize}" + /> + <TextView android:layout_width="match_parent" android:layout_height="match_parent" + android:id="@+id/textAllCaps" + android:textAllCaps="@{obj.textAllCaps}" + /> + <TextView android:layout_width="match_parent" android:layout_height="match_parent" + android:id="@+id/textBufferType" + android:bufferType="@{obj.bufferType}" + /> + <TextView android:layout_width="match_parent" android:layout_height="match_parent" + android:id="@+id/textInputType" + android:inputType="@{obj.inputType}" + /> + <TextView android:layout_width="match_parent" android:layout_height="match_parent" + android:id="@+id/textDigits" + android:digits="@{obj.digits}" + /> + <TextView android:layout_width="match_parent" android:layout_height="match_parent" + android:id="@+id/textPhoneNumber" + android:phoneNumber="@{obj.phoneNumber}" + /> + <TextView android:layout_width="match_parent" android:layout_height="match_parent" + android:id="@+id/textInputMethod" + android:inputMethod="@{obj.inputMethod}" + /> + +</LinearLayout> diff --git a/tools/data-binding/integration-tests/TestApp/app/src/main/res/layout/view_adapter_test.xml b/tools/data-binding/integration-tests/TestApp/app/src/main/res/layout/view_adapter_test.xml new file mode 100644 index 0000000..73660ce --- /dev/null +++ b/tools/data-binding/integration-tests/TestApp/app/src/main/res/layout/view_adapter_test.xml @@ -0,0 +1,73 @@ +<?xml version="1.0" encoding="utf-8"?> +<LinearLayout xmlns:android="http://schemas.android.com/apk/res/android" + android:orientation="vertical" + android:layout_width="match_parent" + android:layout_height="match_parent" + > + <variable name="obj" type="android.databinding.testapp.vo.ViewBindingObject"/> + <View + android:id="@+id/padding" + android:layout_width="10dp" + android:layout_height="10dp" + android:padding="@{obj.padding}" + /> + <View + android:id="@+id/paddingStartEnd" + android:layout_width="10dp" + android:layout_height="10dp" + android:paddingEnd="@{obj.paddingEnd}" + android:paddingStart="@{obj.paddingStart}" + /> + <View + android:id="@+id/paddingTopBottom" + android:layout_width="10dp" + android:layout_height="10dp" + android:paddingBottom="@{obj.paddingBottom}" + android:paddingTop="@{obj.paddingTop}" + /> + <View + android:id="@+id/paddingLeftRight" + android:layout_width="10dp" + android:layout_height="10dp" + android:paddingLeft="@{obj.paddingLeft}" + android:paddingRight="@{obj.paddingRight}" + /> + <View + android:id="@+id/backgroundTint" + android:backgroundTint="@{obj.backgroundTint}" + android:layout_width="10dp" + android:layout_height="10dp"/> + <View + android:id="@+id/fadeScrollbars" + android:fadeScrollbars="@{obj.fadeScrollbars}" + android:layout_width="10dp" + android:layout_height="10dp"/> + <View + android:id="@+id/nextFocus" + android:nextFocusForward="@{obj.nextFocusForward}" + android:nextFocusLeft="@{obj.nextFocusLeft}" + android:nextFocusRight="@{obj.nextFocusRight}" + android:nextFocusUp="@{obj.nextFocusUp}" + android:nextFocusDown="@{obj.nextFocusDown}" + android:layout_width="10dp" + android:layout_height="10dp"/> + <View + android:id="@+id/requiresFadingEdge" + android:requiresFadingEdge="@{obj.requiresFadingEdge}" + android:layout_width="10dp" + android:layout_height="10dp"/> + <View + android:id="@+id/scrollbar" + android:scrollbarDefaultDelayBeforeFade="@{obj.scrollbarDefaultDelayBeforeFade}" + android:scrollbarFadeDuration="@{obj.scrollbarFadeDuration}" + android:scrollbarSize="@{obj.scrollbarSize}" + android:scrollbarStyle="@{obj.scrollbarStyle}" + android:layout_width="10dp" + android:layout_height="10dp"/> + <View + android:id="@+id/transformPivot" + android:transformPivotX="@{obj.transformPivotX}" + android:transformPivotY="@{obj.transformPivotY}" + android:layout_width="10dp" + android:layout_height="10dp"/> +</LinearLayout> diff --git a/tools/data-binding/integration-tests/TestApp/app/src/main/res/layout/view_group_adapter_test.xml b/tools/data-binding/integration-tests/TestApp/app/src/main/res/layout/view_group_adapter_test.xml new file mode 100644 index 0000000..af40a27 --- /dev/null +++ b/tools/data-binding/integration-tests/TestApp/app/src/main/res/layout/view_group_adapter_test.xml @@ -0,0 +1,15 @@ +<?xml version="1.0" encoding="utf-8"?> +<LinearLayout xmlns:android="http://schemas.android.com/apk/res/android" + android:orientation="vertical" + android:layout_width="match_parent" + android:layout_height="match_parent"> + <variable name="obj" type="android.databinding.testapp.vo.ViewGroupBindingObject"/> + <FrameLayout android:layout_width="match_parent" + android:layout_height="match_parent" + android:id="@+id/view" + android:alwaysDrawnWithCache="@{obj.alwaysDrawnWithCache}" + android:animationCache="@{obj.animationCache}" + android:splitMotionEvents="@{obj.splitMotionEvents}" + android:animateLayoutChanges="@{obj.animateLayoutChanges}" + /> +</LinearLayout> diff --git a/tools/data-binding/integration-tests/TestApp/app/src/main/res/layout/view_stub.xml b/tools/data-binding/integration-tests/TestApp/app/src/main/res/layout/view_stub.xml new file mode 100644 index 0000000..af0796e --- /dev/null +++ b/tools/data-binding/integration-tests/TestApp/app/src/main/res/layout/view_stub.xml @@ -0,0 +1,25 @@ +<?xml version="1.0" encoding="utf-8"?> +<LinearLayout xmlns:android="http://schemas.android.com/apk/res/android" + android:orientation="vertical" + android:layout_width="match_parent" + android:layout_height="match_parent" + xmlns:bind="http://schemas.android.com/apk/res-auto"> + <variable name="viewStubVisibility" type="int"/> + <variable name="firstName" type="String"/> + <variable name="lastName" type="String"/> + <TextView android:layout_width="wrap_content" android:layout_height="wrap_content" + android:text="@{firstName}" + android:id="@+id/firstName" + /> + <TextView android:layout_width="wrap_content" android:layout_height="wrap_content" + android:text="@{lastName}" + android:id="@+id/lastName" + /> + + <ViewStub android:layout_width="match_parent" android:layout_height="match_parent" + android:id="@+id/viewStub" + android:visibility="@{viewStubVisibility}" + android:layout="@layout/view_stub_contents" + bind:firstName="@{firstName}" + bind:lastName="@{lastName}"/> +</LinearLayout> diff --git a/tools/data-binding/integration-tests/TestApp/app/src/main/res/layout/view_stub_adapter_test.xml b/tools/data-binding/integration-tests/TestApp/app/src/main/res/layout/view_stub_adapter_test.xml new file mode 100644 index 0000000..e0b922b --- /dev/null +++ b/tools/data-binding/integration-tests/TestApp/app/src/main/res/layout/view_stub_adapter_test.xml @@ -0,0 +1,10 @@ +<?xml version="1.0" encoding="utf-8"?> +<LinearLayout xmlns:android="http://schemas.android.com/apk/res/android" + android:orientation="vertical" + android:layout_width="match_parent" + android:layout_height="match_parent"> + <variable name="obj" type="android.databinding.testapp.vo.ViewStubBindingObject"/> + <ViewStub android:layout_width="match_parent" android:layout_height="match_parent" + android:id="@+id/view" + android:layout="@{obj.layout}"/> +</LinearLayout> diff --git a/tools/data-binding/integration-tests/TestApp/app/src/main/res/layout/view_stub_contents.xml b/tools/data-binding/integration-tests/TestApp/app/src/main/res/layout/view_stub_contents.xml new file mode 100644 index 0000000..9305189 --- /dev/null +++ b/tools/data-binding/integration-tests/TestApp/app/src/main/res/layout/view_stub_contents.xml @@ -0,0 +1,14 @@ +<?xml version="1.0" encoding="utf-8"?> +<LinearLayout xmlns:android="http://schemas.android.com/apk/res/android" + android:orientation="vertical" + android:layout_width="match_parent" + android:layout_height="match_parent"> + <variable name="firstName" type="String"/> + <variable name="lastName" type="String"/> + <TextView android:layout_width="wrap_content" android:layout_height="wrap_content" + android:text="@{firstName}" + android:id="@+id/firstNameContents"/> + <TextView android:layout_width="wrap_content" android:layout_height="wrap_content" + android:text="@{lastName}" + android:id="@+id/lastNameContents"/> +</LinearLayout> diff --git a/tools/data-binding/integration-tests/TestApp/app/src/main/res/values-v21/styles.xml b/tools/data-binding/integration-tests/TestApp/app/src/main/res/values-v21/styles.xml new file mode 100644 index 0000000..0a2c6be --- /dev/null +++ b/tools/data-binding/integration-tests/TestApp/app/src/main/res/values-v21/styles.xml @@ -0,0 +1,18 @@ +<?xml version="1.0" encoding="utf-8"?> +<!-- + ~ 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. + --> + +<resources> + <style name="AppTheme" parent="android:Theme.Material.Light"> + </style> +</resources> diff --git a/tools/data-binding/integration-tests/TestApp/app/src/main/res/values/fractions.xml b/tools/data-binding/integration-tests/TestApp/app/src/main/res/values/fractions.xml new file mode 100644 index 0000000..8817316 --- /dev/null +++ b/tools/data-binding/integration-tests/TestApp/app/src/main/res/values/fractions.xml @@ -0,0 +1,20 @@ +<!-- + ~ Copyright (C) 2015 The Android Open Source Project + ~ Licensed under the Apache License, Version 2.0 (the "License"); + ~ you may not use this file except in compliance with the License. + ~ You may obtain a copy of the License at + ~ http://www.apache.org/licenses/LICENSE-2.0 + ~ Unless required by applicable law or agreed to in writing, software + ~ distributed under the License is distributed on an "AS IS" BASIS, + ~ WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. + ~ See the License for the specific language governing permissions and + ~ limitations under the License. + --> + +<resources> + + <!-- Base application theme. --> + <fraction name="myFraction">150%</fraction> + <fraction name="myParentFraction">300%p</fraction> + +</resources> diff --git a/tools/data-binding/integration-tests/TestApp/app/src/main/res/values/strings.xml b/tools/data-binding/integration-tests/TestApp/app/src/main/res/values/strings.xml new file mode 100644 index 0000000..e53e327 --- /dev/null +++ b/tools/data-binding/integration-tests/TestApp/app/src/main/res/values/strings.xml @@ -0,0 +1,22 @@ +<!-- + ~ 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. + --> + +<resources> + <string name="app_name">TestApp</string> + <string name="rain">Rain</string> + <string name="nameWithTitle">%1$s %2$s</string> + <plurals name="orange"> + <item quantity="one">orange</item> + <item quantity="other">oranges</item> + </plurals> +</resources> diff --git a/tools/data-binding/integration-tests/TestApp/app/src/main/res/values/styles.xml b/tools/data-binding/integration-tests/TestApp/app/src/main/res/values/styles.xml new file mode 100644 index 0000000..c0d5471 --- /dev/null +++ b/tools/data-binding/integration-tests/TestApp/app/src/main/res/values/styles.xml @@ -0,0 +1,21 @@ +<!-- + ~ 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. + --> + +<resources> + + <!-- Base application theme. --> + <style name="AppTheme" parent="android:Theme.Holo"> + <!-- Customize your theme here. --> + </style> + +</resources> diff --git a/tools/data-binding/integration-tests/TestApp/build.gradle b/tools/data-binding/integration-tests/TestApp/build.gradle new file mode 100644 index 0000000..85d7f88 --- /dev/null +++ b/tools/data-binding/integration-tests/TestApp/build.gradle @@ -0,0 +1,29 @@ +buildscript { + def Properties dataBindingProperties = new Properties() + dataBindingProperties.load(new FileInputStream("${projectDir}/../../databinding.properties")) + dataBindingProperties.mavenRepoDir = "${projectDir}/../../${dataBindingProperties.mavenRepoName}" + ext.config = dataBindingProperties + println "loaded config" + + repositories { + jcenter() + maven { + url config.mavenRepoDir + } + } + dependencies { + classpath 'com.android.tools.build:gradle:1.1.3' + classpath "com.android.databinding:dataBinder:${config.snapshotVersion}" + // NOTE: Do not place your application dependencies here; they belong + // in the individual module build.gradle files + } +} + +allprojects { + repositories { + jcenter() + maven { + url config.mavenRepoDir + } + } +} diff --git a/tools/data-binding/integration-tests/TestApp/gradle.properties b/tools/data-binding/integration-tests/TestApp/gradle.properties new file mode 100644 index 0000000..1d3591c --- /dev/null +++ b/tools/data-binding/integration-tests/TestApp/gradle.properties @@ -0,0 +1,18 @@ +# Project-wide Gradle settings. + +# IDE (e.g. Android Studio) users: +# Gradle settings configured through the IDE *will override* +# any settings specified in this file. + +# For more details on how to configure your build environment visit +# http://www.gradle.org/docs/current/userguide/build_environment.html + +# Specifies the JVM arguments used for the daemon process. +# The setting is particularly useful for tweaking memory settings. +# Default value: -Xmx10248m -XX:MaxPermSize=256m +# org.gradle.jvmargs=-Xmx2048m -XX:MaxPermSize=512m -XX:+HeapDumpOnOutOfMemoryError -Dfile.encoding=UTF-8 + +# When configured, Gradle will run in incubating parallel mode. +# This option should only be used with decoupled projects. More details, visit +# http://www.gradle.org/docs/current/userguide/multi_project_builds.html#sec:decoupled_projects +# org.gradle.parallel=true
\ No newline at end of file diff --git a/tools/data-binding/integration-tests/TestApp/gradle/wrapper/gradle-wrapper.jar b/tools/data-binding/integration-tests/TestApp/gradle/wrapper/gradle-wrapper.jar Binary files differnew file mode 100644 index 0000000..8c0fb64 --- /dev/null +++ b/tools/data-binding/integration-tests/TestApp/gradle/wrapper/gradle-wrapper.jar diff --git a/tools/data-binding/integration-tests/TestApp/gradle/wrapper/gradle-wrapper.properties b/tools/data-binding/integration-tests/TestApp/gradle/wrapper/gradle-wrapper.properties new file mode 100644 index 0000000..0c71e76 --- /dev/null +++ b/tools/data-binding/integration-tests/TestApp/gradle/wrapper/gradle-wrapper.properties @@ -0,0 +1,6 @@ +#Wed Apr 10 15:27:10 PDT 2013 +distributionBase=GRADLE_USER_HOME +distributionPath=wrapper/dists +zipStoreBase=GRADLE_USER_HOME +zipStorePath=wrapper/dists +distributionUrl=https\://services.gradle.org/distributions/gradle-2.2.1-all.zip diff --git a/tools/data-binding/integration-tests/TestApp/gradlew b/tools/data-binding/integration-tests/TestApp/gradlew new file mode 100755 index 0000000..91a7e26 --- /dev/null +++ b/tools/data-binding/integration-tests/TestApp/gradlew @@ -0,0 +1,164 @@ +#!/usr/bin/env bash + +############################################################################## +## +## Gradle start up script for UN*X +## +############################################################################## + +# Add default JVM options here. You can also use JAVA_OPTS and GRADLE_OPTS to pass JVM options to this script. +DEFAULT_JVM_OPTS="" + +APP_NAME="Gradle" +APP_BASE_NAME=`basename "$0"` + +# Use the maximum available, or set MAX_FD != -1 to use that value. +MAX_FD="maximum" + +warn ( ) { + echo "$*" +} + +die ( ) { + echo + echo "$*" + echo + exit 1 +} + +# OS specific support (must be 'true' or 'false'). +cygwin=false +msys=false +darwin=false +case "`uname`" in + CYGWIN* ) + cygwin=true + ;; + Darwin* ) + darwin=true + ;; + MINGW* ) + msys=true + ;; +esac + +# For Cygwin, ensure paths are in UNIX format before anything is touched. +if $cygwin ; then + [ -n "$JAVA_HOME" ] && JAVA_HOME=`cygpath --unix "$JAVA_HOME"` +fi + +# Attempt to set APP_HOME +# Resolve links: $0 may be a link +PRG="$0" +# Need this for relative symlinks. +while [ -h "$PRG" ] ; do + ls=`ls -ld "$PRG"` + link=`expr "$ls" : '.*-> \(.*\)$'` + if expr "$link" : '/.*' > /dev/null; then + PRG="$link" + else + PRG=`dirname "$PRG"`"/$link" + fi +done +SAVED="`pwd`" +cd "`dirname \"$PRG\"`/" >&- +APP_HOME="`pwd -P`" +cd "$SAVED" >&- + +CLASSPATH=$APP_HOME/gradle/wrapper/gradle-wrapper.jar + +# Determine the Java command to use to start the JVM. +if [ -n "$JAVA_HOME" ] ; then + if [ -x "$JAVA_HOME/jre/sh/java" ] ; then + # IBM's JDK on AIX uses strange locations for the executables + JAVACMD="$JAVA_HOME/jre/sh/java" + else + JAVACMD="$JAVA_HOME/bin/java" + fi + if [ ! -x "$JAVACMD" ] ; then + die "ERROR: JAVA_HOME is set to an invalid directory: $JAVA_HOME + +Please set the JAVA_HOME variable in your environment to match the +location of your Java installation." + fi +else + JAVACMD="java" + which java >/dev/null 2>&1 || die "ERROR: JAVA_HOME is not set and no 'java' command could be found in your PATH. + +Please set the JAVA_HOME variable in your environment to match the +location of your Java installation." +fi + +# Increase the maximum file descriptors if we can. +if [ "$cygwin" = "false" -a "$darwin" = "false" ] ; then + MAX_FD_LIMIT=`ulimit -H -n` + if [ $? -eq 0 ] ; then + if [ "$MAX_FD" = "maximum" -o "$MAX_FD" = "max" ] ; then + MAX_FD="$MAX_FD_LIMIT" + fi + ulimit -n $MAX_FD + if [ $? -ne 0 ] ; then + warn "Could not set maximum file descriptor limit: $MAX_FD" + fi + else + warn "Could not query maximum file descriptor limit: $MAX_FD_LIMIT" + fi +fi + +# For Darwin, add options to specify how the application appears in the dock +if $darwin; then + GRADLE_OPTS="$GRADLE_OPTS \"-Xdock:name=$APP_NAME\" \"-Xdock:icon=$APP_HOME/media/gradle.icns\"" +fi + +# For Cygwin, switch paths to Windows format before running java +if $cygwin ; then + APP_HOME=`cygpath --path --mixed "$APP_HOME"` + CLASSPATH=`cygpath --path --mixed "$CLASSPATH"` + + # We build the pattern for arguments to be converted via cygpath + ROOTDIRSRAW=`find -L / -maxdepth 1 -mindepth 1 -type d 2>/dev/null` + SEP="" + for dir in $ROOTDIRSRAW ; do + ROOTDIRS="$ROOTDIRS$SEP$dir" + SEP="|" + done + OURCYGPATTERN="(^($ROOTDIRS))" + # Add a user-defined pattern to the cygpath arguments + if [ "$GRADLE_CYGPATTERN" != "" ] ; then + OURCYGPATTERN="$OURCYGPATTERN|($GRADLE_CYGPATTERN)" + fi + # Now convert the arguments - kludge to limit ourselves to /bin/sh + i=0 + for arg in "$@" ; do + CHECK=`echo "$arg"|egrep -c "$OURCYGPATTERN" -` + CHECK2=`echo "$arg"|egrep -c "^-"` ### Determine if an option + + if [ $CHECK -ne 0 ] && [ $CHECK2 -eq 0 ] ; then ### Added a condition + eval `echo args$i`=`cygpath --path --ignore --mixed "$arg"` + else + eval `echo args$i`="\"$arg\"" + fi + i=$((i+1)) + done + case $i in + (0) set -- ;; + (1) set -- "$args0" ;; + (2) set -- "$args0" "$args1" ;; + (3) set -- "$args0" "$args1" "$args2" ;; + (4) set -- "$args0" "$args1" "$args2" "$args3" ;; + (5) set -- "$args0" "$args1" "$args2" "$args3" "$args4" ;; + (6) set -- "$args0" "$args1" "$args2" "$args3" "$args4" "$args5" ;; + (7) set -- "$args0" "$args1" "$args2" "$args3" "$args4" "$args5" "$args6" ;; + (8) set -- "$args0" "$args1" "$args2" "$args3" "$args4" "$args5" "$args6" "$args7" ;; + (9) set -- "$args0" "$args1" "$args2" "$args3" "$args4" "$args5" "$args6" "$args7" "$args8" ;; + esac +fi + +# Split up the JVM_OPTS And GRADLE_OPTS values into an array, following the shell quoting and substitution rules +function splitJvmOpts() { + JVM_OPTS=("$@") +} +eval splitJvmOpts $DEFAULT_JVM_OPTS $JAVA_OPTS $GRADLE_OPTS +JVM_OPTS[${#JVM_OPTS[*]}]="-Dorg.gradle.appname=$APP_BASE_NAME" + +exec "$JAVACMD" "${JVM_OPTS[@]}" -classpath "$CLASSPATH" org.gradle.wrapper.GradleWrapperMain "$@" diff --git a/tools/data-binding/integration-tests/TestApp/gradlew.bat b/tools/data-binding/integration-tests/TestApp/gradlew.bat new file mode 100644 index 0000000..aec9973 --- /dev/null +++ b/tools/data-binding/integration-tests/TestApp/gradlew.bat @@ -0,0 +1,90 @@ +@if "%DEBUG%" == "" @echo off
+@rem ##########################################################################
+@rem
+@rem Gradle startup script for Windows
+@rem
+@rem ##########################################################################
+
+@rem Set local scope for the variables with windows NT shell
+if "%OS%"=="Windows_NT" setlocal
+
+@rem Add default JVM options here. You can also use JAVA_OPTS and GRADLE_OPTS to pass JVM options to this script.
+set DEFAULT_JVM_OPTS=
+
+set DIRNAME=%~dp0
+if "%DIRNAME%" == "" set DIRNAME=.
+set APP_BASE_NAME=%~n0
+set APP_HOME=%DIRNAME%
+
+@rem Find java.exe
+if defined JAVA_HOME goto findJavaFromJavaHome
+
+set JAVA_EXE=java.exe
+%JAVA_EXE% -version >NUL 2>&1
+if "%ERRORLEVEL%" == "0" goto init
+
+echo.
+echo ERROR: JAVA_HOME is not set and no 'java' command could be found in your PATH.
+echo.
+echo Please set the JAVA_HOME variable in your environment to match the
+echo location of your Java installation.
+
+goto fail
+
+:findJavaFromJavaHome
+set JAVA_HOME=%JAVA_HOME:"=%
+set JAVA_EXE=%JAVA_HOME%/bin/java.exe
+
+if exist "%JAVA_EXE%" goto init
+
+echo.
+echo ERROR: JAVA_HOME is set to an invalid directory: %JAVA_HOME%
+echo.
+echo Please set the JAVA_HOME variable in your environment to match the
+echo location of your Java installation.
+
+goto fail
+
+:init
+@rem Get command-line arguments, handling Windowz variants
+
+if not "%OS%" == "Windows_NT" goto win9xME_args
+if "%@eval[2+2]" == "4" goto 4NT_args
+
+:win9xME_args
+@rem Slurp the command line arguments.
+set CMD_LINE_ARGS=
+set _SKIP=2
+
+:win9xME_args_slurp
+if "x%~1" == "x" goto execute
+
+set CMD_LINE_ARGS=%*
+goto execute
+
+:4NT_args
+@rem Get arguments from the 4NT Shell from JP Software
+set CMD_LINE_ARGS=%$
+
+:execute
+@rem Setup the command line
+
+set CLASSPATH=%APP_HOME%\gradle\wrapper\gradle-wrapper.jar
+
+@rem Execute Gradle
+"%JAVA_EXE%" %DEFAULT_JVM_OPTS% %JAVA_OPTS% %GRADLE_OPTS% "-Dorg.gradle.appname=%APP_BASE_NAME%" -classpath "%CLASSPATH%" org.gradle.wrapper.GradleWrapperMain %CMD_LINE_ARGS%
+
+:end
+@rem End local scope for the variables with windows NT shell
+if "%ERRORLEVEL%"=="0" goto mainEnd
+
+:fail
+rem Set variable GRADLE_EXIT_CONSOLE if you need the _script_ return code instead of
+rem the _cmd.exe /c_ return code!
+if not "" == "%GRADLE_EXIT_CONSOLE%" exit 1
+exit /b 1
+
+:mainEnd
+if "%OS%"=="Windows_NT" endlocal
+
+:omega
diff --git a/tools/data-binding/integration-tests/TestApp/settings.gradle b/tools/data-binding/integration-tests/TestApp/settings.gradle new file mode 100644 index 0000000..e7b4def --- /dev/null +++ b/tools/data-binding/integration-tests/TestApp/settings.gradle @@ -0,0 +1 @@ +include ':app' diff --git a/tools/data-binding/library/build.gradle b/tools/data-binding/library/build.gradle new file mode 100644 index 0000000..4679d3f --- /dev/null +++ b/tools/data-binding/library/build.gradle @@ -0,0 +1,105 @@ +/* + * Copyright (C) 2014 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. + */ + +// Top-level build file where you can add configuration options common to all sub-projects/modules. + +buildscript { + repositories { + jcenter() + } + dependencies { + classpath "com.android.tools.build:gradle:${config.androidPluginVersion}" + // NOTE: Do not place your application dependencies here; they belong + // in the individual module build.gradle files + } +} + +apply plugin: 'com.android.library' +apply plugin: 'maven' + +android { + compileSdkVersion 21 + buildToolsVersion "21.1" + + defaultConfig { + minSdkVersion 7 + targetSdkVersion 21 + versionCode 1 + versionName "1.0" + } + compileOptions { + sourceCompatibility JavaVersion.VERSION_1_7 + targetCompatibility JavaVersion.VERSION_1_7 + } + buildTypes { + release { + minifyEnabled false + } + } + packagingOptions { + exclude 'META-INF/services/javax.annotation.processing.Processor' + exclude 'META-INF/LICENSE.txt' + exclude 'META-INF/NOTICE.txt' + exclude 'android/databinding/DataBinderMapper.class' + } +} +dependencies { + compile fileTree(dir: 'libs', include: ['*.jar']) + compile project(":baseLibrary") + compile 'com.android.support:support-v4:+' +} + +configurations { + jarArchives +} + + +//create jar tasks +android.libraryVariants.all { variant -> + def name = variant.buildType.name + + if (name.equals(com.android.builder.core.BuilderConstants.DEBUG)) { + return; // Skip debug builds. + } + // @Jar version is needed to run compiler tests + def task = project.tasks.create "jar${name.capitalize()}", Jar + task.dependsOn variant.javaCompile + task.from variant.javaCompile.destinationDir + artifacts.add('jarArchives', task); +} +uploadArchives { + repositories { + mavenDeployer { + pom.artifactId = 'library' + } + } +} + +afterEvaluate { + tasks['packageReleaseJar'].exclude('android/databinding/DataBinderMapper.*') + tasks['packageDebugJar'].exclude('android/databinding/DataBinderMapper.*') +} + +uploadJarArchives { + repositories { + mavenDeployer { + repository(url: "file://${config.mavenRepoDir}") + pom.artifactId = "libraryJar" + } + } +} + +uploadArchives.dependsOn uploadJarArchives
\ No newline at end of file diff --git a/tools/data-binding/library/src/main/AndroidManifest.xml b/tools/data-binding/library/src/main/AndroidManifest.xml new file mode 100644 index 0000000..8596caf --- /dev/null +++ b/tools/data-binding/library/src/main/AndroidManifest.xml @@ -0,0 +1,20 @@ +<!-- + Copyright (C) 2014 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. + --> + +<manifest xmlns:android="http://schemas.android.com/apk/res/android" + package="com.android.databinding.library"> + +</manifest> diff --git a/tools/data-binding/library/src/main/java/android/databinding/BaseObservable.java b/tools/data-binding/library/src/main/java/android/databinding/BaseObservable.java new file mode 100644 index 0000000..3aa9d35 --- /dev/null +++ b/tools/data-binding/library/src/main/java/android/databinding/BaseObservable.java @@ -0,0 +1,51 @@ +/* + * Copyright (C) 2014 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 android.databinding; + +public class BaseObservable implements Observable { + private PropertyChangeRegistry mCallbacks; + + public BaseObservable() { + } + + @Override + public synchronized void addOnPropertyChangedListener(OnPropertyChangedListener listener) { + if (mCallbacks == null) { + mCallbacks = new PropertyChangeRegistry(); + } + mCallbacks.add(listener); + } + + @Override + public synchronized void removeOnPropertyChangedListener(OnPropertyChangedListener listener) { + if (mCallbacks != null) { + mCallbacks.remove(listener); + } + } + + public synchronized void notifyChange() { + if (mCallbacks != null) { + mCallbacks.notifyCallbacks(this, 0, null); + } + } + + public void notifyPropertyChanged(int fieldId) { + if (mCallbacks != null) { + mCallbacks.notifyCallbacks(this, fieldId, null); + } + } +} diff --git a/tools/data-binding/library/src/main/java/android/databinding/DataBinderMapper.java b/tools/data-binding/library/src/main/java/android/databinding/DataBinderMapper.java new file mode 100644 index 0000000..9fc7f4b --- /dev/null +++ b/tools/data-binding/library/src/main/java/android/databinding/DataBinderMapper.java @@ -0,0 +1,34 @@ +/* + * Copyright (C) 2014 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 android.databinding; + +import android.view.View; + +/** + * This class will be stripped from the jar and then replaced by the annotation processor + * as part of the code generation step. This class's existence is just to ensure that + * compile works and no reflection is needed to access the generated class. + */ +class DataBinderMapper { + public ViewDataBinding getDataBinder(View view, int layoutId) { + return null; + } + public int getId(String key) { + return 0; + } + public static int TARGET_MIN_SDK = 0; +} diff --git a/tools/data-binding/library/src/main/java/android/databinding/DataBindingUtil.java b/tools/data-binding/library/src/main/java/android/databinding/DataBindingUtil.java new file mode 100644 index 0000000..cb5d209 --- /dev/null +++ b/tools/data-binding/library/src/main/java/android/databinding/DataBindingUtil.java @@ -0,0 +1,41 @@ +/* + * 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 android.databinding; + +import android.content.Context; +import android.view.LayoutInflater; +import android.view.View; +import android.view.ViewGroup; + +/** + * Utility class to create {@link ViewDataBinding} from layouts. + */ +public class DataBindingUtil { + private static DataBinderMapper sMapper = new DataBinderMapper(); + + public static <T extends ViewDataBinding> T inflate(Context context, int layoutId, + ViewGroup parent, boolean attachToParent) { + final LayoutInflater inflater = LayoutInflater.from(context); + final View view = inflater.inflate(layoutId, parent, attachToParent); + return bindTo(view, layoutId); + } + + @SuppressWarnings("unchecked") + public static <T extends ViewDataBinding> T bindTo(View root, int layoutId) { + return (T) sMapper.getDataBinder(root, layoutId); + } +} diff --git a/tools/data-binding/library/src/main/java/android/databinding/ListChangeRegistry.java b/tools/data-binding/library/src/main/java/android/databinding/ListChangeRegistry.java new file mode 100644 index 0000000..e4c65bd --- /dev/null +++ b/tools/data-binding/library/src/main/java/android/databinding/ListChangeRegistry.java @@ -0,0 +1,106 @@ +/* + * 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 android.databinding; + +import android.support.v4.util.Pools; + +public class ListChangeRegistry + extends + CallbackRegistry<OnListChangedListener, ObservableList, ListChangeRegistry.ListChanges> { + private static final Pools.SynchronizedPool<ListChanges> sListChanges = + new Pools.SynchronizedPool<>(10); + + private static final int ALL = 0; + private static final int CHANGED = 1; + private static final int INSERTED = 2; + private static final int MOVED = 3; + private static final int REMOVED = 4; + + private static final CallbackRegistry.NotifierCallback<OnListChangedListener, ObservableList, ListChanges> NOTIFIER_CALLBACK = new CallbackRegistry.NotifierCallback<OnListChangedListener, ObservableList, ListChanges>() { + @Override + public void onNotifyCallback(OnListChangedListener callback, ObservableList sender, + int notificationType, ListChanges listChanges) { + switch (notificationType) { + case CHANGED: + callback.onItemRangeChanged(listChanges.start, listChanges.count); + break; + case INSERTED: + callback.onItemRangeInserted(listChanges.start, listChanges.count); + break; + case MOVED: + callback.onItemRangeMoved(listChanges.start, listChanges.to, listChanges.count); + break; + case REMOVED: + callback.onItemRangeRemoved(listChanges.start, listChanges.count); + break; + default: + callback.onChanged(); + break; + } + if (listChanges != null) { + sListChanges.release(listChanges); + } + } + }; + + public void notifyChanged(ObservableList list) { + notifyCallbacks(list, ALL, null); + } + + public void notifyChanged(ObservableList list, int start, int count) { + ListChanges listChanges = acquire(start, 0, count); + notifyCallbacks(list, CHANGED, listChanges); + } + + public void notifyInserted(ObservableList list, int start, int count) { + ListChanges listChanges = acquire(start, 0, count); + notifyCallbacks(list, INSERTED, listChanges); + } + + public void notifyMoved(ObservableList list, int from, int to, int count) { + ListChanges listChanges = acquire(from, to, count); + notifyCallbacks(list, MOVED, listChanges); + } + + public void notifyRemoved(ObservableList list, int start, int count) { + ListChanges listChanges = acquire(start, 0, count); + notifyCallbacks(list, REMOVED, listChanges); + } + + private static ListChanges acquire(int start, int to, int count) { + ListChanges listChanges = sListChanges.acquire(); + if (listChanges == null) { + listChanges = new ListChanges(); + } + listChanges.start = start; + listChanges.to = to; + listChanges.count = count; + return listChanges; + } + + /** + * Creates an EventRegistry that notifies the event with notifier. + */ + public ListChangeRegistry() { + super(NOTIFIER_CALLBACK); + } + + static class ListChanges { + public int start; + public int count; + public int to; + } +} diff --git a/tools/data-binding/library/src/main/java/android/databinding/MapChangeRegistry.java b/tools/data-binding/library/src/main/java/android/databinding/MapChangeRegistry.java new file mode 100644 index 0000000..6403cce --- /dev/null +++ b/tools/data-binding/library/src/main/java/android/databinding/MapChangeRegistry.java @@ -0,0 +1,37 @@ +/* + * 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 android.databinding; + +public class MapChangeRegistry + extends CallbackRegistry<OnMapChangedListener, ObservableMap, Object> { + + private static NotifierCallback<OnMapChangedListener, ObservableMap, Object> NOTIFIER_CALLBACK = + new NotifierCallback<OnMapChangedListener, ObservableMap, Object>() { + @Override + public void onNotifyCallback(OnMapChangedListener callback, ObservableMap sender, + int arg, Object arg2) { + callback.onMapChanged(sender, arg2); + } + }; + + public MapChangeRegistry() { + super(NOTIFIER_CALLBACK); + } + + public void notifyChange(ObservableMap sender, Object key) { + notifyCallbacks(sender, 0, key); + } +} diff --git a/tools/data-binding/library/src/main/java/android/databinding/ObservableArrayList.java b/tools/data-binding/library/src/main/java/android/databinding/ObservableArrayList.java new file mode 100644 index 0000000..d70bc68 --- /dev/null +++ b/tools/data-binding/library/src/main/java/android/databinding/ObservableArrayList.java @@ -0,0 +1,124 @@ +/* + * 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 android.databinding; + +import java.util.ArrayList; +import java.util.Collection; + +public class ObservableArrayList<T> extends ArrayList<T> implements ObservableList<T> { + private ListChangeRegistry mListeners = new ListChangeRegistry(); + + @Override + public void addOnListChangedListener(OnListChangedListener listener) { + if (mListeners == null) { + mListeners = new ListChangeRegistry(); + } + mListeners.add(listener); + } + + @Override + public void removeOnListChangedListener(OnListChangedListener listener) { + if (mListeners != null) { + mListeners.remove(listener); + } + } + + @Override + public boolean add(T object) { + super.add(object); + notifyAdd(size() - 1, 1); + return true; + } + + @Override + public void add(int index, T object) { + super.add(index, object); + notifyAdd(index, 1); + } + + @Override + public boolean addAll(Collection<? extends T> collection) { + int oldSize = size(); + boolean added = super.addAll(collection); + if (added) { + notifyAdd(oldSize, size() - oldSize); + } + return added; + } + + @Override + public boolean addAll(int index, Collection<? extends T> collection) { + boolean added = super.addAll(index, collection); + if (added) { + notifyAdd(index, collection.size()); + } + return added; + } + + @Override + public void clear() { + int oldSize = size(); + super.clear(); + if (oldSize != 0) { + notifyRemove(0, oldSize); + } + } + + @Override + public T remove(int index) { + T val = super.remove(index); + notifyRemove(index, 1); + return val; + } + + @Override + public boolean remove(Object object) { + int index = indexOf(object); + if (index >= 0) { + remove(index); + return true; + } else { + return false; + } + } + + @Override + public T set(int index, T object) { + T val = super.set(index, object); + if (mListeners != null) { + mListeners.notifyChanged(this, index, 1); + } + return val; + } + + @Override + protected void removeRange(int fromIndex, int toIndex) { + super.removeRange(fromIndex, toIndex); + notifyRemove(fromIndex, toIndex - fromIndex); + } + + private void notifyAdd(int start, int count) { + if (mListeners != null) { + mListeners.notifyInserted(this, start, count); + } + } + + private void notifyRemove(int start, int count) { + if (mListeners != null) { + mListeners.notifyRemoved(this, start, count); + } + } +} diff --git a/tools/data-binding/library/src/main/java/android/databinding/ObservableArrayMap.java b/tools/data-binding/library/src/main/java/android/databinding/ObservableArrayMap.java new file mode 100644 index 0000000..c8b57b7 --- /dev/null +++ b/tools/data-binding/library/src/main/java/android/databinding/ObservableArrayMap.java @@ -0,0 +1,107 @@ +/* + * 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 android.databinding; + +import android.support.v4.util.ArrayMap; + +import java.util.Collection; + +public class ObservableArrayMap<K, V> extends ArrayMap<K, V> implements ObservableMap<K, V> { + + private MapChangeRegistry mListeners; + + @Override + public void addOnMapChangedListener( + OnMapChangedListener<? extends ObservableMap<K, V>, K> listener) { + if (mListeners == null) { + mListeners = new MapChangeRegistry(); + } + mListeners.add(listener); + } + + @Override + public void removeOnMapChangedListener( + OnMapChangedListener<? extends ObservableMap<K, V>, K> listener) { + if (mListeners != null) { + mListeners.remove(listener); + } + } + + @Override + public void clear() { + boolean wasEmpty = isEmpty(); + if (!wasEmpty) { + super.clear(); + notifyChange(null); + } + } + + public V put(K k, V v) { + V val = super.put(k, v); + notifyChange(k); + return v; + } + + @Override + public boolean removeAll(Collection<?> collection) { + boolean removed = false; + for (Object key : collection) { + int index = indexOfKey(key); + if (index >= 0) { + removed = true; + removeAt(index); + } + } + return removed; + } + + @Override + public boolean retainAll(Collection<?> collection) { + boolean removed = false; + for (int i = size() - 1; i >= 0; i--) { + Object key = keyAt(i); + if (!collection.contains(key)) { + removeAt(i); + removed = true; + } + } + return removed; + } + + @Override + public V removeAt(int index) { + K key = keyAt(index); + V value = super.removeAt(index); + if (value != null) { + notifyChange(key); + } + return value; + } + + @Override + public V setValueAt(int index, V value) { + K key = keyAt(index); + V oldValue = super.setValueAt(index, value); + notifyChange(key); + return oldValue; + } + + private void notifyChange(Object key) { + if (mListeners != null) { + mListeners.notifyCallbacks(this, 0, key); + } + } +} diff --git a/tools/data-binding/library/src/main/java/android/databinding/ObservableBoolean.java b/tools/data-binding/library/src/main/java/android/databinding/ObservableBoolean.java new file mode 100644 index 0000000..afead95 --- /dev/null +++ b/tools/data-binding/library/src/main/java/android/databinding/ObservableBoolean.java @@ -0,0 +1,29 @@ +/* + * 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 android.databinding; + +public class ObservableBoolean extends BaseObservable { + private boolean mValue; + + public boolean get() { + return mValue; + } + + public void set(boolean value) { + mValue = value; + notifyChange(); + } +} diff --git a/tools/data-binding/library/src/main/java/android/databinding/ObservableByte.java b/tools/data-binding/library/src/main/java/android/databinding/ObservableByte.java new file mode 100644 index 0000000..3ee73a5 --- /dev/null +++ b/tools/data-binding/library/src/main/java/android/databinding/ObservableByte.java @@ -0,0 +1,29 @@ +/* + * 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 android.databinding; + +public class ObservableByte extends BaseObservable { + private byte mValue; + + public byte get() { + return mValue; + } + + public void set(byte value) { + mValue = value; + notifyChange(); + } +} diff --git a/tools/data-binding/library/src/main/java/android/databinding/ObservableChar.java b/tools/data-binding/library/src/main/java/android/databinding/ObservableChar.java new file mode 100644 index 0000000..f78231c --- /dev/null +++ b/tools/data-binding/library/src/main/java/android/databinding/ObservableChar.java @@ -0,0 +1,29 @@ +/* + * 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 android.databinding; + +public class ObservableChar extends BaseObservable { + private char mValue; + + public char get() { + return mValue; + } + + public void set(char value) { + mValue = value; + notifyChange(); + } +} diff --git a/tools/data-binding/library/src/main/java/android/databinding/ObservableDouble.java b/tools/data-binding/library/src/main/java/android/databinding/ObservableDouble.java new file mode 100644 index 0000000..ee7bd05 --- /dev/null +++ b/tools/data-binding/library/src/main/java/android/databinding/ObservableDouble.java @@ -0,0 +1,29 @@ +/* + * 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 android.databinding; + +public class ObservableDouble extends BaseObservable { + private double mValue; + + public double get() { + return mValue; + } + + public void set(double value) { + mValue = value; + notifyChange(); + } +} diff --git a/tools/data-binding/library/src/main/java/android/databinding/ObservableField.java b/tools/data-binding/library/src/main/java/android/databinding/ObservableField.java new file mode 100644 index 0000000..05a61b7 --- /dev/null +++ b/tools/data-binding/library/src/main/java/android/databinding/ObservableField.java @@ -0,0 +1,29 @@ +/* + * 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 android.databinding; + +public class ObservableField<T> extends BaseObservable { + private T mValue; + + public T get() { + return mValue; + } + + public void set(T value) { + mValue = value; + notifyChange(); + } +} diff --git a/tools/data-binding/library/src/main/java/android/databinding/ObservableFloat.java b/tools/data-binding/library/src/main/java/android/databinding/ObservableFloat.java new file mode 100644 index 0000000..e8a063a --- /dev/null +++ b/tools/data-binding/library/src/main/java/android/databinding/ObservableFloat.java @@ -0,0 +1,29 @@ +/* + * 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 android.databinding; + +public class ObservableFloat extends BaseObservable { + private float mValue; + + public float get() { + return mValue; + } + + public void set(float value) { + mValue = value; + notifyChange(); + } +} diff --git a/tools/data-binding/library/src/main/java/android/databinding/ObservableInt.java b/tools/data-binding/library/src/main/java/android/databinding/ObservableInt.java new file mode 100644 index 0000000..77140f8 --- /dev/null +++ b/tools/data-binding/library/src/main/java/android/databinding/ObservableInt.java @@ -0,0 +1,29 @@ +/* + * 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 android.databinding; + +public class ObservableInt extends BaseObservable { + private int mValue; + + public int get() { + return mValue; + } + + public void set(int value) { + mValue = value; + notifyChange(); + } +} diff --git a/tools/data-binding/library/src/main/java/android/databinding/ObservableLong.java b/tools/data-binding/library/src/main/java/android/databinding/ObservableLong.java new file mode 100644 index 0000000..b1621e1 --- /dev/null +++ b/tools/data-binding/library/src/main/java/android/databinding/ObservableLong.java @@ -0,0 +1,29 @@ +/* + * 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 android.databinding; + +public class ObservableLong extends BaseObservable { + private long mValue; + + public long get() { + return mValue; + } + + public void set(long value) { + mValue = value; + notifyChange(); + } +} diff --git a/tools/data-binding/library/src/main/java/android/databinding/ObservableShort.java b/tools/data-binding/library/src/main/java/android/databinding/ObservableShort.java new file mode 100644 index 0000000..6d46705 --- /dev/null +++ b/tools/data-binding/library/src/main/java/android/databinding/ObservableShort.java @@ -0,0 +1,29 @@ +/* + * 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 android.databinding; + +public class ObservableShort extends BaseObservable { + private short mValue; + + public short get() { + return mValue; + } + + public void set(short value) { + mValue = value; + notifyChange(); + } +} diff --git a/tools/data-binding/library/src/main/java/android/databinding/PropertyChangeRegistry.java b/tools/data-binding/library/src/main/java/android/databinding/PropertyChangeRegistry.java new file mode 100644 index 0000000..c675c39 --- /dev/null +++ b/tools/data-binding/library/src/main/java/android/databinding/PropertyChangeRegistry.java @@ -0,0 +1,36 @@ +/* + * Copyright (C) 2014 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 android.databinding; + +public class PropertyChangeRegistry extends + CallbackRegistry<OnPropertyChangedListener, Observable, Void> { + + private static final CallbackRegistry.NotifierCallback<OnPropertyChangedListener, Observable, Void> NOTIFIER_CALLBACK = new CallbackRegistry.NotifierCallback<OnPropertyChangedListener, Observable, Void>() { + @Override + public void onNotifyCallback(OnPropertyChangedListener callback, Observable sender, + int arg, Void notUsed) { + callback.onPropertyChanged(sender, arg); + } + }; + + public PropertyChangeRegistry() { + super(NOTIFIER_CALLBACK); + } + + public void notifyChange(Observable observable, int propertyId) { + notifyCallbacks(observable, propertyId, null); + } +} diff --git a/tools/data-binding/library/src/main/java/android/databinding/ViewDataBinding.java b/tools/data-binding/library/src/main/java/android/databinding/ViewDataBinding.java new file mode 100644 index 0000000..a5ba6e3 --- /dev/null +++ b/tools/data-binding/library/src/main/java/android/databinding/ViewDataBinding.java @@ -0,0 +1,534 @@ +/* + * Copyright (C) 2014 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 android.databinding; + +import com.android.databinding.library.R; + +import android.annotation.TargetApi; +import android.os.Build; +import android.os.Build.VERSION; +import android.os.Build.VERSION_CODES; +import android.util.Log; +import android.util.SparseIntArray; +import android.view.View; +import android.view.View.OnAttachStateChangeListener; +import android.view.ViewGroup; + +import java.lang.ref.WeakReference; + +public abstract class ViewDataBinding { + + /** + * Instead of directly accessing Build.VERSION.SDK_INT, generated code uses this value so that + * we can test API dependent behavior. + */ + static int SDK_INT = VERSION.SDK_INT; + + /** + * Prefix for android:tag on Views with binding. The root View and include tags will not have + * android:tag attributes and will use ids instead. + */ + public static final String BINDING_TAG_PREFIX = "bindingTag"; + + // The length of BINDING_TAG_PREFIX prevents calling length repeatedly. + private static final int BINDING_NUMBER_START = BINDING_TAG_PREFIX.length(); + + // ICS (v 14) fixes a leak when using setTag(int, Object) + private static final boolean USE_TAG_ID = DataBinderMapper.TARGET_MIN_SDK >= 14; + + /** + * Method object extracted out to attach a listener to a bound Observable object. + */ + private static final CreateWeakListener CREATE_PROPERTY_LISTENER = new CreateWeakListener() { + @Override + public WeakListener create(ViewDataBinding viewDataBinding, int localFieldId) { + return new WeakPropertyListener(viewDataBinding, localFieldId); + } + }; + + /** + * Method object extracted out to attach a listener to a bound ObservableList object. + */ + private static final CreateWeakListener CREATE_LIST_LISTENER = new CreateWeakListener() { + @Override + public WeakListener create(ViewDataBinding viewDataBinding, int localFieldId) { + return new WeakListListener(viewDataBinding, localFieldId); + } + }; + + /** + * Method object extracted out to attach a listener to a bound ObservableMap object. + */ + private static final CreateWeakListener CREATE_MAP_LISTENER = new CreateWeakListener() { + @Override + public WeakListener create(ViewDataBinding viewDataBinding, int localFieldId) { + return new WeakMapListener(viewDataBinding, localFieldId); + } + }; + + private static final OnAttachStateChangeListener ROOT_REATTACHED_LISTENER; + + static { + if (VERSION.SDK_INT < VERSION_CODES.KITKAT) { + ROOT_REATTACHED_LISTENER = null; + } else { + ROOT_REATTACHED_LISTENER = new OnAttachStateChangeListener() { + @TargetApi(VERSION_CODES.KITKAT) + @Override + public void onViewAttachedToWindow(View v) { + // execute the pending bindings. + final ViewDataBinding binding; + if (USE_TAG_ID) { + binding = (ViewDataBinding) v.getTag(R.id.dataBinding); + } else { + binding = (ViewDataBinding) v.getTag(); + } + v.post(binding.mRebindRunnable); + v.removeOnAttachStateChangeListener(this); + } + + @Override + public void onViewDetachedFromWindow(View v) { + } + }; + } + } + + /** + * Runnable executed on animation heartbeat to rebind the dirty Views. + */ + private Runnable mRebindRunnable = new Runnable() { + @Override + public void run() { + if (mPendingRebind) { + boolean rebind = true; + if (VERSION.SDK_INT >= VERSION_CODES.KITKAT) { + rebind = mRoot.isAttachedToWindow(); + if (!rebind) { + // Don't execute the pending bindings until the View + // is attached again. + mRoot.addOnAttachStateChangeListener(ROOT_REATTACHED_LISTENER); + } + } + if (rebind) { + mPendingRebind = false; + executePendingBindings(); + } + } + } + }; + + /** + * Flag indicates that there are pending bindings that need to be reevaluated. + */ + private boolean mPendingRebind = false; + + /** + * The observed expressions. + */ + private WeakListener[] mLocalFieldObservers; + + /** + * The root View that this Binding is associated with. + */ + private final View mRoot; + + protected ViewDataBinding(View root, int localFieldCount) { + mLocalFieldObservers = new WeakListener[localFieldCount]; + this.mRoot = root; + if (USE_TAG_ID) { + this.mRoot.setTag(R.id.dataBinding, this); + } else { + this.mRoot.setTag(this); + } + } + + public static int getBuildSdkInt() { + return SDK_INT; + } + + /** + * Called when an observed object changes. Sets the appropriate dirty flag if applicable. + * @param localFieldId The index into mLocalFieldObservers that this Object resides in. + * @param object The object that has changed. + * @param fieldId The BR ID of the field being changed or _all if + * no specific field is being notified. + * @return true if this change should cause a change to the UI. + */ + protected abstract boolean onFieldChange(int localFieldId, Object object, int fieldId); + + public abstract boolean setVariable(int variableId, Object variable); + + /** + * Evaluates the pending bindings, updating any Views that have expressions bound to + * modified variables. This <b>must</b> be run on the UI thread. + */ + public abstract void executePendingBindings(); + + /** + * Used internally to invalidate flags of included layouts. + */ + public abstract void invalidateAll(); + + /** + * Removes binding listeners to expression variables. + */ + public void unbind() { + for (WeakListener weakListener : mLocalFieldObservers) { + if (weakListener != null) { + weakListener.unregister(); + } + } + } + + @Override + protected void finalize() throws Throwable { + unbind(); + } + + /** + * Returns the outermost View in the layout file associated with the Binding. + * @return the outermost View in the layout file associated with the Binding. + */ + public View getRoot() { + return mRoot; + } + + private void handleFieldChange(int mLocalFieldId, Object object, int fieldId) { + boolean result = onFieldChange(mLocalFieldId, object, fieldId); + if (result) { + requestRebind(); + } + } + + protected boolean unregisterFrom(int localFieldId) { + WeakListener listener = mLocalFieldObservers[localFieldId]; + if (listener != null) { + return listener.unregister(); + } + return false; + } + + protected void requestRebind() { + if (mPendingRebind) { + return; + } + mPendingRebind = true; + if (VERSION.SDK_INT >= VERSION_CODES.JELLY_BEAN) { + mRoot.postOnAnimation(mRebindRunnable); + } else { + mRoot.post(mRebindRunnable); + } + } + + protected Object getObservedField(int localFieldId) { + WeakListener listener = mLocalFieldObservers[localFieldId]; + if (listener == null) { + return null; + } + return listener.getTarget(); + } + + private boolean updateRegistration(int localFieldId, Object observable, + CreateWeakListener listenerCreator) { + if (observable == null) { + return unregisterFrom(localFieldId); + } + WeakListener listener = mLocalFieldObservers[localFieldId]; + if (listener == null) { + registerTo(localFieldId, observable, listenerCreator); + return true; + } + if (listener.getTarget() == observable) { + return false;//nothing to do, same object + } + unregisterFrom(localFieldId); + registerTo(localFieldId, observable, listenerCreator); + return true; + } + + protected boolean updateRegistration(int localFieldId, Observable observable) { + return updateRegistration(localFieldId, observable, CREATE_PROPERTY_LISTENER); + } + + protected boolean updateRegistration(int localFieldId, ObservableList observable) { + return updateRegistration(localFieldId, observable, CREATE_LIST_LISTENER); + } + + protected boolean updateRegistration(int localFieldId, ObservableMap observable) { + return updateRegistration(localFieldId, observable, CREATE_MAP_LISTENER); + } + + protected void registerTo(int localFieldId, Object observable, + CreateWeakListener listenerCreator) { + if (observable == null) { + return; + } + WeakListener listener = mLocalFieldObservers[localFieldId]; + if (listener == null) { + listener = listenerCreator.create(this, localFieldId); + mLocalFieldObservers[localFieldId] = listener; + } + listener.setTarget(observable); + } + + /** + * Walk all children of root and assign tagged Views to associated indices in views. + * + * @param root The base of the View hierarchy to walk. + * @param views An array of all Views with binding expressions and all Views with IDs. This + * will start empty and will contain the found Views when this method completes. + * @param includes A mapping of include tag IDs to the index into the views array. + * @param viewsWithIds A mapping of views with IDs but without expressions to the index + * into the views array. + */ + private static void mapTaggedChildViews(View root, View[] views, SparseIntArray includes, + SparseIntArray viewsWithIds) { + if (root instanceof ViewGroup) { + ViewGroup viewGroup = (ViewGroup) root; + for (int i = viewGroup.getChildCount() - 1; i >= 0; i--) { + mapTaggedViews(viewGroup.getChildAt(i), views, includes, viewsWithIds); + } + } + } + + private static void mapTaggedViews(View view, View[] views, SparseIntArray includes, + SparseIntArray viewsWithIds) { + boolean visitChildren = true; + String tag = (String) view.getTag(); + if (tag != null && tag.startsWith(BINDING_TAG_PREFIX)) { + int tagIndex = parseTagInt(tag); + views[tagIndex] = view; + } else { + visitChildren = addViewWithId(view, views, includes, viewsWithIds); + } + if (visitChildren) { + mapTaggedChildViews(view, views, includes, viewsWithIds); + } + } + + /** + * Walks the view hierarchy under root and pulls out tagged Views, includes, and views with + * IDs into a View[] that is returned. This is used to walk the view hierarchy once to find + * all bound and ID'd views. + * + * @param root The root of the view hierarchy to walk. + * @param numViews The total number of ID'd views and views with expressions. + * @param includes Views that are considered includes and should be treated as separate + * binders. + * @param viewsWithIds Views that don't have tags, but have IDs. + * @return An array of size numViews containing all Views in the hierarchy that have IDs + * (with elements in viewsWithIds) or are tagged containing expressions. + */ + protected static View[] mapChildViews(View root, int numViews, SparseIntArray includes, + SparseIntArray viewsWithIds) { + View[] views = new View[numViews]; + boolean visitChildren = addViewWithId(root, views, includes, viewsWithIds); + if (visitChildren) { + mapTaggedChildViews(root, views, includes, viewsWithIds); + } + return views; + } + + private static boolean addViewWithId(View view, View[] views, SparseIntArray includes, + SparseIntArray viewsWithIds) { + final int id = view.getId(); + boolean visitChildren = true; + if (id > 0) { + int index; + if (viewsWithIds != null && (index = viewsWithIds.get(id, -1)) >= 0) { + views[index] = view; + } else if (includes != null && (index = includes.get(id, -1)) >= 0) { + views[index] = view; + visitChildren = false; + } + } + return visitChildren; + } + + /** + * Parse the tag without creating a new String object. This is fast and assumes the + * tag is in the correct format. + * @param str The tag string. + * @return The binding tag number parsed from the tag string. + */ + private static int parseTagInt(String str) { + final int end = str.length(); + int val = 0; + for (int i = BINDING_NUMBER_START; i < end; i++) { + val *= 10; + char c = str.charAt(i); + val += (c - '0'); + } + return val; + } + + private static abstract class WeakListener<T> { + private final WeakReference<ViewDataBinding> mBinder; + protected final int mLocalFieldId; + private T mTarget; + + public WeakListener(ViewDataBinding binder, int localFieldId) { + mBinder = new WeakReference<ViewDataBinding>(binder); + mLocalFieldId = localFieldId; + } + + public void setTarget(T object) { + unregister(); + mTarget = object; + if (mTarget != null) { + addListener(mTarget); + } + } + + public boolean unregister() { + boolean unregistered = false; + if (mTarget != null) { + removeListener(mTarget); + unregistered = true; + } + mTarget = null; + return unregistered; + } + + public T getTarget() { + return mTarget; + } + + protected ViewDataBinding getBinder() { + ViewDataBinding binder = mBinder.get(); + if (binder == null) { + unregister(); // The binder is dead + } + return binder; + } + + protected abstract void addListener(T target); + protected abstract void removeListener(T target); + } + + private static class WeakPropertyListener extends WeakListener<Observable> + implements OnPropertyChangedListener { + public WeakPropertyListener(ViewDataBinding binder, int localFieldId) { + super(binder, localFieldId); + } + + @Override + protected void addListener(Observable target) { + target.addOnPropertyChangedListener(this); + } + + @Override + protected void removeListener(Observable target) { + target.removeOnPropertyChangedListener(this); + } + + @Override + public void onPropertyChanged(Observable sender, int fieldId) { + ViewDataBinding binder = getBinder(); + if (binder == null) { + return; + } + Observable obj = getTarget(); + if (obj != sender) { + return; // notification from the wrong object? + } + binder.handleFieldChange(mLocalFieldId, sender, fieldId); + } + } + + private static class WeakListListener extends WeakListener<ObservableList> + implements OnListChangedListener { + + public WeakListListener(ViewDataBinding binder, int localFieldId) { + super(binder, localFieldId); + } + + @Override + public void onChanged() { + ViewDataBinding binder = getBinder(); + if (binder == null) { + return; + } + ObservableList target = getTarget(); + if (target == null) { + return; // We don't expect any notifications from null targets + } + binder.handleFieldChange(mLocalFieldId, target, 0); + } + + @Override + public void onItemRangeChanged(int positionStart, int itemCount) { + onChanged(); + } + + @Override + public void onItemRangeInserted(int positionStart, int itemCount) { + onChanged(); + } + + @Override + public void onItemRangeMoved(int fromPosition, int toPosition, int itemCount) { + onChanged(); + } + + @Override + public void onItemRangeRemoved(int positionStart, int itemCount) { + onChanged(); + } + + @Override + protected void addListener(ObservableList target) { + target.addOnListChangedListener(this); + } + + @Override + protected void removeListener(ObservableList target) { + target.removeOnListChangedListener(this); + } + } + + private static class WeakMapListener extends WeakListener<ObservableMap> + implements OnMapChangedListener { + public WeakMapListener(ViewDataBinding binder, int localFieldId) { + super(binder, localFieldId); + } + + @Override + protected void addListener(ObservableMap target) { + target.addOnMapChangedListener(this); + } + + @Override + protected void removeListener(ObservableMap target) { + target.removeOnMapChangedListener(this); + } + + @Override + public void onMapChanged(ObservableMap sender, Object key) { + ViewDataBinding binder = getBinder(); + if (binder == null || sender != getTarget()) { + return; + } + binder.handleFieldChange(mLocalFieldId, sender, 0); + } + } + + private interface CreateWeakListener { + WeakListener create(ViewDataBinding viewDataBinding, int localFieldId); + } +} diff --git a/tools/data-binding/library/src/main/java/android/databinding/ViewStubProxy.java b/tools/data-binding/library/src/main/java/android/databinding/ViewStubProxy.java new file mode 100644 index 0000000..01b878a --- /dev/null +++ b/tools/data-binding/library/src/main/java/android/databinding/ViewStubProxy.java @@ -0,0 +1,102 @@ +/* + * 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 android.databinding; + +import android.view.View; +import android.view.ViewStub; +import android.view.ViewStub.OnInflateListener; + +/** + * This class represents a ViewStub before and after inflation. Before inflation, + * the ViewStub is accessible. After inflation, the ViewDataBinding is accessible + * if the inflated View has bindings. If not, the root View will be accessible. + */ +public class ViewStubProxy { + private ViewStub mViewStub; + private ViewDataBinding mViewDataBinding; + private View mRoot; + private OnInflateListener mOnInflateListener; + private ViewDataBinding mContainingBinding; + + private OnInflateListener mProxyListener = new OnInflateListener() { + @Override + public void onInflate(ViewStub stub, View inflated) { + mRoot = inflated; + mViewDataBinding = DataBindingUtil.bindTo(inflated, stub.getLayoutResource()); + mViewStub = null; + + if (mOnInflateListener != null) { + mOnInflateListener.onInflate(stub, inflated); + mOnInflateListener = null; + } + mContainingBinding.invalidateAll(); + mContainingBinding.executePendingBindings(); + } + }; + + public ViewStubProxy(ViewStub viewStub) { + mViewStub = viewStub; + mViewStub.setOnInflateListener(mProxyListener); + } + + public void setContainingBinding(ViewDataBinding containingBinding) { + mContainingBinding = containingBinding; + } + + /** + * @return <code>true</code> if the ViewStub has replaced itself with the inflated layout + * or <code>false</code> if not. + */ + public boolean isInflated() { + return mRoot != null; + } + + /** + * @return The root View of the layout replacing the ViewStub once it has been inflated. + * <code>null</code> is returned prior to inflation. + */ + public View getRoot() { + return mRoot; + } + + /** + * @return The data binding associated with the inflated layout once it has been inflated. + * <code>null</code> prior to inflation or if there is no binding associated with the layout. + */ + public ViewDataBinding getBinding() { + return mViewDataBinding; + } + + /** + * @return The ViewStub in the layout or <code>null</code> if the ViewStub has been inflated. + */ + public ViewStub getViewStub() { + return mViewStub; + } + + /** + * Sets the {@link OnInflateListener} to be called when the ViewStub inflates. The proxy must + * have an OnInflateListener, so <code>listener</code> will be called immediately after + * the proxy's listener is called. + * + * @param listener The OnInflateListener to notify of successful inflation + */ + public void setOnInflateListener(OnInflateListener listener) { + if (mViewStub != null) { + mOnInflateListener = listener; + } + } +} diff --git a/tools/data-binding/library/src/main/res/values/ids.xml b/tools/data-binding/library/src/main/res/values/ids.xml new file mode 100644 index 0000000..a7b89d3 --- /dev/null +++ b/tools/data-binding/library/src/main/res/values/ids.xml @@ -0,0 +1,4 @@ +<?xml version="1.0" encoding="utf-8"?> +<resources> + <item type="id" name="dataBinding"/> +</resources>
\ No newline at end of file diff --git a/tools/data-binding/samples/BindingDemo/.gitignore b/tools/data-binding/samples/BindingDemo/.gitignore new file mode 100644 index 0000000..afbdab3 --- /dev/null +++ b/tools/data-binding/samples/BindingDemo/.gitignore @@ -0,0 +1,6 @@ +.gradle +/local.properties +/.idea/workspace.xml +/.idea/libraries +.DS_Store +/build diff --git a/tools/data-binding/samples/BindingDemo/app/.gitignore b/tools/data-binding/samples/BindingDemo/app/.gitignore new file mode 100644 index 0000000..796b96d --- /dev/null +++ b/tools/data-binding/samples/BindingDemo/app/.gitignore @@ -0,0 +1 @@ +/build diff --git a/tools/data-binding/samples/BindingDemo/app/build.gradle b/tools/data-binding/samples/BindingDemo/app/build.gradle new file mode 100644 index 0000000..fa54aab --- /dev/null +++ b/tools/data-binding/samples/BindingDemo/app/build.gradle @@ -0,0 +1,72 @@ +/* + * Copyright (C) 2014 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. + */ + +apply plugin: 'com.android.application' +apply plugin: 'com.android.databinding' + +def generatedSources = "$buildDir/generated/source/br" + +android { + compileSdkVersion 21 + buildToolsVersion "21.1.1" + + defaultConfig { + applicationId "com.android.bindingdemo" + minSdkVersion 15 + targetSdkVersion 21 + versionCode 1 + versionName "1.0" + } + compileOptions { + sourceCompatibility JavaVersion.VERSION_1_7 + targetCompatibility JavaVersion.VERSION_1_7 + } + buildTypes { + release { + minifyEnabled false + proguardFiles getDefaultProguardFile('proguard-android.txt'), 'proguard-rules.pro' + } + } + packagingOptions { + exclude 'META-INF/services/javax.annotation.processing.Processor' + } +} + +android.applicationVariants.all { variant -> + variant.javaCompile.doFirst { + println "*** compile doFirst ${variant.name}" + new File(generatedSources).mkdirs() + variant.javaCompile.options.compilerArgs += [ + '-s', generatedSources + ] + } +} + +dependencies { + compile fileTree(dir: 'libs', include: ['*.jar']) + compile 'com.android.support:appcompat-v7:21.+' + compile 'com.android.databinding:library:0.3-SNAPSHOT@aar' + compile 'com.android.support:recyclerview-v7:21.0.2' + compile 'com.android.support:gridlayout-v7:21.+' + compile 'com.android.support:cardview-v7:21.+' + compile 'com.android.databinding:baseLibrary:0.3-SNAPSHOT' + compile 'com.android.databinding:adapters:0.3-SNAPSHOT' + provided 'com.android.databinding:annotationprocessor:0.3-SNAPSHOT' + provided fileTree(dir : 'build/databinder/src', include : ['*.java']) + + testCompile 'junit:junit:4.12' + testCompile 'org.mockito:mockito-core:1.9.5' +} diff --git a/tools/data-binding/samples/BindingDemo/app/proguard-rules.pro b/tools/data-binding/samples/BindingDemo/app/proguard-rules.pro new file mode 100644 index 0000000..b7210d1 --- /dev/null +++ b/tools/data-binding/samples/BindingDemo/app/proguard-rules.pro @@ -0,0 +1,17 @@ +# Add project specific ProGuard rules here. +# By default, the flags in this file are appended to flags specified +# in /Users/yboyar/android/sdk/tools/proguard/proguard-android.txt +# You can edit the include path and order by changing the proguardFiles +# directive in build.gradle. +# +# For more details, see +# http://developer.android.com/guide/developing/tools/proguard.html + +# Add any project specific keep options here: + +# If your project uses WebView with JS, uncomment the following +# and specify the fully qualified class name to the JavaScript interface +# class: +#-keepclassmembers class fqcn.of.javascript.interface.for.webview { +# public *; +#} diff --git a/tools/data-binding/samples/BindingDemo/app/src/main/AndroidManifest.xml b/tools/data-binding/samples/BindingDemo/app/src/main/AndroidManifest.xml new file mode 100644 index 0000000..3455d7c --- /dev/null +++ b/tools/data-binding/samples/BindingDemo/app/src/main/AndroidManifest.xml @@ -0,0 +1,37 @@ +<?xml version="1.0" encoding="utf-8"?> +<!-- + Copyright (C) 2014 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. + --> + +<manifest xmlns:android="http://schemas.android.com/apk/res/android" + package="com.android.example.bindingdemo" > + + <application + android:allowBackup="true" + android:icon="@drawable/ic_launcher" + android:label="@string/app_name" + android:theme="@style/AppTheme" > + <activity + android:name=".MainActivity" + android:label="@string/app_name" > + <intent-filter> + <action android:name="android.intent.action.MAIN" /> + + <category android:name="android.intent.category.LAUNCHER" /> + </intent-filter> + </activity> + </application> + +</manifest> diff --git a/tools/data-binding/samples/BindingDemo/app/src/main/java/com/android/example/bindingdemo/DataBoundAdapter.java b/tools/data-binding/samples/BindingDemo/app/src/main/java/com/android/example/bindingdemo/DataBoundAdapter.java new file mode 100644 index 0000000..89eef68 --- /dev/null +++ b/tools/data-binding/samples/BindingDemo/app/src/main/java/com/android/example/bindingdemo/DataBoundAdapter.java @@ -0,0 +1,31 @@ +package com.android.example.bindingdemo; + +import android.support.v7.widget.RecyclerView; +import android.view.ViewGroup; + +import android.databinding.DataBindingUtil; +import android.databinding.ViewDataBinding; + +abstract public class DataBoundAdapter<T extends ViewDataBinding> + extends RecyclerView.Adapter<DataBoundAdapter.DataBoundViewHolder<T>> { + final int mLayoutId; + final Class<T> mBinderInterface; + public DataBoundAdapter(int mLayoutId, Class<T> mBinderInterface) { + this.mLayoutId = mLayoutId; + this.mBinderInterface = mBinderInterface; + } + + @Override + public DataBoundAdapter.DataBoundViewHolder<T> onCreateViewHolder(ViewGroup viewGroup, int type) { + T binder = DataBindingUtil.inflate(viewGroup.getContext(), mLayoutId, viewGroup, false); + return new DataBoundViewHolder(binder); + } + + static class DataBoundViewHolder<T extends ViewDataBinding> extends RecyclerView.ViewHolder { + public final T dataBinder; + public DataBoundViewHolder(T mViewBinder) { + super(mViewBinder.getRoot()); + this.dataBinder = mViewBinder; + } + } +} diff --git a/tools/data-binding/samples/BindingDemo/app/src/main/java/com/android/example/bindingdemo/MainActivity.java b/tools/data-binding/samples/BindingDemo/app/src/main/java/com/android/example/bindingdemo/MainActivity.java new file mode 100644 index 0000000..bfd5e4a --- /dev/null +++ b/tools/data-binding/samples/BindingDemo/app/src/main/java/com/android/example/bindingdemo/MainActivity.java @@ -0,0 +1,225 @@ +package com.android.example.bindingdemo; + +import android.databinding.Bindable; +import android.databinding.Observable; +import android.databinding.OnPropertyChangedListener; +import android.support.v7.app.ActionBarActivity; +import android.os.Bundle; +import android.support.v7.widget.LinearLayoutManager; +import android.support.v7.widget.RecyclerView; +import android.view.Menu; +import android.view.MenuItem; +import android.view.View; +import android.view.ViewGroup; + +import android.databinding.DataBindingUtil; +import android.databinding.PropertyChangeRegistry; +import com.android.example.bindingdemo.databinding.ListItemBinding; +import com.android.example.bindingdemo.databinding.MainActivityBinding; +import com.android.example.bindingdemo.vo.User; +import com.android.example.bindingdemo.vo.Users; + +import java.util.ArrayList; +import java.util.Arrays; +import java.util.List; + +import com.android.example.bindingdemo.BR; +public class MainActivity extends ActionBarActivity implements Observable { + @Bindable + UserAdapter tkAdapter; + @Bindable + UserAdapter robotAdapter; + @Bindable + MainActivityBinding dataBinder; + @Bindable + User selected; + + @Bindable + User selected2; + + private final PropertyChangeRegistry mListeners = new PropertyChangeRegistry(); + + public User getSelected2() { + return selected2; + } + + @Override + protected void onCreate(Bundle savedInstanceState) { + super.onCreate(savedInstanceState); + dataBinder = MainActivityBinding.inflate(this); + setContentView(dataBinder.getRoot()); + dataBinder.robotList.setHasFixedSize(true); + dataBinder.toolkittyList.setHasFixedSize(true); + tkAdapter = new UserAdapter(Users.toolkities); + robotAdapter = new UserAdapter(Users.robots); + dataBinder.toolkittyList.setLayoutManager(new LinearLayoutManager(this, LinearLayoutManager.HORIZONTAL, false)); + dataBinder.robotList.setLayoutManager(new LinearLayoutManager(this, LinearLayoutManager.HORIZONTAL, false)); + dataBinder.setActivity(this); + dataBinder.executePendingBindings(); + } + + public UserAdapter getTkAdapter() { + return tkAdapter; + } + + public UserAdapter getRobotAdapter() { + return robotAdapter; + } + + public User getSelected() { + return selected; + } + + private void setSelected(User selected) { + if (selected == this.selected) { + return; + } + this.selected = selected; + mListeners.notifyChange(this, BR.selected); + } + + @Bindable + public View.OnClickListener onSave = new View.OnClickListener() { + @Override + public void onClick(View v) { + if (selected == null) { + return; + } + selected.setName(dataBinder.selectedName.getText().toString()); + selected.setLastName(dataBinder.selectedLastname.getText().toString()); + } + }; + + @Bindable + public View.OnClickListener onUnselect = new View.OnClickListener() { + + @Override + public void onClick(View v) { + setSelected(null); + } + }; + + @Bindable + public View.OnClickListener onDelete = new View.OnClickListener() { + @Override + public void onClick(View v) { + if (selected == null) { + return; + } + if (selected.getGroup() == User.TOOLKITTY) { + tkAdapter.remove(selected); + selected.setGroup(User.ROBOT); + robotAdapter.add(selected); + dataBinder.robotList.smoothScrollToPosition(robotAdapter.getItemCount() - 1); + } else { + tkAdapter.add(selected); + dataBinder.toolkittyList.smoothScrollToPosition(tkAdapter.getItemCount() - 1); + selected.setGroup(User.TOOLKITTY); + robotAdapter.remove(selected); + } + } + }; + + + @Override + public boolean onCreateOptionsMenu(Menu menu) { + // Inflate the menu; this adds items to the action bar if it is present. + getMenuInflater().inflate(R.menu.menu_main, menu); + return true; + } + + @Override + public boolean onOptionsItemSelected(MenuItem item) { + // Handle action bar item clicks here. The action bar will + // automatically handle clicks on the Home/Up button, so long + // as you specify a parent activity in AndroidManifest.xml. + int id = item.getItemId(); + + //noinspection SimplifiableIfStatement + if (id == R.id.action_settings) { + return true; + } + + return super.onOptionsItemSelected(item); + } + + @Override + public void addOnPropertyChangedListener(OnPropertyChangedListener listener) { + mListeners.add(listener); + } + + @Override + public void removeOnPropertyChangedListener(OnPropertyChangedListener listener) { + mListeners.remove(listener); + } + + public class UserAdapter extends DataBoundAdapter<ListItemBinding> implements View.OnClickListener, Observable { + final private List<User> userList = new ArrayList<>(); + final private PropertyChangeRegistry mListeners = new PropertyChangeRegistry(); + + public UserAdapter(User[] toolkities) { + super(R.layout.list_item, ListItemBinding.class); + userList.addAll(Arrays.asList(toolkities)); + } + + @Override + public DataBoundViewHolder<ListItemBinding> onCreateViewHolder(ViewGroup viewGroup, int type) { + DataBoundViewHolder<ListItemBinding> vh = super.onCreateViewHolder(viewGroup, type); + vh.dataBinder.setClickListener(this); + return vh; + } + + @Override + public void onBindViewHolder(DataBoundViewHolder<ListItemBinding> vh, int index) { + vh.dataBinder.setUser(userList.get(index)); + vh.dataBinder.executePendingBindings(); + } + + @Bindable + @Override + public int getItemCount() { + return userList.size(); + } + + public void add(User user) { + if (userList.contains(user)) { + return; + } + userList.add(user); + notifyItemInserted(userList.size() - 1); + mListeners.notifyChange(this, BR.itemCount); + } + + public void remove(User user) { + int i = userList.indexOf(user); + if (i < 0) { + return; + } + userList.remove(i); + notifyItemRemoved(i); + mListeners.notifyChange(this, BR.itemCount); + } + + @Override + public void onClick(View v) { + RecyclerView.LayoutParams lp = (RecyclerView.LayoutParams) v.getLayoutParams(); + final int pos = lp.getViewPosition(); + if (pos > -1 && pos < userList.size()) { + v.requestFocus(); + setSelected(userList.get(pos)); + } else { + setSelected(null); + } + } + + @Override + public void addOnPropertyChangedListener(OnPropertyChangedListener listener) { + mListeners.add(listener); + } + + @Override + public void removeOnPropertyChangedListener(OnPropertyChangedListener listener) { + mListeners.remove(listener); + } + } +} diff --git a/tools/data-binding/samples/BindingDemo/app/src/main/java/com/android/example/bindingdemo/vo/User.java b/tools/data-binding/samples/BindingDemo/app/src/main/java/com/android/example/bindingdemo/vo/User.java new file mode 100644 index 0000000..5b7e5c7 --- /dev/null +++ b/tools/data-binding/samples/BindingDemo/app/src/main/java/com/android/example/bindingdemo/vo/User.java @@ -0,0 +1,83 @@ +package com.android.example.bindingdemo.vo; + +import android.databinding.Bindable; +import android.graphics.Color; + +import android.databinding.BaseObservable; +import com.android.example.bindingdemo.BR; + +import java.util.Objects; + +public class User extends BaseObservable { + @Bindable + private String name; + @Bindable + private String lastName; + @Bindable + private int photoResource = 0; + @Bindable + private int favoriteColor = Color.RED; + @Bindable + private int group; + public static final int TOOLKITTY = 1; + public static final int ROBOT = 2; + + public User(String name, String lastName, int photoResource, int group) { + this.name = name; + this.lastName = lastName; + this.photoResource = photoResource; + this.group = group; + } + + public void setGroup(int group) { + if (this.group == group) { + return; + } + this.group = group; + notifyPropertyChanged(BR.group); + } + + public int getGroup() { + return group; + } + + public String getName() { + return name; + } + + public void setName(String name) { + if (Objects.equals(name, this.name)) { + return; + } + this.name = name; + notifyPropertyChanged(BR.name); + } + + public String getLastName() { + return lastName; + } + + public void setLastName(String lastName) { + if (Objects.equals(lastName, this.lastName)) { + return; + } + this.lastName = lastName; + notifyPropertyChanged(BR.lastName); + } + + public int getPhotoResource() { + return photoResource; + } + + public void setPhotoResource(int photoResource) { + if (this.photoResource == photoResource) { + return; + } + this.photoResource = photoResource; + notifyPropertyChanged(BR.photoResource); + } + + public int getFavoriteColor() { + return favoriteColor; + } +} diff --git a/tools/data-binding/samples/BindingDemo/app/src/main/java/com/android/example/bindingdemo/vo/Users.java b/tools/data-binding/samples/BindingDemo/app/src/main/java/com/android/example/bindingdemo/vo/Users.java new file mode 100644 index 0000000..d954888 --- /dev/null +++ b/tools/data-binding/samples/BindingDemo/app/src/main/java/com/android/example/bindingdemo/vo/Users.java @@ -0,0 +1,22 @@ +package com.android.example.bindingdemo.vo; + +import com.android.example.bindingdemo.R; + +public class Users { + public static final User[] robots = new User[]{ + new User("romain", "guy", R.drawable.romain, User.ROBOT), + }; + public static final User[] toolkities = new User[]{ + new User("chet", "haase", R.drawable.chet, User.TOOLKITTY), + new User("adam", "powell", R.drawable.adam, User.TOOLKITTY), + new User("alan", "viverette", R.drawable.alan, User.TOOLKITTY), + new User("chris", "craik", R.drawable.chris, User.TOOLKITTY), + new User("george", "mount", R.drawable.george, User.TOOLKITTY), + new User("john", "reck", R.drawable.john, User.TOOLKITTY), + new User("rob", "tsuk", R.drawable.rob, User.TOOLKITTY), + new User("Teng-Hui", "Zhu", R.drawable.tenghui, User.TOOLKITTY), + new User("yigit", "boyar", R.drawable.yigit, User.TOOLKITTY), + + + }; +} diff --git a/tools/data-binding/samples/BindingDemo/app/src/main/res/drawable-hdpi/ic_launcher.png b/tools/data-binding/samples/BindingDemo/app/src/main/res/drawable-hdpi/ic_launcher.png Binary files differnew file mode 100644 index 0000000..96a442e --- /dev/null +++ b/tools/data-binding/samples/BindingDemo/app/src/main/res/drawable-hdpi/ic_launcher.png diff --git a/tools/data-binding/samples/BindingDemo/app/src/main/res/drawable-mdpi/ic_launcher.png b/tools/data-binding/samples/BindingDemo/app/src/main/res/drawable-mdpi/ic_launcher.png Binary files differnew file mode 100644 index 0000000..359047d --- /dev/null +++ b/tools/data-binding/samples/BindingDemo/app/src/main/res/drawable-mdpi/ic_launcher.png diff --git a/tools/data-binding/samples/BindingDemo/app/src/main/res/drawable-xhdpi/ic_launcher.png b/tools/data-binding/samples/BindingDemo/app/src/main/res/drawable-xhdpi/ic_launcher.png Binary files differnew file mode 100644 index 0000000..71c6d76 --- /dev/null +++ b/tools/data-binding/samples/BindingDemo/app/src/main/res/drawable-xhdpi/ic_launcher.png diff --git a/tools/data-binding/samples/BindingDemo/app/src/main/res/drawable-xxhdpi/ic_launcher.png b/tools/data-binding/samples/BindingDemo/app/src/main/res/drawable-xxhdpi/ic_launcher.png Binary files differnew file mode 100644 index 0000000..4df1894 --- /dev/null +++ b/tools/data-binding/samples/BindingDemo/app/src/main/res/drawable-xxhdpi/ic_launcher.png diff --git a/tools/data-binding/samples/BindingDemo/app/src/main/res/drawable/adam.png b/tools/data-binding/samples/BindingDemo/app/src/main/res/drawable/adam.png Binary files differnew file mode 100644 index 0000000..583a065 --- /dev/null +++ b/tools/data-binding/samples/BindingDemo/app/src/main/res/drawable/adam.png diff --git a/tools/data-binding/samples/BindingDemo/app/src/main/res/drawable/alan.png b/tools/data-binding/samples/BindingDemo/app/src/main/res/drawable/alan.png Binary files differnew file mode 100644 index 0000000..c0c9161 --- /dev/null +++ b/tools/data-binding/samples/BindingDemo/app/src/main/res/drawable/alan.png diff --git a/tools/data-binding/samples/BindingDemo/app/src/main/res/drawable/chet.png b/tools/data-binding/samples/BindingDemo/app/src/main/res/drawable/chet.png Binary files differnew file mode 100644 index 0000000..06cc751 --- /dev/null +++ b/tools/data-binding/samples/BindingDemo/app/src/main/res/drawable/chet.png diff --git a/tools/data-binding/samples/BindingDemo/app/src/main/res/drawable/chris.png b/tools/data-binding/samples/BindingDemo/app/src/main/res/drawable/chris.png Binary files differnew file mode 100644 index 0000000..11686c5 --- /dev/null +++ b/tools/data-binding/samples/BindingDemo/app/src/main/res/drawable/chris.png diff --git a/tools/data-binding/samples/BindingDemo/app/src/main/res/drawable/george.png b/tools/data-binding/samples/BindingDemo/app/src/main/res/drawable/george.png Binary files differnew file mode 100644 index 0000000..fe744e0 --- /dev/null +++ b/tools/data-binding/samples/BindingDemo/app/src/main/res/drawable/george.png diff --git a/tools/data-binding/samples/BindingDemo/app/src/main/res/drawable/john.png b/tools/data-binding/samples/BindingDemo/app/src/main/res/drawable/john.png Binary files differnew file mode 100644 index 0000000..7bd0108 --- /dev/null +++ b/tools/data-binding/samples/BindingDemo/app/src/main/res/drawable/john.png diff --git a/tools/data-binding/samples/BindingDemo/app/src/main/res/drawable/rob.png b/tools/data-binding/samples/BindingDemo/app/src/main/res/drawable/rob.png Binary files differnew file mode 100644 index 0000000..fd41cb0 --- /dev/null +++ b/tools/data-binding/samples/BindingDemo/app/src/main/res/drawable/rob.png diff --git a/tools/data-binding/samples/BindingDemo/app/src/main/res/drawable/romain.png b/tools/data-binding/samples/BindingDemo/app/src/main/res/drawable/romain.png Binary files differnew file mode 100644 index 0000000..7a9af15 --- /dev/null +++ b/tools/data-binding/samples/BindingDemo/app/src/main/res/drawable/romain.png diff --git a/tools/data-binding/samples/BindingDemo/app/src/main/res/drawable/tenghui.png b/tools/data-binding/samples/BindingDemo/app/src/main/res/drawable/tenghui.png Binary files differnew file mode 100644 index 0000000..13442b0 --- /dev/null +++ b/tools/data-binding/samples/BindingDemo/app/src/main/res/drawable/tenghui.png diff --git a/tools/data-binding/samples/BindingDemo/app/src/main/res/drawable/yigit.png b/tools/data-binding/samples/BindingDemo/app/src/main/res/drawable/yigit.png Binary files differnew file mode 100644 index 0000000..57e9baf --- /dev/null +++ b/tools/data-binding/samples/BindingDemo/app/src/main/res/drawable/yigit.png diff --git a/tools/data-binding/samples/BindingDemo/app/src/main/res/layout/list_item.xml b/tools/data-binding/samples/BindingDemo/app/src/main/res/layout/list_item.xml new file mode 100644 index 0000000..1aaf2f0 --- /dev/null +++ b/tools/data-binding/samples/BindingDemo/app/src/main/res/layout/list_item.xml @@ -0,0 +1,46 @@ +<?xml version="1.0" encoding="utf-8"?> +<!-- + Copyright (C) 2014 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. + --> + +<android.support.v7.widget.CardView xmlns:android="http://schemas.android.com/apk/res/android" + xmlns:card_view="http://schemas.android.com/apk/res-auto" + xmlns:bind="http://schemas.android.com/apk/res-auto" + xmlns:bind_var="http://schemas.android.com/apk/res-auto" + card_view:cardUseCompatPadding="true" + card_view:contentPadding="8dp" + android:orientation="horizontal" android:layout_width="wrap_content" + android:id="@+id/root" + android:focusable="true" + android:gravity="center_vertical" + bind:onClickListener="@{clickListener}" + android:layout_height="match_parent"> + <variable name="user" type="com.android.example.bindingdemo.vo.User"/> + <variable name="clickListener" type="android.view.View.OnClickListener"/> + <ImageView + android:id="@+id/user_photo" + android:backgroundResource="@{user.photoResource}" + android:scaleType="fitCenter" + android:layout_width="@dimen/user_photo" + android:layout_height="@dimen/user_photo" /> + + <TextView + android:layout_marginLeft="@dimen/user_name_margin_left" + android:id="@+id/fullname" + android:gravity="center" + android:text='@{user.name.substring(0,1).toUpperCase() + "." + user.lastName}' + android:layout_width="wrap_content" + android:layout_height="match_parent" /> +</android.support.v7.widget.CardView> diff --git a/tools/data-binding/samples/BindingDemo/app/src/main/res/layout/main_activity.xml b/tools/data-binding/samples/BindingDemo/app/src/main/res/layout/main_activity.xml new file mode 100644 index 0000000..03d7368 --- /dev/null +++ b/tools/data-binding/samples/BindingDemo/app/src/main/res/layout/main_activity.xml @@ -0,0 +1,141 @@ +<!-- + Copyright (C) 2014 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. + --> + +<LinearLayout xmlns:android="http://schemas.android.com/apk/res/android" + xmlns:bind="http://schemas.android.com/apk/res-auto" + xmlns:tools="http://schemas.android.com/tools" + android:layout_width="match_parent" + android:layout_height="match_parent" + android:orientation="vertical" + android:paddingBottom="@dimen/activity_vertical_margin" + android:paddingLeft="@dimen/activity_horizontal_margin" + android:paddingRight="@dimen/activity_horizontal_margin" + android:paddingTop="@dimen/activity_vertical_margin" + android:id="@+id/activityRoot" + tools:activity=".MainActivity" + android:clickable="true" + android:onClickListener="@{activity.onUnselect}"> + <variable name="activity" type="com.android.example.bindingdemo.MainActivity"/> + <!----> + <import + type="android.view.View" + /> + <!----> + <import type="com.android.example.bindingdemo.R.string" alias="Strings"/> + <import type="com.android.example.bindingdemo.vo.User"/> + <TextView + android:id="@+id/title" + android:layout_width="wrap_content" + android:layout_height="wrap_content" + android:text="@{activity.getString(Strings.toolkitties, activity.tkAdapter.itemCount)}"> + + </TextView> + + <android.support.v7.widget.RecyclerView + android:id="@+id/toolkittyList" + android:layout_width="match_parent" + android:layout_height="@dimen/list_height" + bind:adapter="@{activity.tkAdapter}" + ></android.support.v7.widget.RecyclerView> + + <View + android:layout_width="match_parent" + android:layout_height="10dp" /> + + <TextView android:text="@{activity.selected2 == activity.selected ? `same` : `different`}" + android:id="@+id/deleteme" + android:layout_width="match_parent" + android:layout_height="wrap_content"/> + + <android.support.v7.widget.CardView + android:id="@+id/selected_card" + bind:contentPadding="@{activity.selected == null ? 5 : activity.selected.name.length()}" + android:layout_width="match_parent" + android:layout_height="wrap_content" + bind:visibility="@{activity.selected == null ? View.INVISIBLE : View.VISIBLE}"> + + <GridLayout + android:layout_width="match_parent" + android:layout_height="wrap_content" + android:columnCount="2" + android:rowCount="4"> + + <ImageView + android:id="@+id/selected_photo" + android:layout_width="@dimen/big_user_photo" + android:layout_height="@dimen/big_user_photo" + android:layout_column="0" + android:layout_row="0" + android:layout_rowSpan="2" + android:scaleType="fitCenter" + android:backgroundResource="@{activity.selected.photoResource}" /> + + <EditText + android:id="@+id/selected_name" + android:layout_width="wrap_content" + android:layout_height="wrap_content" + android:layout_column="1" + android:layout_gravity="fill" + android:layout_row="0" + android:background="@android:color/holo_blue_dark" + android:gravity="center" + android:text="@{activity.selected.name}" /> + + <EditText + android:id="@+id/selected_lastname" + android:layout_width="wrap_content" + android:layout_height="wrap_content" + android:layout_column="1" + android:layout_gravity="fill" + android:layout_row="1" + android:background="@android:color/holo_blue_bright" + android:gravity="center" + android:text="@{activity.selected.lastName}" /> + <Button + android:id="@+id/edit_button" + bind:onClickListener="@{activity.onSave}" + android:text="@{`Save changes to ` + activity.selected.name}" + android:layout_width="wrap_content" + android:layout_height="wrap_content" + android:layout_column="1" + android:layout_gravity="right" + android:layout_row="2"/> + + <Button + android:id="@+id/delete_button" + bind:onClickListener="@{activity.onDelete}" + android:text="@{activity.getString(activity.selected.group == User.TOOLKITTY ? Strings.became_robot : Strings.became_kitten, activity.selected.name)}" + android:layout_width="wrap_content" + android:layout_height="wrap_content" + android:layout_column="1" + android:layout_gravity="right" + android:layout_row="3"/> + + </GridLayout> + </android.support.v7.widget.CardView> + <View + android:layout_width="match_parent" + android:layout_height="10dp" /> + <TextView + android:id="@+id/robotsTitle" + android:layout_width="wrap_content" + android:layout_height="wrap_content" + android:text="@{activity.robotAdapter.itemCount + " Robots "}" /> + <android.support.v7.widget.RecyclerView + android:id="@+id/robotList" + android:layout_width="match_parent" bind:adapter="@{activity.robotAdapter}" android:layout_height="@dimen/list_height" + ></android.support.v7.widget.RecyclerView> +</LinearLayout> diff --git a/tools/data-binding/samples/BindingDemo/app/src/main/res/menu/menu_main.xml b/tools/data-binding/samples/BindingDemo/app/src/main/res/menu/menu_main.xml new file mode 100644 index 0000000..ab38265 --- /dev/null +++ b/tools/data-binding/samples/BindingDemo/app/src/main/res/menu/menu_main.xml @@ -0,0 +1,22 @@ +<!-- + Copyright (C) 2014 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. + --> + +<menu xmlns:android="http://schemas.android.com/apk/res/android" + xmlns:app="http://schemas.android.com/apk/res-auto" + xmlns:tools="http://schemas.android.com/tools" tools:context=".MainActivity"> + <item android:id="@+id/action_settings" android:title="@string/action_settings" + android:orderInCategory="100" app:showAsAction="never" /> +</menu> diff --git a/tools/data-binding/samples/BindingDemo/app/src/main/res/values-w820dp/dimens.xml b/tools/data-binding/samples/BindingDemo/app/src/main/res/values-w820dp/dimens.xml new file mode 100644 index 0000000..793d18d --- /dev/null +++ b/tools/data-binding/samples/BindingDemo/app/src/main/res/values-w820dp/dimens.xml @@ -0,0 +1,22 @@ +<!-- + Copyright (C) 2014 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. + --> + +<resources> + <!-- Example customization of dimensions originally defined in res/values/dimens.xml + (such as screen margins) for screens with more than 820dp of available width. This + would include 7" and 10" devices in landscape (~960dp and ~1280dp respectively). --> + <dimen name="activity_horizontal_margin">64dp</dimen> +</resources> diff --git a/tools/data-binding/samples/BindingDemo/app/src/main/res/values/dimens.xml b/tools/data-binding/samples/BindingDemo/app/src/main/res/values/dimens.xml new file mode 100644 index 0000000..7c360c6 --- /dev/null +++ b/tools/data-binding/samples/BindingDemo/app/src/main/res/values/dimens.xml @@ -0,0 +1,26 @@ +<!-- + Copyright (C) 2014 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. + --> + +<resources> + <!-- Default screen margins, per the Android Design guidelines. --> + <dimen name="activity_horizontal_margin">16dp</dimen> + <dimen name="activity_vertical_margin">16dp</dimen> + <dimen name="list_height">80dp</dimen> + <dimen name="user_photo">48dp</dimen> + <dimen name="user_name_margin_left">56dp</dimen> + <dimen name="big_user_photo">96dp</dimen> + <dimen name="item_width">200dp</dimen> +</resources> diff --git a/tools/data-binding/samples/BindingDemo/app/src/main/res/values/strings.xml b/tools/data-binding/samples/BindingDemo/app/src/main/res/values/strings.xml new file mode 100644 index 0000000..ea6f485 --- /dev/null +++ b/tools/data-binding/samples/BindingDemo/app/src/main/res/values/strings.xml @@ -0,0 +1,28 @@ +<?xml version="1.0" encoding="utf-8"?> +<!-- + Copyright (C) 2014 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. + --> + +<resources> + + <string name="app_name">BindingDemo</string> + <string name="hello_world">Hello world!</string> + <string name="action_settings">Settings</string> + <string name="toolkitty_list">toolkitties</string> + <string name="became_robot">%s joined robots</string> + <string name="became_kitten">%s joined tool kitties</string> + <string name="toolkitties">%s ToolKitties</string> + +</resources> diff --git a/tools/data-binding/samples/BindingDemo/app/src/main/res/values/styles.xml b/tools/data-binding/samples/BindingDemo/app/src/main/res/values/styles.xml new file mode 100644 index 0000000..00d89fa --- /dev/null +++ b/tools/data-binding/samples/BindingDemo/app/src/main/res/values/styles.xml @@ -0,0 +1,24 @@ +<!-- + Copyright (C) 2014 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. + --> + +<resources> + + <!-- Base application theme. --> + <style name="AppTheme" parent="Theme.AppCompat.Light.DarkActionBar"> + <!-- Customize your theme here. --> + </style> + +</resources> diff --git a/tools/data-binding/samples/BindingDemo/app/src/test/java/com/android/example/bindingdemo/vo/UnitTest.java b/tools/data-binding/samples/BindingDemo/app/src/test/java/com/android/example/bindingdemo/vo/UnitTest.java new file mode 100644 index 0000000..65449ce --- /dev/null +++ b/tools/data-binding/samples/BindingDemo/app/src/test/java/com/android/example/bindingdemo/vo/UnitTest.java @@ -0,0 +1,40 @@ +package com.android.example.bindingdemo.vo; + +import android.databinding.OnPropertyChangedListener; + +import com.android.example.bindingdemo.R; + +import org.junit.Before; +import org.junit.Test; + +import static org.junit.Assert.assertEquals; +import static org.mockito.Mockito.mock; +import static org.mockito.Mockito.verify; +import static org.mockito.Mockito.verifyNoMoreInteractions; + +import com.android.example.bindingdemo.BR; +public class UnitTest { + + private User testUser; + + @Before + public void setUp() throws Exception { + testUser = new User("Ted", "Tester", R.drawable.george, User.ROBOT); + } + + @Test + public void settersWorkFineOnTheJvm() throws Exception { + assertEquals("Ted", testUser.getName()); + testUser.setName("Tom"); + assertEquals("Tom", testUser.getName()); + } + + @Test + public void listeners() throws Exception { + OnPropertyChangedListener mockListener = mock(OnPropertyChangedListener.class); + testUser.addOnPropertyChangedListener(mockListener); + testUser.setName("Tom"); + verify(mockListener).onPropertyChanged(testUser, BR.name); + verifyNoMoreInteractions(mockListener); + } +} diff --git a/tools/data-binding/samples/BindingDemo/build.gradle b/tools/data-binding/samples/BindingDemo/build.gradle new file mode 100644 index 0000000..18f0f56 --- /dev/null +++ b/tools/data-binding/samples/BindingDemo/build.gradle @@ -0,0 +1,42 @@ +/* + * Copyright (C) 2014 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. + */ + +// Top-level build file where you can add configuration options common to all sub-projects/modules. +buildscript { + repositories { + jcenter() + maven { + url "$projectDir/../../maven-repo" + } + mavenCentral() + } + dependencies { + classpath "com.android.tools.build:gradle:1.1.3" + classpath 'com.android.databinding:dataBinder:0.3-SNAPSHOT' + // NOTE: Do not place your application dependencies here; they belong + // in the individual module build.gradle files + } +} + +allprojects { + repositories { + jcenter() + maven { + url "$projectDir/../../../maven-repo" + } + mavenCentral() + } +} diff --git a/tools/data-binding/samples/BindingDemo/gradle.properties b/tools/data-binding/samples/BindingDemo/gradle.properties new file mode 100644 index 0000000..f60a634 --- /dev/null +++ b/tools/data-binding/samples/BindingDemo/gradle.properties @@ -0,0 +1,34 @@ +# +# Copyright (C) 2014 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. +# + +# Project-wide Gradle settings. + +# IDE (e.g. Android Studio) users: +# Gradle settings configured through the IDE *will override* +# any settings specified in this file. + +# For more details on how to configure your build environment visit +# http://www.gradle.org/docs/current/userguide/build_environment.html + +# Specifies the JVM arguments used for the daemon process. +# The setting is particularly useful for tweaking memory settings. +# Default value: -Xmx10248m -XX:MaxPermSize=256m +# org.gradle.jvmargs=-Xmx2048m -XX:MaxPermSize=512m -XX:+HeapDumpOnOutOfMemoryError -Dfile.encoding=UTF-8 + +# When configured, Gradle will run in incubating parallel mode. +# This option should only be used with decoupled projects. More details, visit +# http://www.gradle.org/docs/current/userguide/multi_project_builds.html#sec:decoupled_projects +# org.gradle.parallel=true
\ No newline at end of file diff --git a/tools/data-binding/samples/BindingDemo/gradle/wrapper/gradle-wrapper.jar b/tools/data-binding/samples/BindingDemo/gradle/wrapper/gradle-wrapper.jar Binary files differnew file mode 100644 index 0000000..8c0fb64 --- /dev/null +++ b/tools/data-binding/samples/BindingDemo/gradle/wrapper/gradle-wrapper.jar diff --git a/tools/data-binding/samples/BindingDemo/gradle/wrapper/gradle-wrapper.properties b/tools/data-binding/samples/BindingDemo/gradle/wrapper/gradle-wrapper.properties new file mode 100644 index 0000000..f02c72a --- /dev/null +++ b/tools/data-binding/samples/BindingDemo/gradle/wrapper/gradle-wrapper.properties @@ -0,0 +1,6 @@ +#Wed Dec 17 11:22:31 PST 2014 +distributionBase=GRADLE_USER_HOME +distributionPath=wrapper/dists +zipStoreBase=GRADLE_USER_HOME +zipStorePath=wrapper/dists +distributionUrl=https\://services.gradle.org/distributions/gradle-2.2.1-all.zip diff --git a/tools/data-binding/samples/BindingDemo/gradlew b/tools/data-binding/samples/BindingDemo/gradlew new file mode 100755 index 0000000..91a7e26 --- /dev/null +++ b/tools/data-binding/samples/BindingDemo/gradlew @@ -0,0 +1,164 @@ +#!/usr/bin/env bash + +############################################################################## +## +## Gradle start up script for UN*X +## +############################################################################## + +# Add default JVM options here. You can also use JAVA_OPTS and GRADLE_OPTS to pass JVM options to this script. +DEFAULT_JVM_OPTS="" + +APP_NAME="Gradle" +APP_BASE_NAME=`basename "$0"` + +# Use the maximum available, or set MAX_FD != -1 to use that value. +MAX_FD="maximum" + +warn ( ) { + echo "$*" +} + +die ( ) { + echo + echo "$*" + echo + exit 1 +} + +# OS specific support (must be 'true' or 'false'). +cygwin=false +msys=false +darwin=false +case "`uname`" in + CYGWIN* ) + cygwin=true + ;; + Darwin* ) + darwin=true + ;; + MINGW* ) + msys=true + ;; +esac + +# For Cygwin, ensure paths are in UNIX format before anything is touched. +if $cygwin ; then + [ -n "$JAVA_HOME" ] && JAVA_HOME=`cygpath --unix "$JAVA_HOME"` +fi + +# Attempt to set APP_HOME +# Resolve links: $0 may be a link +PRG="$0" +# Need this for relative symlinks. +while [ -h "$PRG" ] ; do + ls=`ls -ld "$PRG"` + link=`expr "$ls" : '.*-> \(.*\)$'` + if expr "$link" : '/.*' > /dev/null; then + PRG="$link" + else + PRG=`dirname "$PRG"`"/$link" + fi +done +SAVED="`pwd`" +cd "`dirname \"$PRG\"`/" >&- +APP_HOME="`pwd -P`" +cd "$SAVED" >&- + +CLASSPATH=$APP_HOME/gradle/wrapper/gradle-wrapper.jar + +# Determine the Java command to use to start the JVM. +if [ -n "$JAVA_HOME" ] ; then + if [ -x "$JAVA_HOME/jre/sh/java" ] ; then + # IBM's JDK on AIX uses strange locations for the executables + JAVACMD="$JAVA_HOME/jre/sh/java" + else + JAVACMD="$JAVA_HOME/bin/java" + fi + if [ ! -x "$JAVACMD" ] ; then + die "ERROR: JAVA_HOME is set to an invalid directory: $JAVA_HOME + +Please set the JAVA_HOME variable in your environment to match the +location of your Java installation." + fi +else + JAVACMD="java" + which java >/dev/null 2>&1 || die "ERROR: JAVA_HOME is not set and no 'java' command could be found in your PATH. + +Please set the JAVA_HOME variable in your environment to match the +location of your Java installation." +fi + +# Increase the maximum file descriptors if we can. +if [ "$cygwin" = "false" -a "$darwin" = "false" ] ; then + MAX_FD_LIMIT=`ulimit -H -n` + if [ $? -eq 0 ] ; then + if [ "$MAX_FD" = "maximum" -o "$MAX_FD" = "max" ] ; then + MAX_FD="$MAX_FD_LIMIT" + fi + ulimit -n $MAX_FD + if [ $? -ne 0 ] ; then + warn "Could not set maximum file descriptor limit: $MAX_FD" + fi + else + warn "Could not query maximum file descriptor limit: $MAX_FD_LIMIT" + fi +fi + +# For Darwin, add options to specify how the application appears in the dock +if $darwin; then + GRADLE_OPTS="$GRADLE_OPTS \"-Xdock:name=$APP_NAME\" \"-Xdock:icon=$APP_HOME/media/gradle.icns\"" +fi + +# For Cygwin, switch paths to Windows format before running java +if $cygwin ; then + APP_HOME=`cygpath --path --mixed "$APP_HOME"` + CLASSPATH=`cygpath --path --mixed "$CLASSPATH"` + + # We build the pattern for arguments to be converted via cygpath + ROOTDIRSRAW=`find -L / -maxdepth 1 -mindepth 1 -type d 2>/dev/null` + SEP="" + for dir in $ROOTDIRSRAW ; do + ROOTDIRS="$ROOTDIRS$SEP$dir" + SEP="|" + done + OURCYGPATTERN="(^($ROOTDIRS))" + # Add a user-defined pattern to the cygpath arguments + if [ "$GRADLE_CYGPATTERN" != "" ] ; then + OURCYGPATTERN="$OURCYGPATTERN|($GRADLE_CYGPATTERN)" + fi + # Now convert the arguments - kludge to limit ourselves to /bin/sh + i=0 + for arg in "$@" ; do + CHECK=`echo "$arg"|egrep -c "$OURCYGPATTERN" -` + CHECK2=`echo "$arg"|egrep -c "^-"` ### Determine if an option + + if [ $CHECK -ne 0 ] && [ $CHECK2 -eq 0 ] ; then ### Added a condition + eval `echo args$i`=`cygpath --path --ignore --mixed "$arg"` + else + eval `echo args$i`="\"$arg\"" + fi + i=$((i+1)) + done + case $i in + (0) set -- ;; + (1) set -- "$args0" ;; + (2) set -- "$args0" "$args1" ;; + (3) set -- "$args0" "$args1" "$args2" ;; + (4) set -- "$args0" "$args1" "$args2" "$args3" ;; + (5) set -- "$args0" "$args1" "$args2" "$args3" "$args4" ;; + (6) set -- "$args0" "$args1" "$args2" "$args3" "$args4" "$args5" ;; + (7) set -- "$args0" "$args1" "$args2" "$args3" "$args4" "$args5" "$args6" ;; + (8) set -- "$args0" "$args1" "$args2" "$args3" "$args4" "$args5" "$args6" "$args7" ;; + (9) set -- "$args0" "$args1" "$args2" "$args3" "$args4" "$args5" "$args6" "$args7" "$args8" ;; + esac +fi + +# Split up the JVM_OPTS And GRADLE_OPTS values into an array, following the shell quoting and substitution rules +function splitJvmOpts() { + JVM_OPTS=("$@") +} +eval splitJvmOpts $DEFAULT_JVM_OPTS $JAVA_OPTS $GRADLE_OPTS +JVM_OPTS[${#JVM_OPTS[*]}]="-Dorg.gradle.appname=$APP_BASE_NAME" + +exec "$JAVACMD" "${JVM_OPTS[@]}" -classpath "$CLASSPATH" org.gradle.wrapper.GradleWrapperMain "$@" diff --git a/tools/data-binding/samples/BindingDemo/gradlew.bat b/tools/data-binding/samples/BindingDemo/gradlew.bat new file mode 100644 index 0000000..aec9973 --- /dev/null +++ b/tools/data-binding/samples/BindingDemo/gradlew.bat @@ -0,0 +1,90 @@ +@if "%DEBUG%" == "" @echo off
+@rem ##########################################################################
+@rem
+@rem Gradle startup script for Windows
+@rem
+@rem ##########################################################################
+
+@rem Set local scope for the variables with windows NT shell
+if "%OS%"=="Windows_NT" setlocal
+
+@rem Add default JVM options here. You can also use JAVA_OPTS and GRADLE_OPTS to pass JVM options to this script.
+set DEFAULT_JVM_OPTS=
+
+set DIRNAME=%~dp0
+if "%DIRNAME%" == "" set DIRNAME=.
+set APP_BASE_NAME=%~n0
+set APP_HOME=%DIRNAME%
+
+@rem Find java.exe
+if defined JAVA_HOME goto findJavaFromJavaHome
+
+set JAVA_EXE=java.exe
+%JAVA_EXE% -version >NUL 2>&1
+if "%ERRORLEVEL%" == "0" goto init
+
+echo.
+echo ERROR: JAVA_HOME is not set and no 'java' command could be found in your PATH.
+echo.
+echo Please set the JAVA_HOME variable in your environment to match the
+echo location of your Java installation.
+
+goto fail
+
+:findJavaFromJavaHome
+set JAVA_HOME=%JAVA_HOME:"=%
+set JAVA_EXE=%JAVA_HOME%/bin/java.exe
+
+if exist "%JAVA_EXE%" goto init
+
+echo.
+echo ERROR: JAVA_HOME is set to an invalid directory: %JAVA_HOME%
+echo.
+echo Please set the JAVA_HOME variable in your environment to match the
+echo location of your Java installation.
+
+goto fail
+
+:init
+@rem Get command-line arguments, handling Windowz variants
+
+if not "%OS%" == "Windows_NT" goto win9xME_args
+if "%@eval[2+2]" == "4" goto 4NT_args
+
+:win9xME_args
+@rem Slurp the command line arguments.
+set CMD_LINE_ARGS=
+set _SKIP=2
+
+:win9xME_args_slurp
+if "x%~1" == "x" goto execute
+
+set CMD_LINE_ARGS=%*
+goto execute
+
+:4NT_args
+@rem Get arguments from the 4NT Shell from JP Software
+set CMD_LINE_ARGS=%$
+
+:execute
+@rem Setup the command line
+
+set CLASSPATH=%APP_HOME%\gradle\wrapper\gradle-wrapper.jar
+
+@rem Execute Gradle
+"%JAVA_EXE%" %DEFAULT_JVM_OPTS% %JAVA_OPTS% %GRADLE_OPTS% "-Dorg.gradle.appname=%APP_BASE_NAME%" -classpath "%CLASSPATH%" org.gradle.wrapper.GradleWrapperMain %CMD_LINE_ARGS%
+
+:end
+@rem End local scope for the variables with windows NT shell
+if "%ERRORLEVEL%"=="0" goto mainEnd
+
+:fail
+rem Set variable GRADLE_EXIT_CONSOLE if you need the _script_ return code instead of
+rem the _cmd.exe /c_ return code!
+if not "" == "%GRADLE_EXIT_CONSOLE%" exit 1
+exit /b 1
+
+:mainEnd
+if "%OS%"=="Windows_NT" endlocal
+
+:omega
diff --git a/tools/data-binding/samples/BindingDemo/settings.gradle b/tools/data-binding/samples/BindingDemo/settings.gradle new file mode 100644 index 0000000..c465666 --- /dev/null +++ b/tools/data-binding/samples/BindingDemo/settings.gradle @@ -0,0 +1,17 @@ +/* + * Copyright (C) 2014 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. + */ + +include ':app' diff --git a/tools/data-binding/settings.gradle b/tools/data-binding/settings.gradle new file mode 100644 index 0000000..2190518 --- /dev/null +++ b/tools/data-binding/settings.gradle @@ -0,0 +1,7 @@ +include ':baseLibrary', ':app' +include ':library' +include ':compiler' +include ':gradlePlugin' +include ':grammarBuilder' +include ':annotationprocessor' +include ':xmlGrammar' diff --git a/tools/data-binding/xmlGrammar/XMLLexer.g4 b/tools/data-binding/xmlGrammar/XMLLexer.g4 new file mode 100644 index 0000000..ea7a23c --- /dev/null +++ b/tools/data-binding/xmlGrammar/XMLLexer.g4 @@ -0,0 +1,93 @@ +/* + [The "BSD licence"] + Copyright (c) 2013 Terence Parr + All rights reserved. + + Redistribution and use in source and binary forms, with or without + modification, are permitted provided that the following conditions + are met: + 1. Redistributions of source code must retain the above copyright + notice, this list of conditions and the following disclaimer. + 2. Redistributions in binary form must reproduce the above copyright + notice, this list of conditions and the following disclaimer in the + documentation and/or other materials provided with the distribution. + 3. The name of the author may not be used to endorse or promote products + derived from this software without specific prior written permission. + + THIS SOFTWARE IS PROVIDED BY THE AUTHOR ``AS IS'' AND ANY EXPRESS OR + IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE IMPLIED WARRANTIES + OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE ARE DISCLAIMED. + IN NO EVENT SHALL THE AUTHOR BE LIABLE FOR ANY DIRECT, INDIRECT, + INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING, BUT + NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; LOSS OF USE, + DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND ON ANY + THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT + (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE OF + THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE. +*/ + +/** XML lexer derived from ANTLR v4 ref guide book example */ +lexer grammar XMLLexer; + +// Default "mode": Everything OUTSIDE of a tag +COMMENT : '<!--' .*? '-->' ; +CDATA : '<![CDATA[' .*? ']]>' ; +/** Scarf all DTD stuff, Entity Declarations like <!ENTITY ...>, + * and Notation Declarations <!NOTATION ...> + */ +DTD : '<!' .*? '>' -> skip ; +EntityRef : '&' Name ';' ; +CharRef : '&#' DIGIT+ ';' + | '&#x' HEXDIGIT+ ';' + ; +SEA_WS : (' '|'\t'|'\r'? '\n')+ ; + +OPEN : '<' -> pushMode(INSIDE) ; +XMLDeclOpen : '<?xml' S -> pushMode(INSIDE) ; +SPECIAL_OPEN: '<?' Name -> more, pushMode(PROC_INSTR) ; + +TEXT : ~[<&]+ ; // match any 16 bit char other than < and & + +// ----------------- Everything INSIDE of a tag --------------------- +mode INSIDE; + +CLOSE : '>' -> popMode ; +SPECIAL_CLOSE: '?>' -> popMode ; // close <?xml...?> +SLASH_CLOSE : '/>' -> popMode ; +SLASH : '/' ; +EQUALS : '=' ; +STRING : '"' ~[<"]* '"' + | '\'' ~[<']* '\'' + ; +Name : NameStartChar NameChar* ; +S : [ \t\r\n] -> skip ; + +fragment +HEXDIGIT : [a-fA-F0-9] ; + +fragment +DIGIT : [0-9] ; + +fragment +NameChar : NameStartChar + | '-' | '_' | '.' | DIGIT + | '\u00B7' + | '\u0300'..'\u036F' + | '\u203F'..'\u2040' + ; + +fragment +NameStartChar + : [:a-zA-Z] + | '\u2070'..'\u218F' + | '\u2C00'..'\u2FEF' + | '\u3001'..'\uD7FF' + | '\uF900'..'\uFDCF' + | '\uFDF0'..'\uFFFD' + ; + +// ----------------- Handle <? ... ?> --------------------- +mode PROC_INSTR; + +PI : '?>' -> popMode ; // close <?...?> +IGNORE : . -> more ; diff --git a/tools/data-binding/xmlGrammar/XMLParser.g4 b/tools/data-binding/xmlGrammar/XMLParser.g4 new file mode 100644 index 0000000..1b03e2d --- /dev/null +++ b/tools/data-binding/xmlGrammar/XMLParser.g4 @@ -0,0 +1,54 @@ +/* + [The "BSD licence"] + Copyright (c) 2013 Terence Parr + All rights reserved. + + Redistribution and use in source and binary forms, with or without + modification, are permitted provided that the following conditions + are met: + 1. Redistributions of source code must retain the above copyright + notice, this list of conditions and the following disclaimer. + 2. Redistributions in binary form must reproduce the above copyright + notice, this list of conditions and the following disclaimer in the + documentation and/or other materials provided with the distribution. + 3. The name of the author may not be used to endorse or promote products + derived from this software without specific prior written permission. + + THIS SOFTWARE IS PROVIDED BY THE AUTHOR ``AS IS'' AND ANY EXPRESS OR + IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE IMPLIED WARRANTIES + OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE ARE DISCLAIMED. + IN NO EVENT SHALL THE AUTHOR BE LIABLE FOR ANY DIRECT, INDIRECT, + INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING, BUT + NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; LOSS OF USE, + DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND ON ANY + THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT + (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE OF + THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE. +*/ + +/** XML parser derived from ANTLR v4 ref guide book example */ +parser grammar XMLParser; + +options { tokenVocab=XMLLexer; } + +document : prolog? misc* element misc*; + +prolog : XMLDeclOpen attribute* SPECIAL_CLOSE ; + +content : chardata? + ((element | reference | CDATA | PI | COMMENT) chardata?)* ; + +element : '<' elmName=Name attribute* '>' content '<' '/' Name '>' + | '<' elmName=Name attribute* '/>' + ; + +reference : EntityRef | CharRef ; + +attribute : attrName=Name '=' attrValue=STRING ; // Our STRING is AttValue in spec + +/** ``All text that is not markup constitutes the character data of + * the document.'' + */ +chardata : TEXT | SEA_WS ; + +misc : COMMENT | PI | SEA_WS ; diff --git a/tools/data-binding/xmlGrammar/build.gradle b/tools/data-binding/xmlGrammar/build.gradle new file mode 100644 index 0000000..c9d3607 --- /dev/null +++ b/tools/data-binding/xmlGrammar/build.gradle @@ -0,0 +1,42 @@ +apply plugin: 'java' +apply plugin: 'kotlin' +apply plugin: 'application' +apply plugin: 'maven' + +sourceCompatibility = config.javaTargetCompatibility +targetCompatibility = config.javaSourceCompatibility + +mainClassName = "org.antlr.v4.Tool" + + +repositories { + mavenCentral() +} + + +buildscript { + repositories { + mavenCentral() + } + dependencies { + classpath "org.jetbrains.kotlin:kotlin-gradle-plugin:${config.kotlinVersion}" + } +} + + +run { + args "XMLParser.g4", "-visitor", "-o", "src/main/java/android/databinding/parser", "-package", "android.databinding.parser", "-lib", "." +} + +dependencies { + compile "org.jetbrains.kotlin:kotlin-stdlib:${config.kotlinVersion}" + compile 'com.tunnelvisionlabs:antlr4:4.4' +} + +uploadArchives { + repositories { + mavenDeployer { + pom.artifactId = 'xmlGrammer' + } + } +} diff --git a/tools/data-binding/xmlGrammar/src/main/java/android/databinding/parser/XMLLexer.java b/tools/data-binding/xmlGrammar/src/main/java/android/databinding/parser/XMLLexer.java new file mode 100644 index 0000000..b8c79bb --- /dev/null +++ b/tools/data-binding/xmlGrammar/src/main/java/android/databinding/parser/XMLLexer.java @@ -0,0 +1,136 @@ +// Generated from XMLLexer.g4 by ANTLR 4.4 +package android.databinding.parser; + +import org.antlr.v4.runtime.CharStream; +import org.antlr.v4.runtime.Lexer; +import org.antlr.v4.runtime.atn.ATN; +import org.antlr.v4.runtime.atn.ATNDeserializer; +import org.antlr.v4.runtime.atn.LexerATNSimulator; + +public class XMLLexer extends Lexer { + public static final int + COMMENT=1, CDATA=2, DTD=3, EntityRef=4, CharRef=5, SEA_WS=6, OPEN=7, XMLDeclOpen=8, + TEXT=9, CLOSE=10, SPECIAL_CLOSE=11, SLASH_CLOSE=12, SLASH=13, EQUALS=14, + STRING=15, Name=16, S=17, PI=18; + public static final int INSIDE = 1; + public static final int PROC_INSTR = 2; + public static String[] modeNames = { + "DEFAULT_MODE", "INSIDE", "PROC_INSTR" + }; + + public static final String[] tokenNames = { + "'\\u0000'", "'\\u0001'", "'\\u0002'", "'\\u0003'", "'\\u0004'", "'\\u0005'", + "'\\u0006'", "'\\u0007'", "'\b'", "'\t'", "'\n'", "'\\u000B'", "'\f'", + "'\r'", "'\\u000E'", "'\\u000F'", "'\\u0010'", "'\\u0011'", "'\\u0012'" + }; + public static final String[] ruleNames = { + "COMMENT", "CDATA", "DTD", "EntityRef", "CharRef", "SEA_WS", "OPEN", "XMLDeclOpen", + "SPECIAL_OPEN", "TEXT", "CLOSE", "SPECIAL_CLOSE", "SLASH_CLOSE", "SLASH", + "EQUALS", "STRING", "Name", "S", "HEXDIGIT", "DIGIT", "NameChar", "NameStartChar", + "PI", "IGNORE" + }; + + + public XMLLexer(CharStream input) { + super(input); + _interp = new LexerATNSimulator(this,_ATN); + } + + @Override + public String getGrammarFileName() { return "XMLLexer.g4"; } + + @Override + public String[] getTokenNames() { return tokenNames; } + + @Override + public String[] getRuleNames() { return ruleNames; } + + @Override + public String getSerializedATN() { return _serializedATN; } + + @Override + public String[] getModeNames() { return modeNames; } + + public static final String _serializedATN = + "\3\uaf6f\u8320\u479d\ub75c\u4880\u1605\u191c\uab37\2\24\u00e9\b\1\b\1"+ + "\b\1\4\2\t\2\4\3\t\3\4\4\t\4\4\5\t\5\4\6\t\6\4\7\t\7\4\b\t\b\4\t\t\t\4"+ + "\n\t\n\4\13\t\13\4\f\t\f\4\r\t\r\4\16\t\16\4\17\t\17\4\20\t\20\4\21\t"+ + "\21\4\22\t\22\4\23\t\23\4\24\t\24\4\25\t\25\4\26\t\26\4\27\t\27\4\30\t"+ + "\30\4\31\t\31\3\2\3\2\3\2\3\2\3\2\3\2\7\2<\n\2\f\2\16\2?\13\2\3\2\3\2"+ + "\3\2\3\2\3\3\3\3\3\3\3\3\3\3\3\3\3\3\3\3\3\3\3\3\3\3\7\3P\n\3\f\3\16\3"+ + "S\13\3\3\3\3\3\3\3\3\3\3\4\3\4\3\4\3\4\7\4]\n\4\f\4\16\4`\13\4\3\4\3\4"+ + "\3\4\3\4\3\5\3\5\3\5\3\5\3\6\3\6\3\6\3\6\6\6n\n\6\r\6\16\6o\3\6\3\6\3"+ + "\6\3\6\3\6\3\6\3\6\6\6y\n\6\r\6\16\6z\3\6\3\6\5\6\177\n\6\3\7\3\7\5\7"+ + "\u0083\n\7\3\7\6\7\u0086\n\7\r\7\16\7\u0087\3\b\3\b\3\b\3\b\3\t\3\t\3"+ + "\t\3\t\3\t\3\t\3\t\3\t\3\t\3\t\3\n\3\n\3\n\3\n\3\n\3\n\3\n\3\n\3\13\6"+ + "\13\u00a1\n\13\r\13\16\13\u00a2\3\f\3\f\3\f\3\f\3\r\3\r\3\r\3\r\3\r\3"+ + "\16\3\16\3\16\3\16\3\16\3\17\3\17\3\20\3\20\3\21\3\21\7\21\u00b9\n\21"+ + "\f\21\16\21\u00bc\13\21\3\21\3\21\3\21\7\21\u00c1\n\21\f\21\16\21\u00c4"+ + "\13\21\3\21\5\21\u00c7\n\21\3\22\3\22\7\22\u00cb\n\22\f\22\16\22\u00ce"+ + "\13\22\3\23\3\23\3\23\3\23\3\24\3\24\3\25\3\25\3\26\3\26\3\26\3\26\5\26"+ + "\u00dc\n\26\3\27\5\27\u00df\n\27\3\30\3\30\3\30\3\30\3\30\3\31\3\31\3"+ + "\31\3\31\5=Q^\2\2\32\5\2\3\7\2\4\t\2\5\13\2\6\r\2\7\17\2\b\21\2\t\23\2"+ + "\n\25\2\2\27\2\13\31\2\f\33\2\r\35\2\16\37\2\17!\2\20#\2\21%\2\22\'\2"+ + "\23)\2\2+\2\2-\2\2/\2\2\61\2\24\63\2\2\5\2\3\4\f\4\2\13\13\"\"\4\2((>"+ + ">\4\2$$>>\4\2))>>\5\2\13\f\17\17\"\"\5\2\62;CHch\3\2\62;\4\2/\60aa\5\2"+ + "\u00b9\u00b9\u0302\u0371\u2041\u2042\n\2<<C\\c|\u2072\u2191\u2c02\u2ff1"+ + "\u3003\ud801\uf902\ufdd1\ufdf2\uffff\u00f3\2\5\3\2\2\2\2\7\3\2\2\2\2\t"+ + "\3\2\2\2\2\13\3\2\2\2\2\r\3\2\2\2\2\17\3\2\2\2\2\21\3\2\2\2\2\23\3\2\2"+ + "\2\2\25\3\2\2\2\2\27\3\2\2\2\3\31\3\2\2\2\3\33\3\2\2\2\3\35\3\2\2\2\3"+ + "\37\3\2\2\2\3!\3\2\2\2\3#\3\2\2\2\3%\3\2\2\2\3\'\3\2\2\2\4\61\3\2\2\2"+ + "\4\63\3\2\2\2\5\65\3\2\2\2\7D\3\2\2\2\tX\3\2\2\2\13e\3\2\2\2\r~\3\2\2"+ + "\2\17\u0085\3\2\2\2\21\u0089\3\2\2\2\23\u008d\3\2\2\2\25\u0097\3\2\2\2"+ + "\27\u00a0\3\2\2\2\31\u00a4\3\2\2\2\33\u00a8\3\2\2\2\35\u00ad\3\2\2\2\37"+ + "\u00b2\3\2\2\2!\u00b4\3\2\2\2#\u00c6\3\2\2\2%\u00c8\3\2\2\2\'\u00cf\3"+ + "\2\2\2)\u00d3\3\2\2\2+\u00d5\3\2\2\2-\u00db\3\2\2\2/\u00de\3\2\2\2\61"+ + "\u00e0\3\2\2\2\63\u00e5\3\2\2\2\65\66\7>\2\2\66\67\7#\2\2\678\7/\2\28"+ + "9\7/\2\29=\3\2\2\2:<\13\2\2\2;:\3\2\2\2<?\3\2\2\2=>\3\2\2\2=;\3\2\2\2"+ + ">@\3\2\2\2?=\3\2\2\2@A\7/\2\2AB\7/\2\2BC\7@\2\2C\6\3\2\2\2DE\7>\2\2EF"+ + "\7#\2\2FG\7]\2\2GH\7E\2\2HI\7F\2\2IJ\7C\2\2JK\7V\2\2KL\7C\2\2LM\7]\2\2"+ + "MQ\3\2\2\2NP\13\2\2\2ON\3\2\2\2PS\3\2\2\2QR\3\2\2\2QO\3\2\2\2RT\3\2\2"+ + "\2SQ\3\2\2\2TU\7_\2\2UV\7_\2\2VW\7@\2\2W\b\3\2\2\2XY\7>\2\2YZ\7#\2\2Z"+ + "^\3\2\2\2[]\13\2\2\2\\[\3\2\2\2]`\3\2\2\2^_\3\2\2\2^\\\3\2\2\2_a\3\2\2"+ + "\2`^\3\2\2\2ab\7@\2\2bc\3\2\2\2cd\b\4\2\2d\n\3\2\2\2ef\7(\2\2fg\5%\22"+ + "\2gh\7=\2\2h\f\3\2\2\2ij\7(\2\2jk\7%\2\2km\3\2\2\2ln\5+\25\2ml\3\2\2\2"+ + "no\3\2\2\2om\3\2\2\2op\3\2\2\2pq\3\2\2\2qr\7=\2\2r\177\3\2\2\2st\7(\2"+ + "\2tu\7%\2\2uv\7z\2\2vx\3\2\2\2wy\5)\24\2xw\3\2\2\2yz\3\2\2\2zx\3\2\2\2"+ + "z{\3\2\2\2{|\3\2\2\2|}\7=\2\2}\177\3\2\2\2~i\3\2\2\2~s\3\2\2\2\177\16"+ + "\3\2\2\2\u0080\u0086\t\2\2\2\u0081\u0083\7\17\2\2\u0082\u0081\3\2\2\2"+ + "\u0082\u0083\3\2\2\2\u0083\u0084\3\2\2\2\u0084\u0086\7\f\2\2\u0085\u0080"+ + "\3\2\2\2\u0085\u0082\3\2\2\2\u0086\u0087\3\2\2\2\u0087\u0085\3\2\2\2\u0087"+ + "\u0088\3\2\2\2\u0088\20\3\2\2\2\u0089\u008a\7>\2\2\u008a\u008b\3\2\2\2"+ + "\u008b\u008c\b\b\3\2\u008c\22\3\2\2\2\u008d\u008e\7>\2\2\u008e\u008f\7"+ + "A\2\2\u008f\u0090\7z\2\2\u0090\u0091\7o\2\2\u0091\u0092\7n\2\2\u0092\u0093"+ + "\3\2\2\2\u0093\u0094\5\'\23\2\u0094\u0095\3\2\2\2\u0095\u0096\b\t\3\2"+ + "\u0096\24\3\2\2\2\u0097\u0098\7>\2\2\u0098\u0099\7A\2\2\u0099\u009a\3"+ + "\2\2\2\u009a\u009b\5%\22\2\u009b\u009c\3\2\2\2\u009c\u009d\b\n\4\2\u009d"+ + "\u009e\b\n\5\2\u009e\26\3\2\2\2\u009f\u00a1\n\3\2\2\u00a0\u009f\3\2\2"+ + "\2\u00a1\u00a2\3\2\2\2\u00a2\u00a0\3\2\2\2\u00a2\u00a3\3\2\2\2\u00a3\30"+ + "\3\2\2\2\u00a4\u00a5\7@\2\2\u00a5\u00a6\3\2\2\2\u00a6\u00a7\b\f\6\2\u00a7"+ + "\32\3\2\2\2\u00a8\u00a9\7A\2\2\u00a9\u00aa\7@\2\2\u00aa\u00ab\3\2\2\2"+ + "\u00ab\u00ac\b\r\6\2\u00ac\34\3\2\2\2\u00ad\u00ae\7\61\2\2\u00ae\u00af"+ + "\7@\2\2\u00af\u00b0\3\2\2\2\u00b0\u00b1\b\16\6\2\u00b1\36\3\2\2\2\u00b2"+ + "\u00b3\7\61\2\2\u00b3 \3\2\2\2\u00b4\u00b5\7?\2\2\u00b5\"\3\2\2\2\u00b6"+ + "\u00ba\7$\2\2\u00b7\u00b9\n\4\2\2\u00b8\u00b7\3\2\2\2\u00b9\u00bc\3\2"+ + "\2\2\u00ba\u00b8\3\2\2\2\u00ba\u00bb\3\2\2\2\u00bb\u00bd\3\2\2\2\u00bc"+ + "\u00ba\3\2\2\2\u00bd\u00c7\7$\2\2\u00be\u00c2\7)\2\2\u00bf\u00c1\n\5\2"+ + "\2\u00c0\u00bf\3\2\2\2\u00c1\u00c4\3\2\2\2\u00c2\u00c0\3\2\2\2\u00c2\u00c3"+ + "\3\2\2\2\u00c3\u00c5\3\2\2\2\u00c4\u00c2\3\2\2\2\u00c5\u00c7\7)\2\2\u00c6"+ + "\u00b6\3\2\2\2\u00c6\u00be\3\2\2\2\u00c7$\3\2\2\2\u00c8\u00cc\5/\27\2"+ + "\u00c9\u00cb\5-\26\2\u00ca\u00c9\3\2\2\2\u00cb\u00ce\3\2\2\2\u00cc\u00ca"+ + "\3\2\2\2\u00cc\u00cd\3\2\2\2\u00cd&\3\2\2\2\u00ce\u00cc\3\2\2\2\u00cf"+ + "\u00d0\t\6\2\2\u00d0\u00d1\3\2\2\2\u00d1\u00d2\b\23\2\2\u00d2(\3\2\2\2"+ + "\u00d3\u00d4\t\7\2\2\u00d4*\3\2\2\2\u00d5\u00d6\t\b\2\2\u00d6,\3\2\2\2"+ + "\u00d7\u00dc\5/\27\2\u00d8\u00dc\t\t\2\2\u00d9\u00dc\5+\25\2\u00da\u00dc"+ + "\t\n\2\2\u00db\u00d7\3\2\2\2\u00db\u00d8\3\2\2\2\u00db\u00d9\3\2\2\2\u00db"+ + "\u00da\3\2\2\2\u00dc.\3\2\2\2\u00dd\u00df\t\13\2\2\u00de\u00dd\3\2\2\2"+ + "\u00df\60\3\2\2\2\u00e0\u00e1\7A\2\2\u00e1\u00e2\7@\2\2\u00e2\u00e3\3"+ + "\2\2\2\u00e3\u00e4\b\30\6\2\u00e4\62\3\2\2\2\u00e5\u00e6\13\2\2\2\u00e6"+ + "\u00e7\3\2\2\2\u00e7\u00e8\b\31\4\2\u00e8\64\3\2\2\2\25\2\3\4=Q^oz~\u0082"+ + "\u0085\u0087\u00a2\u00ba\u00c2\u00c6\u00cc\u00db\u00de\7\b\2\2\7\3\2\5"+ + "\2\2\7\4\2\6\2\2"; + public static final ATN _ATN = + new ATNDeserializer().deserialize(_serializedATN.toCharArray()); + static { + } +}
\ No newline at end of file diff --git a/tools/data-binding/xmlGrammar/src/main/java/android/databinding/parser/XMLLexer.tokens b/tools/data-binding/xmlGrammar/src/main/java/android/databinding/parser/XMLLexer.tokens new file mode 100644 index 0000000..cd122a4 --- /dev/null +++ b/tools/data-binding/xmlGrammar/src/main/java/android/databinding/parser/XMLLexer.tokens @@ -0,0 +1,23 @@ +OPEN=7 +CDATA=2 +SLASH=13 +CharRef=5 +SEA_WS=6 +SPECIAL_CLOSE=11 +CLOSE=10 +DTD=3 +Name=16 +EQUALS=14 +PI=18 +S=17 +SLASH_CLOSE=12 +TEXT=9 +COMMENT=1 +XMLDeclOpen=8 +EntityRef=4 +STRING=15 +'='=14 +'/'=13 +'<'=7 +'/>'=12 +'>'=10 diff --git a/tools/data-binding/xmlGrammar/src/main/java/android/databinding/parser/XMLParser.java b/tools/data-binding/xmlGrammar/src/main/java/android/databinding/parser/XMLParser.java new file mode 100644 index 0000000..f18a5f0 --- /dev/null +++ b/tools/data-binding/xmlGrammar/src/main/java/android/databinding/parser/XMLParser.java @@ -0,0 +1,660 @@ +// Generated from XMLParser.g4 by ANTLR 4.4 +package android.databinding.parser; +import org.antlr.v4.runtime.atn.*; +import org.antlr.v4.runtime.dfa.DFA; +import org.antlr.v4.runtime.*; +import org.antlr.v4.runtime.misc.*; +import org.antlr.v4.runtime.tree.*; +import java.util.List; +import java.util.Iterator; +import java.util.ArrayList; + +public class XMLParser extends Parser { + public static final int + OPEN=7, CDATA=2, SLASH=13, CharRef=5, SEA_WS=6, SPECIAL_CLOSE=11, CLOSE=10, + DTD=3, Name=16, EQUALS=14, PI=18, S=17, SLASH_CLOSE=12, TEXT=9, COMMENT=1, + XMLDeclOpen=8, EntityRef=4, STRING=15; + public static final String[] tokenNames = { + "<INVALID>", "COMMENT", "CDATA", "DTD", "EntityRef", "CharRef", "SEA_WS", + "'<'", "XMLDeclOpen", "TEXT", "'>'", "SPECIAL_CLOSE", "'/>'", "'/'", "'='", + "STRING", "Name", "S", "PI" + }; + public static final int + RULE_document = 0, RULE_prolog = 1, RULE_content = 2, RULE_element = 3, + RULE_reference = 4, RULE_attribute = 5, RULE_chardata = 6, RULE_misc = 7; + public static final String[] ruleNames = { + "document", "prolog", "content", "element", "reference", "attribute", + "chardata", "misc" + }; + + @Override + public String getGrammarFileName() { return "XMLParser.g4"; } + + @Override + public String[] getTokenNames() { return tokenNames; } + + @Override + public String[] getRuleNames() { return ruleNames; } + + @Override + public String getSerializedATN() { return _serializedATN; } + + public XMLParser(TokenStream input) { + super(input); + _interp = new ParserATNSimulator(this,_ATN); + } + public static class DocumentContext extends ParserRuleContext { + public ElementContext element() { + return getRuleContext(ElementContext.class,0); + } + public List<? extends MiscContext> misc() { + return getRuleContexts(MiscContext.class); + } + public PrologContext prolog() { + return getRuleContext(PrologContext.class,0); + } + public MiscContext misc(int i) { + return getRuleContext(MiscContext.class,i); + } + public DocumentContext(ParserRuleContext parent, int invokingState) { + super(parent, invokingState); + } + @Override public int getRuleIndex() { return RULE_document; } + @Override + public void enterRule(ParseTreeListener listener) { + if ( listener instanceof XMLParserListener ) ((XMLParserListener)listener).enterDocument(this); + } + @Override + public void exitRule(ParseTreeListener listener) { + if ( listener instanceof XMLParserListener ) ((XMLParserListener)listener).exitDocument(this); + } + @Override + public <Result> Result accept(ParseTreeVisitor<? extends Result> visitor) { + if ( visitor instanceof XMLParserVisitor<?> ) return ((XMLParserVisitor<? extends Result>)visitor).visitDocument(this); + else return visitor.visitChildren(this); + } + } + + @RuleVersion(0) + public final DocumentContext document() throws RecognitionException { + DocumentContext _localctx = new DocumentContext(_ctx, getState()); + enterRule(_localctx, 0, RULE_document); + int _la; + try { + enterOuterAlt(_localctx, 1); + { + setState(17); + _la = _input.LA(1); + if (_la==XMLDeclOpen) { + { + setState(16); prolog(); + } + } + + setState(22); + _errHandler.sync(this); + _la = _input.LA(1); + while ((((_la) & ~0x3f) == 0 && ((1L << _la) & ((1L << COMMENT) | (1L << SEA_WS) | (1L << PI))) != 0)) { + { + { + setState(19); misc(); + } + } + setState(24); + _errHandler.sync(this); + _la = _input.LA(1); + } + setState(25); element(); + setState(29); + _errHandler.sync(this); + _la = _input.LA(1); + while ((((_la) & ~0x3f) == 0 && ((1L << _la) & ((1L << COMMENT) | (1L << SEA_WS) | (1L << PI))) != 0)) { + { + { + setState(26); misc(); + } + } + setState(31); + _errHandler.sync(this); + _la = _input.LA(1); + } + } + } + catch (RecognitionException re) { + _localctx.exception = re; + _errHandler.reportError(this, re); + _errHandler.recover(this, re); + } + finally { + exitRule(); + } + return _localctx; + } + + public static class PrologContext extends ParserRuleContext { + public TerminalNode SPECIAL_CLOSE() { return getToken(XMLParser.SPECIAL_CLOSE, 0); } + public List<? extends AttributeContext> attribute() { + return getRuleContexts(AttributeContext.class); + } + public AttributeContext attribute(int i) { + return getRuleContext(AttributeContext.class,i); + } + public TerminalNode XMLDeclOpen() { return getToken(XMLParser.XMLDeclOpen, 0); } + public PrologContext(ParserRuleContext parent, int invokingState) { + super(parent, invokingState); + } + @Override public int getRuleIndex() { return RULE_prolog; } + @Override + public void enterRule(ParseTreeListener listener) { + if ( listener instanceof XMLParserListener ) ((XMLParserListener)listener).enterProlog(this); + } + @Override + public void exitRule(ParseTreeListener listener) { + if ( listener instanceof XMLParserListener ) ((XMLParserListener)listener).exitProlog(this); + } + @Override + public <Result> Result accept(ParseTreeVisitor<? extends Result> visitor) { + if ( visitor instanceof XMLParserVisitor<?> ) return ((XMLParserVisitor<? extends Result>)visitor).visitProlog(this); + else return visitor.visitChildren(this); + } + } + + @RuleVersion(0) + public final PrologContext prolog() throws RecognitionException { + PrologContext _localctx = new PrologContext(_ctx, getState()); + enterRule(_localctx, 2, RULE_prolog); + int _la; + try { + enterOuterAlt(_localctx, 1); + { + setState(32); match(XMLDeclOpen); + setState(36); + _errHandler.sync(this); + _la = _input.LA(1); + while (_la==Name) { + { + { + setState(33); attribute(); + } + } + setState(38); + _errHandler.sync(this); + _la = _input.LA(1); + } + setState(39); match(SPECIAL_CLOSE); + } + } + catch (RecognitionException re) { + _localctx.exception = re; + _errHandler.reportError(this, re); + _errHandler.recover(this, re); + } + finally { + exitRule(); + } + return _localctx; + } + + public static class ContentContext extends ParserRuleContext { + public List<? extends TerminalNode> PI() { return getTokens(XMLParser.PI); } + public List<? extends TerminalNode> CDATA() { return getTokens(XMLParser.CDATA); } + public List<? extends ElementContext> element() { + return getRuleContexts(ElementContext.class); + } + public TerminalNode PI(int i) { + return getToken(XMLParser.PI, i); + } + public ElementContext element(int i) { + return getRuleContext(ElementContext.class,i); + } + public TerminalNode COMMENT(int i) { + return getToken(XMLParser.COMMENT, i); + } + public TerminalNode CDATA(int i) { + return getToken(XMLParser.CDATA, i); + } + public ReferenceContext reference(int i) { + return getRuleContext(ReferenceContext.class,i); + } + public List<? extends TerminalNode> COMMENT() { return getTokens(XMLParser.COMMENT); } + public ChardataContext chardata(int i) { + return getRuleContext(ChardataContext.class,i); + } + public List<? extends ChardataContext> chardata() { + return getRuleContexts(ChardataContext.class); + } + public List<? extends ReferenceContext> reference() { + return getRuleContexts(ReferenceContext.class); + } + public ContentContext(ParserRuleContext parent, int invokingState) { + super(parent, invokingState); + } + @Override public int getRuleIndex() { return RULE_content; } + @Override + public void enterRule(ParseTreeListener listener) { + if ( listener instanceof XMLParserListener ) ((XMLParserListener)listener).enterContent(this); + } + @Override + public void exitRule(ParseTreeListener listener) { + if ( listener instanceof XMLParserListener ) ((XMLParserListener)listener).exitContent(this); + } + @Override + public <Result> Result accept(ParseTreeVisitor<? extends Result> visitor) { + if ( visitor instanceof XMLParserVisitor<?> ) return ((XMLParserVisitor<? extends Result>)visitor).visitContent(this); + else return visitor.visitChildren(this); + } + } + + @RuleVersion(0) + public final ContentContext content() throws RecognitionException { + ContentContext _localctx = new ContentContext(_ctx, getState()); + enterRule(_localctx, 4, RULE_content); + int _la; + try { + int _alt; + enterOuterAlt(_localctx, 1); + { + setState(42); + _la = _input.LA(1); + if (_la==SEA_WS || _la==TEXT) { + { + setState(41); chardata(); + } + } + + setState(56); + _errHandler.sync(this); + _alt = getInterpreter().adaptivePredict(_input,7,_ctx); + while ( _alt!=2 && _alt!=org.antlr.v4.runtime.atn.ATN.INVALID_ALT_NUMBER ) { + if ( _alt==1 ) { + { + { + setState(49); + switch (_input.LA(1)) { + case OPEN: + { + setState(44); element(); + } + break; + case EntityRef: + case CharRef: + { + setState(45); reference(); + } + break; + case CDATA: + { + setState(46); match(CDATA); + } + break; + case PI: + { + setState(47); match(PI); + } + break; + case COMMENT: + { + setState(48); match(COMMENT); + } + break; + default: + throw new NoViableAltException(this); + } + setState(52); + _la = _input.LA(1); + if (_la==SEA_WS || _la==TEXT) { + { + setState(51); chardata(); + } + } + + } + } + } + setState(58); + _errHandler.sync(this); + _alt = getInterpreter().adaptivePredict(_input,7,_ctx); + } + } + } + catch (RecognitionException re) { + _localctx.exception = re; + _errHandler.reportError(this, re); + _errHandler.recover(this, re); + } + finally { + exitRule(); + } + return _localctx; + } + + public static class ElementContext extends ParserRuleContext { + public Token elmName; + public List<? extends AttributeContext> attribute() { + return getRuleContexts(AttributeContext.class); + } + public AttributeContext attribute(int i) { + return getRuleContext(AttributeContext.class,i); + } + public TerminalNode Name(int i) { + return getToken(XMLParser.Name, i); + } + public List<? extends TerminalNode> Name() { return getTokens(XMLParser.Name); } + public ContentContext content() { + return getRuleContext(ContentContext.class,0); + } + public ElementContext(ParserRuleContext parent, int invokingState) { + super(parent, invokingState); + } + @Override public int getRuleIndex() { return RULE_element; } + @Override + public void enterRule(ParseTreeListener listener) { + if ( listener instanceof XMLParserListener ) ((XMLParserListener)listener).enterElement(this); + } + @Override + public void exitRule(ParseTreeListener listener) { + if ( listener instanceof XMLParserListener ) ((XMLParserListener)listener).exitElement(this); + } + @Override + public <Result> Result accept(ParseTreeVisitor<? extends Result> visitor) { + if ( visitor instanceof XMLParserVisitor<?> ) return ((XMLParserVisitor<? extends Result>)visitor).visitElement(this); + else return visitor.visitChildren(this); + } + } + + @RuleVersion(0) + public final ElementContext element() throws RecognitionException { + ElementContext _localctx = new ElementContext(_ctx, getState()); + enterRule(_localctx, 6, RULE_element); + int _la; + try { + setState(83); + switch ( getInterpreter().adaptivePredict(_input,10,_ctx) ) { + case 1: + enterOuterAlt(_localctx, 1); + { + setState(59); match(OPEN); + setState(60); _localctx.elmName = match(Name); + setState(64); + _errHandler.sync(this); + _la = _input.LA(1); + while (_la==Name) { + { + { + setState(61); attribute(); + } + } + setState(66); + _errHandler.sync(this); + _la = _input.LA(1); + } + setState(67); match(CLOSE); + setState(68); content(); + setState(69); match(OPEN); + setState(70); match(SLASH); + setState(71); match(Name); + setState(72); match(CLOSE); + } + break; + + case 2: + enterOuterAlt(_localctx, 2); + { + setState(74); match(OPEN); + setState(75); _localctx.elmName = match(Name); + setState(79); + _errHandler.sync(this); + _la = _input.LA(1); + while (_la==Name) { + { + { + setState(76); attribute(); + } + } + setState(81); + _errHandler.sync(this); + _la = _input.LA(1); + } + setState(82); match(SLASH_CLOSE); + } + break; + } + } + catch (RecognitionException re) { + _localctx.exception = re; + _errHandler.reportError(this, re); + _errHandler.recover(this, re); + } + finally { + exitRule(); + } + return _localctx; + } + + public static class ReferenceContext extends ParserRuleContext { + public TerminalNode CharRef() { return getToken(XMLParser.CharRef, 0); } + public TerminalNode EntityRef() { return getToken(XMLParser.EntityRef, 0); } + public ReferenceContext(ParserRuleContext parent, int invokingState) { + super(parent, invokingState); + } + @Override public int getRuleIndex() { return RULE_reference; } + @Override + public void enterRule(ParseTreeListener listener) { + if ( listener instanceof XMLParserListener ) ((XMLParserListener)listener).enterReference(this); + } + @Override + public void exitRule(ParseTreeListener listener) { + if ( listener instanceof XMLParserListener ) ((XMLParserListener)listener).exitReference(this); + } + @Override + public <Result> Result accept(ParseTreeVisitor<? extends Result> visitor) { + if ( visitor instanceof XMLParserVisitor<?> ) return ((XMLParserVisitor<? extends Result>)visitor).visitReference(this); + else return visitor.visitChildren(this); + } + } + + @RuleVersion(0) + public final ReferenceContext reference() throws RecognitionException { + ReferenceContext _localctx = new ReferenceContext(_ctx, getState()); + enterRule(_localctx, 8, RULE_reference); + int _la; + try { + enterOuterAlt(_localctx, 1); + { + setState(85); + _la = _input.LA(1); + if ( !(_la==EntityRef || _la==CharRef) ) { + _errHandler.recoverInline(this); + } + consume(); + } + } + catch (RecognitionException re) { + _localctx.exception = re; + _errHandler.reportError(this, re); + _errHandler.recover(this, re); + } + finally { + exitRule(); + } + return _localctx; + } + + public static class AttributeContext extends ParserRuleContext { + public Token attrName; + public Token attrValue; + public TerminalNode Name() { return getToken(XMLParser.Name, 0); } + public TerminalNode STRING() { return getToken(XMLParser.STRING, 0); } + public AttributeContext(ParserRuleContext parent, int invokingState) { + super(parent, invokingState); + } + @Override public int getRuleIndex() { return RULE_attribute; } + @Override + public void enterRule(ParseTreeListener listener) { + if ( listener instanceof XMLParserListener ) ((XMLParserListener)listener).enterAttribute(this); + } + @Override + public void exitRule(ParseTreeListener listener) { + if ( listener instanceof XMLParserListener ) ((XMLParserListener)listener).exitAttribute(this); + } + @Override + public <Result> Result accept(ParseTreeVisitor<? extends Result> visitor) { + if ( visitor instanceof XMLParserVisitor<?> ) return ((XMLParserVisitor<? extends Result>)visitor).visitAttribute(this); + else return visitor.visitChildren(this); + } + } + + @RuleVersion(0) + public final AttributeContext attribute() throws RecognitionException { + AttributeContext _localctx = new AttributeContext(_ctx, getState()); + enterRule(_localctx, 10, RULE_attribute); + try { + enterOuterAlt(_localctx, 1); + { + setState(87); _localctx.attrName = match(Name); + setState(88); match(EQUALS); + setState(89); _localctx.attrValue = match(STRING); + } + } + catch (RecognitionException re) { + _localctx.exception = re; + _errHandler.reportError(this, re); + _errHandler.recover(this, re); + } + finally { + exitRule(); + } + return _localctx; + } + + public static class ChardataContext extends ParserRuleContext { + public TerminalNode SEA_WS() { return getToken(XMLParser.SEA_WS, 0); } + public TerminalNode TEXT() { return getToken(XMLParser.TEXT, 0); } + public ChardataContext(ParserRuleContext parent, int invokingState) { + super(parent, invokingState); + } + @Override public int getRuleIndex() { return RULE_chardata; } + @Override + public void enterRule(ParseTreeListener listener) { + if ( listener instanceof XMLParserListener ) ((XMLParserListener)listener).enterChardata(this); + } + @Override + public void exitRule(ParseTreeListener listener) { + if ( listener instanceof XMLParserListener ) ((XMLParserListener)listener).exitChardata(this); + } + @Override + public <Result> Result accept(ParseTreeVisitor<? extends Result> visitor) { + if ( visitor instanceof XMLParserVisitor<?> ) return ((XMLParserVisitor<? extends Result>)visitor).visitChardata(this); + else return visitor.visitChildren(this); + } + } + + @RuleVersion(0) + public final ChardataContext chardata() throws RecognitionException { + ChardataContext _localctx = new ChardataContext(_ctx, getState()); + enterRule(_localctx, 12, RULE_chardata); + int _la; + try { + enterOuterAlt(_localctx, 1); + { + setState(91); + _la = _input.LA(1); + if ( !(_la==SEA_WS || _la==TEXT) ) { + _errHandler.recoverInline(this); + } + consume(); + } + } + catch (RecognitionException re) { + _localctx.exception = re; + _errHandler.reportError(this, re); + _errHandler.recover(this, re); + } + finally { + exitRule(); + } + return _localctx; + } + + public static class MiscContext extends ParserRuleContext { + public TerminalNode SEA_WS() { return getToken(XMLParser.SEA_WS, 0); } + public TerminalNode PI() { return getToken(XMLParser.PI, 0); } + public TerminalNode COMMENT() { return getToken(XMLParser.COMMENT, 0); } + public MiscContext(ParserRuleContext parent, int invokingState) { + super(parent, invokingState); + } + @Override public int getRuleIndex() { return RULE_misc; } + @Override + public void enterRule(ParseTreeListener listener) { + if ( listener instanceof XMLParserListener ) ((XMLParserListener)listener).enterMisc(this); + } + @Override + public void exitRule(ParseTreeListener listener) { + if ( listener instanceof XMLParserListener ) ((XMLParserListener)listener).exitMisc(this); + } + @Override + public <Result> Result accept(ParseTreeVisitor<? extends Result> visitor) { + if ( visitor instanceof XMLParserVisitor<?> ) return ((XMLParserVisitor<? extends Result>)visitor).visitMisc(this); + else return visitor.visitChildren(this); + } + } + + @RuleVersion(0) + public final MiscContext misc() throws RecognitionException { + MiscContext _localctx = new MiscContext(_ctx, getState()); + enterRule(_localctx, 14, RULE_misc); + int _la; + try { + enterOuterAlt(_localctx, 1); + { + setState(93); + _la = _input.LA(1); + if ( !((((_la) & ~0x3f) == 0 && ((1L << _la) & ((1L << COMMENT) | (1L << SEA_WS) | (1L << PI))) != 0)) ) { + _errHandler.recoverInline(this); + } + consume(); + } + } + catch (RecognitionException re) { + _localctx.exception = re; + _errHandler.reportError(this, re); + _errHandler.recover(this, re); + } + finally { + exitRule(); + } + return _localctx; + } + + public static final String _serializedATN = + "\3\uaf6f\u8320\u479d\ub75c\u4880\u1605\u191c\uab37\3\24b\4\2\t\2\4\3\t"+ + "\3\4\4\t\4\4\5\t\5\4\6\t\6\4\7\t\7\4\b\t\b\4\t\t\t\3\2\5\2\24\n\2\3\2"+ + "\7\2\27\n\2\f\2\16\2\32\13\2\3\2\3\2\7\2\36\n\2\f\2\16\2!\13\2\3\3\3\3"+ + "\7\3%\n\3\f\3\16\3(\13\3\3\3\3\3\3\4\5\4-\n\4\3\4\3\4\3\4\3\4\3\4\5\4"+ + "\64\n\4\3\4\5\4\67\n\4\7\49\n\4\f\4\16\4<\13\4\3\5\3\5\3\5\7\5A\n\5\f"+ + "\5\16\5D\13\5\3\5\3\5\3\5\3\5\3\5\3\5\3\5\3\5\3\5\3\5\7\5P\n\5\f\5\16"+ + "\5S\13\5\3\5\5\5V\n\5\3\6\3\6\3\7\3\7\3\7\3\7\3\b\3\b\3\t\3\t\3\t\2\2"+ + "\2\n\2\2\4\2\6\2\b\2\n\2\f\2\16\2\20\2\2\5\3\2\6\7\4\2\b\b\13\13\5\2\3"+ + "\3\b\b\24\24g\2\23\3\2\2\2\4\"\3\2\2\2\6,\3\2\2\2\bU\3\2\2\2\nW\3\2\2"+ + "\2\fY\3\2\2\2\16]\3\2\2\2\20_\3\2\2\2\22\24\5\4\3\2\23\22\3\2\2\2\23\24"+ + "\3\2\2\2\24\30\3\2\2\2\25\27\5\20\t\2\26\25\3\2\2\2\27\32\3\2\2\2\30\26"+ + "\3\2\2\2\30\31\3\2\2\2\31\33\3\2\2\2\32\30\3\2\2\2\33\37\5\b\5\2\34\36"+ + "\5\20\t\2\35\34\3\2\2\2\36!\3\2\2\2\37\35\3\2\2\2\37 \3\2\2\2 \3\3\2\2"+ + "\2!\37\3\2\2\2\"&\7\n\2\2#%\5\f\7\2$#\3\2\2\2%(\3\2\2\2&$\3\2\2\2&\'\3"+ + "\2\2\2\')\3\2\2\2(&\3\2\2\2)*\7\r\2\2*\5\3\2\2\2+-\5\16\b\2,+\3\2\2\2"+ + ",-\3\2\2\2-:\3\2\2\2.\64\5\b\5\2/\64\5\n\6\2\60\64\7\4\2\2\61\64\7\24"+ + "\2\2\62\64\7\3\2\2\63.\3\2\2\2\63/\3\2\2\2\63\60\3\2\2\2\63\61\3\2\2\2"+ + "\63\62\3\2\2\2\64\66\3\2\2\2\65\67\5\16\b\2\66\65\3\2\2\2\66\67\3\2\2"+ + "\2\679\3\2\2\28\63\3\2\2\29<\3\2\2\2:8\3\2\2\2:;\3\2\2\2;\7\3\2\2\2<:"+ + "\3\2\2\2=>\7\t\2\2>B\7\22\2\2?A\5\f\7\2@?\3\2\2\2AD\3\2\2\2B@\3\2\2\2"+ + "BC\3\2\2\2CE\3\2\2\2DB\3\2\2\2EF\7\f\2\2FG\5\6\4\2GH\7\t\2\2HI\7\17\2"+ + "\2IJ\7\22\2\2JK\7\f\2\2KV\3\2\2\2LM\7\t\2\2MQ\7\22\2\2NP\5\f\7\2ON\3\2"+ + "\2\2PS\3\2\2\2QO\3\2\2\2QR\3\2\2\2RT\3\2\2\2SQ\3\2\2\2TV\7\16\2\2U=\3"+ + "\2\2\2UL\3\2\2\2V\t\3\2\2\2WX\t\2\2\2X\13\3\2\2\2YZ\7\22\2\2Z[\7\20\2"+ + "\2[\\\7\21\2\2\\\r\3\2\2\2]^\t\3\2\2^\17\3\2\2\2_`\t\4\2\2`\21\3\2\2\2"+ + "\r\23\30\37&,\63\66:BQU"; + public static final ATN _ATN = + new ATNDeserializer().deserialize(_serializedATN.toCharArray()); + static { + } +}
\ No newline at end of file diff --git a/tools/data-binding/xmlGrammar/src/main/java/android/databinding/parser/XMLParser.tokens b/tools/data-binding/xmlGrammar/src/main/java/android/databinding/parser/XMLParser.tokens new file mode 100644 index 0000000..b1423a1 --- /dev/null +++ b/tools/data-binding/xmlGrammar/src/main/java/android/databinding/parser/XMLParser.tokens @@ -0,0 +1,23 @@ +OPEN=7 +CDATA=2 +SLASH=13 +CharRef=5 +SEA_WS=6 +SPECIAL_CLOSE=11 +CLOSE=10 +DTD=3 +Name=16 +EQUALS=14 +PI=18 +SLASH_CLOSE=12 +S=17 +TEXT=9 +XMLDeclOpen=8 +COMMENT=1 +EntityRef=4 +STRING=15 +'='=14 +'<'=7 +'/'=13 +'/>'=12 +'>'=10 diff --git a/tools/data-binding/xmlGrammar/src/main/java/android/databinding/parser/XMLParserBaseListener.java b/tools/data-binding/xmlGrammar/src/main/java/android/databinding/parser/XMLParserBaseListener.java new file mode 100644 index 0000000..4c2bae2 --- /dev/null +++ b/tools/data-binding/xmlGrammar/src/main/java/android/databinding/parser/XMLParserBaseListener.java @@ -0,0 +1,144 @@ +// Generated from XMLParser.g4 by ANTLR 4.4 +package android.databinding.parser; + +import org.antlr.v4.runtime.ParserRuleContext; +import org.antlr.v4.runtime.Token; +import org.antlr.v4.runtime.misc.NotNull; +import org.antlr.v4.runtime.tree.ErrorNode; +import org.antlr.v4.runtime.tree.TerminalNode; + +/** + * This class provides an empty implementation of {@link XMLParserListener}, + * which can be extended to create a listener which only needs to handle a subset + * of the available methods. + */ +public class XMLParserBaseListener implements XMLParserListener { + /** + * {@inheritDoc} + * + * <p>The default implementation does nothing.</p> + */ + @Override public void enterContent(@NotNull XMLParser.ContentContext ctx) { } + /** + * {@inheritDoc} + * + * <p>The default implementation does nothing.</p> + */ + @Override public void exitContent(@NotNull XMLParser.ContentContext ctx) { } + + /** + * {@inheritDoc} + * + * <p>The default implementation does nothing.</p> + */ + @Override public void enterElement(@NotNull XMLParser.ElementContext ctx) { } + /** + * {@inheritDoc} + * + * <p>The default implementation does nothing.</p> + */ + @Override public void exitElement(@NotNull XMLParser.ElementContext ctx) { } + + /** + * {@inheritDoc} + * + * <p>The default implementation does nothing.</p> + */ + @Override public void enterProlog(@NotNull XMLParser.PrologContext ctx) { } + /** + * {@inheritDoc} + * + * <p>The default implementation does nothing.</p> + */ + @Override public void exitProlog(@NotNull XMLParser.PrologContext ctx) { } + + /** + * {@inheritDoc} + * + * <p>The default implementation does nothing.</p> + */ + @Override public void enterDocument(@NotNull XMLParser.DocumentContext ctx) { } + /** + * {@inheritDoc} + * + * <p>The default implementation does nothing.</p> + */ + @Override public void exitDocument(@NotNull XMLParser.DocumentContext ctx) { } + + /** + * {@inheritDoc} + * + * <p>The default implementation does nothing.</p> + */ + @Override public void enterAttribute(@NotNull XMLParser.AttributeContext ctx) { } + /** + * {@inheritDoc} + * + * <p>The default implementation does nothing.</p> + */ + @Override public void exitAttribute(@NotNull XMLParser.AttributeContext ctx) { } + + /** + * {@inheritDoc} + * + * <p>The default implementation does nothing.</p> + */ + @Override public void enterChardata(@NotNull XMLParser.ChardataContext ctx) { } + /** + * {@inheritDoc} + * + * <p>The default implementation does nothing.</p> + */ + @Override public void exitChardata(@NotNull XMLParser.ChardataContext ctx) { } + + /** + * {@inheritDoc} + * + * <p>The default implementation does nothing.</p> + */ + @Override public void enterReference(@NotNull XMLParser.ReferenceContext ctx) { } + /** + * {@inheritDoc} + * + * <p>The default implementation does nothing.</p> + */ + @Override public void exitReference(@NotNull XMLParser.ReferenceContext ctx) { } + + /** + * {@inheritDoc} + * + * <p>The default implementation does nothing.</p> + */ + @Override public void enterMisc(@NotNull XMLParser.MiscContext ctx) { } + /** + * {@inheritDoc} + * + * <p>The default implementation does nothing.</p> + */ + @Override public void exitMisc(@NotNull XMLParser.MiscContext ctx) { } + + /** + * {@inheritDoc} + * + * <p>The default implementation does nothing.</p> + */ + @Override public void enterEveryRule(@NotNull ParserRuleContext ctx) { } + /** + * {@inheritDoc} + * + * <p>The default implementation does nothing.</p> + */ + @Override public void exitEveryRule(@NotNull ParserRuleContext ctx) { } + /** + * {@inheritDoc} + * + * <p>The default implementation does nothing.</p> + */ + @Override public void visitTerminal(@NotNull TerminalNode node) { } + /** + * {@inheritDoc} + * + * <p>The default implementation does nothing.</p> + */ + @Override public void visitErrorNode(@NotNull ErrorNode node) { } +}
\ No newline at end of file diff --git a/tools/data-binding/xmlGrammar/src/main/java/android/databinding/parser/XMLParserBaseVisitor.java b/tools/data-binding/xmlGrammar/src/main/java/android/databinding/parser/XMLParserBaseVisitor.java new file mode 100644 index 0000000..6b04b77 --- /dev/null +++ b/tools/data-binding/xmlGrammar/src/main/java/android/databinding/parser/XMLParserBaseVisitor.java @@ -0,0 +1,79 @@ +// Generated from XMLParser.g4 by ANTLR 4.4 +package android.databinding.parser; +import org.antlr.v4.runtime.Token; +import org.antlr.v4.runtime.misc.NotNull; +import org.antlr.v4.runtime.tree.AbstractParseTreeVisitor; + +/** + * This class provides an empty implementation of {@link XMLParserVisitor}, + * which can be extended to create a visitor which only needs to handle a subset + * of the available methods. + * + * @param <Result> The return type of the visit operation. Use {@link Void} for + * operations with no return type. + */ +public class XMLParserBaseVisitor<Result> extends AbstractParseTreeVisitor<Result> implements XMLParserVisitor<Result> { + /** + * {@inheritDoc} + * + * <p>The default implementation returns the result of calling + * {@link #visitChildren} on {@code ctx}.</p> + */ + @Override public Result visitContent(@NotNull XMLParser.ContentContext ctx) { return visitChildren(ctx); } + + /** + * {@inheritDoc} + * + * <p>The default implementation returns the result of calling + * {@link #visitChildren} on {@code ctx}.</p> + */ + @Override public Result visitElement(@NotNull XMLParser.ElementContext ctx) { return visitChildren(ctx); } + + /** + * {@inheritDoc} + * + * <p>The default implementation returns the result of calling + * {@link #visitChildren} on {@code ctx}.</p> + */ + @Override public Result visitProlog(@NotNull XMLParser.PrologContext ctx) { return visitChildren(ctx); } + + /** + * {@inheritDoc} + * + * <p>The default implementation returns the result of calling + * {@link #visitChildren} on {@code ctx}.</p> + */ + @Override public Result visitDocument(@NotNull XMLParser.DocumentContext ctx) { return visitChildren(ctx); } + + /** + * {@inheritDoc} + * + * <p>The default implementation returns the result of calling + * {@link #visitChildren} on {@code ctx}.</p> + */ + @Override public Result visitAttribute(@NotNull XMLParser.AttributeContext ctx) { return visitChildren(ctx); } + + /** + * {@inheritDoc} + * + * <p>The default implementation returns the result of calling + * {@link #visitChildren} on {@code ctx}.</p> + */ + @Override public Result visitChardata(@NotNull XMLParser.ChardataContext ctx) { return visitChildren(ctx); } + + /** + * {@inheritDoc} + * + * <p>The default implementation returns the result of calling + * {@link #visitChildren} on {@code ctx}.</p> + */ + @Override public Result visitReference(@NotNull XMLParser.ReferenceContext ctx) { return visitChildren(ctx); } + + /** + * {@inheritDoc} + * + * <p>The default implementation returns the result of calling + * {@link #visitChildren} on {@code ctx}.</p> + */ + @Override public Result visitMisc(@NotNull XMLParser.MiscContext ctx) { return visitChildren(ctx); } +}
\ No newline at end of file diff --git a/tools/data-binding/xmlGrammar/src/main/java/android/databinding/parser/XMLParserListener.java b/tools/data-binding/xmlGrammar/src/main/java/android/databinding/parser/XMLParserListener.java new file mode 100644 index 0000000..6bee172 --- /dev/null +++ b/tools/data-binding/xmlGrammar/src/main/java/android/databinding/parser/XMLParserListener.java @@ -0,0 +1,99 @@ +// Generated from XMLParser.g4 by ANTLR 4.4 +package android.databinding.parser; +import org.antlr.v4.runtime.Token; +import org.antlr.v4.runtime.misc.NotNull; +import org.antlr.v4.runtime.tree.ParseTreeListener; + +/** + * This interface defines a complete listener for a parse tree produced by + * {@link XMLParser}. + */ +public interface XMLParserListener extends ParseTreeListener { + /** + * Enter a parse tree produced by {@link XMLParser#content}. + * @param ctx the parse tree + */ + void enterContent(@NotNull XMLParser.ContentContext ctx); + /** + * Exit a parse tree produced by {@link XMLParser#content}. + * @param ctx the parse tree + */ + void exitContent(@NotNull XMLParser.ContentContext ctx); + + /** + * Enter a parse tree produced by {@link XMLParser#element}. + * @param ctx the parse tree + */ + void enterElement(@NotNull XMLParser.ElementContext ctx); + /** + * Exit a parse tree produced by {@link XMLParser#element}. + * @param ctx the parse tree + */ + void exitElement(@NotNull XMLParser.ElementContext ctx); + + /** + * Enter a parse tree produced by {@link XMLParser#prolog}. + * @param ctx the parse tree + */ + void enterProlog(@NotNull XMLParser.PrologContext ctx); + /** + * Exit a parse tree produced by {@link XMLParser#prolog}. + * @param ctx the parse tree + */ + void exitProlog(@NotNull XMLParser.PrologContext ctx); + + /** + * Enter a parse tree produced by {@link XMLParser#document}. + * @param ctx the parse tree + */ + void enterDocument(@NotNull XMLParser.DocumentContext ctx); + /** + * Exit a parse tree produced by {@link XMLParser#document}. + * @param ctx the parse tree + */ + void exitDocument(@NotNull XMLParser.DocumentContext ctx); + + /** + * Enter a parse tree produced by {@link XMLParser#attribute}. + * @param ctx the parse tree + */ + void enterAttribute(@NotNull XMLParser.AttributeContext ctx); + /** + * Exit a parse tree produced by {@link XMLParser#attribute}. + * @param ctx the parse tree + */ + void exitAttribute(@NotNull XMLParser.AttributeContext ctx); + + /** + * Enter a parse tree produced by {@link XMLParser#chardata}. + * @param ctx the parse tree + */ + void enterChardata(@NotNull XMLParser.ChardataContext ctx); + /** + * Exit a parse tree produced by {@link XMLParser#chardata}. + * @param ctx the parse tree + */ + void exitChardata(@NotNull XMLParser.ChardataContext ctx); + + /** + * Enter a parse tree produced by {@link XMLParser#reference}. + * @param ctx the parse tree + */ + void enterReference(@NotNull XMLParser.ReferenceContext ctx); + /** + * Exit a parse tree produced by {@link XMLParser#reference}. + * @param ctx the parse tree + */ + void exitReference(@NotNull XMLParser.ReferenceContext ctx); + + /** + * Enter a parse tree produced by {@link XMLParser#misc}. + * @param ctx the parse tree + */ + void enterMisc(@NotNull XMLParser.MiscContext ctx); + /** + * Exit a parse tree produced by {@link XMLParser#misc}. + * @param ctx the parse tree + */ + void exitMisc(@NotNull XMLParser.MiscContext ctx); +}
\ No newline at end of file diff --git a/tools/data-binding/xmlGrammar/src/main/java/android/databinding/parser/XMLParserVisitor.java b/tools/data-binding/xmlGrammar/src/main/java/android/databinding/parser/XMLParserVisitor.java new file mode 100644 index 0000000..6a76a00 --- /dev/null +++ b/tools/data-binding/xmlGrammar/src/main/java/android/databinding/parser/XMLParserVisitor.java @@ -0,0 +1,70 @@ +// Generated from XMLParser.g4 by ANTLR 4.4 +package android.databinding.parser; +import org.antlr.v4.runtime.Token; +import org.antlr.v4.runtime.misc.NotNull; +import org.antlr.v4.runtime.tree.ParseTreeVisitor; + +/** + * This interface defines a complete generic visitor for a parse tree produced + * by {@link XMLParser}. + * + * @param <Result> The return type of the visit operation. Use {@link Void} for + * operations with no return type. + */ +public interface XMLParserVisitor<Result> extends ParseTreeVisitor<Result> { + /** + * Visit a parse tree produced by {@link XMLParser#content}. + * @param ctx the parse tree + * @return the visitor result + */ + Result visitContent(@NotNull XMLParser.ContentContext ctx); + + /** + * Visit a parse tree produced by {@link XMLParser#element}. + * @param ctx the parse tree + * @return the visitor result + */ + Result visitElement(@NotNull XMLParser.ElementContext ctx); + + /** + * Visit a parse tree produced by {@link XMLParser#prolog}. + * @param ctx the parse tree + * @return the visitor result + */ + Result visitProlog(@NotNull XMLParser.PrologContext ctx); + + /** + * Visit a parse tree produced by {@link XMLParser#document}. + * @param ctx the parse tree + * @return the visitor result + */ + Result visitDocument(@NotNull XMLParser.DocumentContext ctx); + + /** + * Visit a parse tree produced by {@link XMLParser#attribute}. + * @param ctx the parse tree + * @return the visitor result + */ + Result visitAttribute(@NotNull XMLParser.AttributeContext ctx); + + /** + * Visit a parse tree produced by {@link XMLParser#chardata}. + * @param ctx the parse tree + * @return the visitor result + */ + Result visitChardata(@NotNull XMLParser.ChardataContext ctx); + + /** + * Visit a parse tree produced by {@link XMLParser#reference}. + * @param ctx the parse tree + * @return the visitor result + */ + Result visitReference(@NotNull XMLParser.ReferenceContext ctx); + + /** + * Visit a parse tree produced by {@link XMLParser#misc}. + * @param ctx the parse tree + * @return the visitor result + */ + Result visitMisc(@NotNull XMLParser.MiscContext ctx); +}
\ No newline at end of file diff --git a/tools/data-binding/xmlGrammar/src/main/kotlin/xmlEditorTest.kt b/tools/data-binding/xmlGrammar/src/main/kotlin/xmlEditorTest.kt new file mode 100644 index 0000000..48356ad --- /dev/null +++ b/tools/data-binding/xmlGrammar/src/main/kotlin/xmlEditorTest.kt @@ -0,0 +1,131 @@ +/* + * Copyright (C) 2014 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 android.databinding.parser + +import java.io.File +import org.antlr.v4.runtime.ANTLRInputStream +import org.antlr.v4.runtime.CommonTokenStream +import java.io.FileReader +import org.antlr.v4.runtime.Token +import java.util.Comparator +import kotlin.properties.Delegates + +fun main(vararg args : String) { + val f = File("/Volumes/ssd/src/data-binding/KDataBinder/samples/BindingDemo/app/src/main/res/layout/main_activity.xml") + antlrTest(f); +} + +fun log(f : () -> String) { + System.out.println("LOG: ${f()}"); +} + +fun antlrTest(f: File) : String? { + val inputStream = ANTLRInputStream(FileReader(f)) + val lexer = XMLLexer(inputStream) + val tokenStream = CommonTokenStream(lexer) + val parser = XMLParser(tokenStream) + val expr = parser.document() + log{"exp tree: ${expr.toStringTree(parser)}"} + val reservedElementNames = arrayListOf("variable", "import") + val visitor = object : XMLParserBaseVisitor<MutableList<Pair<Position, Position>>>() { + override fun visitAttribute(ctx: XMLParser.AttributeContext): MutableList<Pair<Position, Position>>? { + log{"attr:${ctx.attrName.getText()} ${ctx.attrValue.getText()}"} + if (ctx.attrName.getText().startsWith("bind:")) { + + return arrayListOf(Pair(ctx.getStart().toPosition(), ctx.getStop().toEndPosition())) + } else if (ctx.attrValue.getText().startsWith("\"@{") && ctx.attrValue.getText().endsWith("}\"")) { + return arrayListOf(Pair(ctx.getStart().toPosition(), ctx.getStop().toEndPosition())) + } + + //log{"visiting attr: ${ctx.getText()} at location ${ctx.getStart().toS()} ${ctx.getStop().toS()}"} + return super<XMLParserBaseVisitor>.visitAttribute(ctx) + } + + override fun visitElement(ctx: XMLParser.ElementContext): MutableList<Pair<Position, Position>>? { + log{"elm ${ctx.elmName.getText()} || ${ctx.Name()}"} + if (reservedElementNames.contains(ctx.elmName?.getText()) || ctx.elmName.getText().startsWith("bind:")) { + return arrayListOf(Pair(ctx.getStart().toPosition(), ctx.getStop().toEndPosition())) + } + return super< XMLParserBaseVisitor>.visitElement(ctx) + } + + override fun defaultResult(): MutableList<Pair<Position, Position>>? = arrayListOf() + + override fun aggregateResult(aggregate: MutableList<Pair<Position, Position>>?, nextResult: MutableList<Pair<Position, Position>>?): MutableList<Pair<Position, Position>>? { + return if (aggregate == null) { + return nextResult + } else if (nextResult == null) { + return aggregate + } else { + aggregate.addAll(nextResult) + return aggregate + } + } + } + val parsedExpr = expr.accept(visitor) + if (parsedExpr.size() == 0) { + return null//nothing to strip + } + log {"result ${parsedExpr.joinToString("\n-> ")}"} + parsedExpr.forEach { + log {"${it.first.line} ${it.first.charIndex}"} + } + val out = StringBuilder() + val lines = f.readLines("utf-8") + lines.forEach { out.appendln(it) } + + val sorted = parsedExpr.sortBy(object : Comparator<Pair<Position, Position>> { + override fun compare(o1: Pair<Position, Position>, o2: Pair<Position, Position>): Int { + val lineCmp = o1.first.line.compareTo(o2.first.charIndex) + if (lineCmp != 0) { + return lineCmp + } + return o1.first.line.compareTo(o2.first.charIndex) + } + }) + + var lineStarts = arrayListOf(0) + + lines.withIndices().forEach { + if (it.first > 0) { + lineStarts.add(lineStarts[it.first - 1] + lines[it.first - 1].length() + 1) + } + } + + val seperator = System.lineSeparator().charAt(0) + + sorted.forEach { + val posStart = lineStarts[it.first.line] + it.first.charIndex + val posEnd = lineStarts[it.second.line] + it.second.charIndex + for( i in posStart..(posEnd - 1)) { + if (out.charAt(i) != seperator) { + out.setCharAt(i, ' ') + } + } + } + + return out.toString() +} + + +fun org.antlr.v4.runtime.Token.toS() : String = "[L:${getLine()} CH:${getCharPositionInLine()}]" + +fun org.antlr.v4.runtime.Token.toPosition() : Position = Position(getLine() -1 , getCharPositionInLine()) + +fun org.antlr.v4.runtime.Token.toEndPosition() : Position = Position(getLine() - 1 , getCharPositionInLine() + getText().size) + +data class Position(var line : Int, var charIndex : Int) { +} |