Add package-level documentation referencing the guide. am: 52c473f
am: 84dc2cd
* commit '84dc2cd14c86ca635e28ef140bdf329fea3737f9':
Add package-level documentation referencing the guide.
Change-Id: I30296ce9006248c0f4fcae725ba087942bcf3998
diff --git a/.gitignore b/.gitignore
index 448ab8f..3093115 100644
--- a/.gitignore
+++ b/.gitignore
@@ -3,3 +3,7 @@
.gradle
maven-repo
.caches
+*.DS_Store
+.idea/workspace.xml
+.idea/libraries
+.idea/modules
diff --git a/.idea/.name b/.idea/.name
new file mode 100644
index 0000000..9e84af0
--- /dev/null
+++ b/.idea/.name
@@ -0,0 +1 @@
+data-binding
\ No newline at end of file
diff --git a/.idea/codeStyleSettings.xml b/.idea/codeStyleSettings.xml
new file mode 100755
index 0000000..003973d
--- /dev/null
+++ b/.idea/codeStyleSettings.xml
@@ -0,0 +1,160 @@
+<?xml version="1.0" encoding="UTF-8"?>
+<project version="4">
+ <component name="ProjectCodeStyleSettingsManager">
+ <option name="PER_PROJECT_SETTINGS">
+ <value>
+ <option name="LINE_SEPARATOR" value=" " />
+ <option name="FIELD_NAME_PREFIX" value="m" />
+ <option name="STATIC_FIELD_NAME_PREFIX" value="m" />
+ <option name="CLASS_COUNT_TO_USE_IMPORT_ON_DEMAND" value="99" />
+ <option name="NAMES_COUNT_TO_USE_IMPORT_ON_DEMAND" value="99" />
+ <option name="PACKAGES_TO_USE_IMPORT_ON_DEMAND">
+ <value />
+ </option>
+ <option name="IMPORT_LAYOUT_TABLE">
+ <value>
+ <package name="android" withSubpackages="true" static="false" />
+ <emptyLine />
+ <package name="com" withSubpackages="true" static="false" />
+ <emptyLine />
+ <package name="junit" withSubpackages="true" static="false" />
+ <emptyLine />
+ <package name="net" withSubpackages="true" static="false" />
+ <emptyLine />
+ <package name="org" withSubpackages="true" static="false" />
+ <emptyLine />
+ <package name="" withSubpackages="true" static="false" />
+ <emptyLine />
+ <package name="java" withSubpackages="true" static="false" />
+ <emptyLine />
+ <package name="javax" withSubpackages="true" static="false" />
+ <emptyLine />
+ <package name="" withSubpackages="true" static="true" />
+ <emptyLine />
+ </value>
+ </option>
+ <option name="RIGHT_MARGIN" value="100" />
+ <option name="JD_P_AT_EMPTY_LINES" value="false" />
+ <option name="JD_KEEP_EMPTY_PARAMETER" value="false" />
+ <option name="JD_KEEP_EMPTY_EXCEPTION" value="false" />
+ <option name="JD_KEEP_EMPTY_RETURN" value="false" />
+ <option name="KEEP_CONTROL_STATEMENT_IN_ONE_LINE" value="false" />
+ <option name="KEEP_BLANK_LINES_IN_CODE" value="1" />
+ <option name="BLANK_LINES_AROUND_FIELD" value="1" />
+ <option name="BLANK_LINES_AFTER_CLASS_HEADER" value="1" />
+ <option name="ALIGN_MULTILINE_PARAMETERS" value="false" />
+ <option name="ALIGN_MULTILINE_FOR" value="false" />
+ <option name="CALL_PARAMETERS_WRAP" value="1" />
+ <option name="METHOD_PARAMETERS_WRAP" value="1" />
+ <option name="EXTENDS_LIST_WRAP" value="1" />
+ <option name="THROWS_LIST_WRAP" value="1" />
+ <option name="EXTENDS_KEYWORD_WRAP" value="1" />
+ <option name="THROWS_KEYWORD_WRAP" value="1" />
+ <option name="METHOD_CALL_CHAIN_WRAP" value="1" />
+ <option name="BINARY_OPERATION_WRAP" value="1" />
+ <option name="BINARY_OPERATION_SIGN_ON_NEXT_LINE" value="true" />
+ <option name="TERNARY_OPERATION_WRAP" value="1" />
+ <option name="TERNARY_OPERATION_SIGNS_ON_NEXT_LINE" value="true" />
+ <option name="FOR_STATEMENT_WRAP" value="1" />
+ <option name="ARRAY_INITIALIZER_WRAP" value="1" />
+ <option name="ASSIGNMENT_WRAP" value="1" />
+ <option name="PLACE_ASSIGNMENT_SIGN_ON_NEXT_LINE" value="true" />
+ <option name="WRAP_COMMENTS" value="true" />
+ <option name="IF_BRACE_FORCE" value="3" />
+ <option name="DOWHILE_BRACE_FORCE" value="3" />
+ <option name="WHILE_BRACE_FORCE" value="3" />
+ <option name="FOR_BRACE_FORCE" value="3" />
+ <GroovyCodeStyleSettings>
+ <option name="USE_FQ_CLASS_NAMES_IN_JAVADOC" value="false" />
+ <option name="CLASS_COUNT_TO_USE_IMPORT_ON_DEMAND" value="99" />
+ <option name="NAMES_COUNT_TO_USE_IMPORT_ON_DEMAND" value="99" />
+ <option name="PACKAGES_TO_USE_IMPORT_ON_DEMAND">
+ <value />
+ </option>
+ <option name="IMPORT_LAYOUT_TABLE">
+ <value>
+ <package name="" withSubpackages="true" static="false" />
+ <emptyLine />
+ <package name="javax" withSubpackages="true" static="false" />
+ <package name="java" withSubpackages="true" static="false" />
+ <emptyLine />
+ <package name="" withSubpackages="true" static="true" />
+ <emptyLine />
+ </value>
+ </option>
+ </GroovyCodeStyleSettings>
+ <JetCodeStyleSettings>
+ <option name="PACKAGES_TO_USE_STAR_IMPORTS">
+ <value>
+ <package name="kotlinx.android.synthetic" withSubpackages="true" static="false" />
+ </value>
+ </option>
+ <option name="NAME_COUNT_TO_USE_STAR_IMPORT" value="2147483647" />
+ <option name="NAME_COUNT_TO_USE_STAR_IMPORT_FOR_MEMBERS" value="2147483647" />
+ </JetCodeStyleSettings>
+ <XML>
+ <option name="XML_LEGACY_SETTINGS_IMPORTED" value="true" />
+ </XML>
+ <codeStyleSettings language="Groovy">
+ <option name="KEEP_FIRST_COLUMN_COMMENT" value="false" />
+ <option name="KEEP_CONTROL_STATEMENT_IN_ONE_LINE" value="false" />
+ <option name="KEEP_BLANK_LINES_IN_CODE" value="1" />
+ <option name="BLANK_LINES_AROUND_FIELD" value="1" />
+ <option name="BLANK_LINES_AFTER_CLASS_HEADER" value="1" />
+ <option name="ALIGN_MULTILINE_PARAMETERS" value="false" />
+ <option name="ALIGN_MULTILINE_FOR" value="false" />
+ <option name="CALL_PARAMETERS_WRAP" value="1" />
+ <option name="METHOD_PARAMETERS_WRAP" value="1" />
+ <option name="EXTENDS_LIST_WRAP" value="1" />
+ <option name="THROWS_LIST_WRAP" value="1" />
+ <option name="EXTENDS_KEYWORD_WRAP" value="1" />
+ <option name="THROWS_KEYWORD_WRAP" value="1" />
+ <option name="METHOD_CALL_CHAIN_WRAP" value="1" />
+ <option name="BINARY_OPERATION_WRAP" value="1" />
+ <option name="TERNARY_OPERATION_WRAP" value="1" />
+ <option name="FOR_STATEMENT_WRAP" value="1" />
+ <option name="ASSIGNMENT_WRAP" value="1" />
+ <option name="IF_BRACE_FORCE" value="3" />
+ <option name="WHILE_BRACE_FORCE" value="3" />
+ <option name="FOR_BRACE_FORCE" value="3" />
+ <option name="PARENT_SETTINGS_INSTALLED" value="true" />
+ </codeStyleSettings>
+ <codeStyleSettings language="JAVA">
+ <option name="LINE_COMMENT_AT_FIRST_COLUMN" value="false" />
+ <option name="BLOCK_COMMENT_AT_FIRST_COLUMN" value="false" />
+ <option name="KEEP_CONTROL_STATEMENT_IN_ONE_LINE" value="false" />
+ <option name="KEEP_BLANK_LINES_IN_CODE" value="1" />
+ <option name="BLANK_LINES_AROUND_FIELD" value="1" />
+ <option name="BLANK_LINES_AFTER_CLASS_HEADER" value="1" />
+ <option name="ALIGN_MULTILINE_PARAMETERS" value="false" />
+ <option name="ALIGN_MULTILINE_FOR" value="false" />
+ <option name="CALL_PARAMETERS_WRAP" value="1" />
+ <option name="METHOD_PARAMETERS_WRAP" value="1" />
+ <option name="EXTENDS_LIST_WRAP" value="1" />
+ <option name="THROWS_LIST_WRAP" value="1" />
+ <option name="EXTENDS_KEYWORD_WRAP" value="1" />
+ <option name="THROWS_KEYWORD_WRAP" value="1" />
+ <option name="METHOD_CALL_CHAIN_WRAP" value="1" />
+ <option name="BINARY_OPERATION_WRAP" value="1" />
+ <option name="BINARY_OPERATION_SIGN_ON_NEXT_LINE" value="true" />
+ <option name="TERNARY_OPERATION_WRAP" value="1" />
+ <option name="TERNARY_OPERATION_SIGNS_ON_NEXT_LINE" value="true" />
+ <option name="FOR_STATEMENT_WRAP" value="1" />
+ <option name="ARRAY_INITIALIZER_WRAP" value="1" />
+ <option name="ASSIGNMENT_WRAP" value="1" />
+ <option name="PLACE_ASSIGNMENT_SIGN_ON_NEXT_LINE" value="true" />
+ <option name="IF_BRACE_FORCE" value="3" />
+ <option name="DOWHILE_BRACE_FORCE" value="3" />
+ <option name="WHILE_BRACE_FORCE" value="3" />
+ <option name="FOR_BRACE_FORCE" value="3" />
+ <option name="PARENT_SETTINGS_INSTALLED" value="true" />
+ </codeStyleSettings>
+ <codeStyleSettings language="JSON">
+ <option name="KEEP_BLANK_LINES_IN_CODE" value="1" />
+ <option name="PARENT_SETTINGS_INSTALLED" value="true" />
+ </codeStyleSettings>
+ </value>
+ </option>
+ <option name="USE_PER_PROJECT_SETTINGS" value="true" />
+ </component>
+</project>
diff --git a/.idea/compiler.xml b/.idea/compiler.xml
new file mode 100644
index 0000000..96cc43e
--- /dev/null
+++ b/.idea/compiler.xml
@@ -0,0 +1,22 @@
+<?xml version="1.0" encoding="UTF-8"?>
+<project version="4">
+ <component name="CompilerConfiguration">
+ <resourceExtensions />
+ <wildcardResourcePatterns>
+ <entry name="!?*.java" />
+ <entry name="!?*.form" />
+ <entry name="!?*.class" />
+ <entry name="!?*.groovy" />
+ <entry name="!?*.scala" />
+ <entry name="!?*.flex" />
+ <entry name="!?*.kt" />
+ <entry name="!?*.clj" />
+ <entry name="!?*.aj" />
+ </wildcardResourcePatterns>
+ <annotationProcessing>
+ <profile default="true" name="Default" enabled="false">
+ <processorPath useClasspath="true" />
+ </profile>
+ </annotationProcessing>
+ </component>
+</project>
\ No newline at end of file
diff --git a/.idea/copyright/aosp.xml b/.idea/copyright/aosp.xml
new file mode 100644
index 0000000..077aec6
--- /dev/null
+++ b/.idea/copyright/aosp.xml
@@ -0,0 +1,9 @@
+<component name="CopyrightManager">
+ <copyright>
+ <option name="notice" value="Copyright (C) &#36;today.year The Android Open Source Project Licensed under the Apache License, Version 2.0 (the "License"); you may not use this file except in compliance with the License. You may obtain a copy of the License at http://www.apache.org/licenses/LICENSE-2.0 Unless required by applicable law or agreed to in writing, software distributed under the License is distributed on an "AS IS" BASIS, WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. See the License for the specific language governing permissions and limitations under the License." />
+ <option name="keyword" value="Copyright" />
+ <option name="allowReplaceKeyword" value="" />
+ <option name="myName" value="aosp" />
+ <option name="myLocal" value="true" />
+ </copyright>
+</component>
\ No newline at end of file
diff --git a/.idea/copyright/profiles_settings.xml b/.idea/copyright/profiles_settings.xml
new file mode 100644
index 0000000..fc5a2c3
--- /dev/null
+++ b/.idea/copyright/profiles_settings.xml
@@ -0,0 +1,7 @@
+<component name="CopyrightManager">
+ <settings default="">
+ <module2copyright>
+ <element module="All" copyright="aosp" />
+ </module2copyright>
+ </settings>
+</component>
\ No newline at end of file
diff --git a/.idea/encodings.xml b/.idea/encodings.xml
new file mode 100644
index 0000000..97626ba
--- /dev/null
+++ b/.idea/encodings.xml
@@ -0,0 +1,6 @@
+<?xml version="1.0" encoding="UTF-8"?>
+<project version="4">
+ <component name="Encoding">
+ <file url="PROJECT" charset="UTF-8" />
+ </component>
+</project>
\ No newline at end of file
diff --git a/.idea/gradle.xml b/.idea/gradle.xml
new file mode 100644
index 0000000..c9e59e1
--- /dev/null
+++ b/.idea/gradle.xml
@@ -0,0 +1,33 @@
+<?xml version="1.0" encoding="UTF-8"?>
+<project version="4">
+ <component name="GradleSettings">
+ <option name="linkedExternalProjectsSettings">
+ <GradleProjectSettings>
+ <option name="distributionType" value="DEFAULT_WRAPPED" />
+ <option name="externalProjectPath" value="$PROJECT_DIR$" />
+ <option name="gradleJvm" value="1.6" />
+ <option name="modules">
+ <set>
+ <option value="$PROJECT_DIR$" />
+ <option value="$PROJECT_DIR$/baseLibrary" />
+ <option value="$PROJECT_DIR$/compilationTests" />
+ <option value="$PROJECT_DIR$/compiler" />
+ <option value="$PROJECT_DIR$/compilerCommon" />
+ <option value="$PROJECT_DIR$/dataBinding" />
+ </set>
+ </option>
+ <option name="useAutoImport" value="true" />
+ <option name="myModules">
+ <set>
+ <option value="$PROJECT_DIR$" />
+ <option value="$PROJECT_DIR$/baseLibrary" />
+ <option value="$PROJECT_DIR$/compilationTests" />
+ <option value="$PROJECT_DIR$/compiler" />
+ <option value="$PROJECT_DIR$/compilerCommon" />
+ <option value="$PROJECT_DIR$/dataBinding" />
+ </set>
+ </option>
+ </GradleProjectSettings>
+ </option>
+ </component>
+</project>
\ No newline at end of file
diff --git a/.idea/misc.xml b/.idea/misc.xml
new file mode 100644
index 0000000..caac652
--- /dev/null
+++ b/.idea/misc.xml
@@ -0,0 +1,19 @@
+<?xml version="1.0" encoding="UTF-8"?>
+<project version="4">
+ <component name="EntryPointsManager">
+ <entry_points version="2.0" />
+ </component>
+ <component name="ProjectLevelVcsManager" settingsEditedManually="false">
+ <OptionsSetting value="true" id="Add" />
+ <OptionsSetting value="true" id="Remove" />
+ <OptionsSetting value="true" id="Checkout" />
+ <OptionsSetting value="true" id="Update" />
+ <OptionsSetting value="true" id="Status" />
+ <OptionsSetting value="true" id="Edit" />
+ <ConfirmationsSetting value="0" id="Add" />
+ <ConfirmationsSetting value="0" id="Remove" />
+ </component>
+ <component name="ProjectRootManager" version="2" languageLevel="JDK_1_6" default="false" assert-keyword="true" jdk-15="true" project-jdk-name="1.6" project-jdk-type="JavaSDK">
+ <output url="file://$PROJECT_DIR$/classes" />
+ </component>
+</project>
\ No newline at end of file
diff --git a/.idea/modules.xml b/.idea/modules.xml
new file mode 100644
index 0000000..7eefe60
--- /dev/null
+++ b/.idea/modules.xml
@@ -0,0 +1,13 @@
+<?xml version="1.0" encoding="UTF-8"?>
+<project version="4">
+ <component name="ProjectModuleManager">
+ <modules>
+ <module fileurl="file://$PROJECT_DIR$/baseLibrary/baseLibrary.iml" filepath="$PROJECT_DIR$/baseLibrary/baseLibrary.iml" />
+ <module fileurl="file://$PROJECT_DIR$/compilationTests/compilationTests.iml" filepath="$PROJECT_DIR$/compilationTests/compilationTests.iml" />
+ <module fileurl="file://$PROJECT_DIR$/compiler/compiler.iml" filepath="$PROJECT_DIR$/compiler/compiler.iml" />
+ <module fileurl="file://$PROJECT_DIR$/compilerCommon/compilerCommon.iml" filepath="$PROJECT_DIR$/compilerCommon/compilerCommon.iml" />
+ <module fileurl="file://$PROJECT_DIR$/data-binding.iml" filepath="$PROJECT_DIR$/data-binding.iml" />
+ <module fileurl="file://$PROJECT_DIR$/dataBinding/dataBinding.iml" filepath="$PROJECT_DIR$/dataBinding/dataBinding.iml" />
+ </modules>
+ </component>
+</project>
\ No newline at end of file
diff --git a/.idea/uiDesigner.xml b/.idea/uiDesigner.xml
new file mode 100644
index 0000000..e96534f
--- /dev/null
+++ b/.idea/uiDesigner.xml
@@ -0,0 +1,124 @@
+<?xml version="1.0" encoding="UTF-8"?>
+<project version="4">
+ <component name="Palette2">
+ <group name="Swing">
+ <item class="com.intellij.uiDesigner.HSpacer" tooltip-text="Horizontal Spacer" icon="/com/intellij/uiDesigner/icons/hspacer.png" removable="false" auto-create-binding="false" can-attach-label="false">
+ <default-constraints vsize-policy="1" hsize-policy="6" anchor="0" fill="1" />
+ </item>
+ <item class="com.intellij.uiDesigner.VSpacer" tooltip-text="Vertical Spacer" icon="/com/intellij/uiDesigner/icons/vspacer.png" removable="false" auto-create-binding="false" can-attach-label="false">
+ <default-constraints vsize-policy="6" hsize-policy="1" anchor="0" fill="2" />
+ </item>
+ <item class="javax.swing.JPanel" icon="/com/intellij/uiDesigner/icons/panel.png" removable="false" auto-create-binding="false" can-attach-label="false">
+ <default-constraints vsize-policy="3" hsize-policy="3" anchor="0" fill="3" />
+ </item>
+ <item class="javax.swing.JScrollPane" icon="/com/intellij/uiDesigner/icons/scrollPane.png" removable="false" auto-create-binding="false" can-attach-label="true">
+ <default-constraints vsize-policy="7" hsize-policy="7" anchor="0" fill="3" />
+ </item>
+ <item class="javax.swing.JButton" icon="/com/intellij/uiDesigner/icons/button.png" removable="false" auto-create-binding="true" can-attach-label="false">
+ <default-constraints vsize-policy="0" hsize-policy="3" anchor="0" fill="1" />
+ <initial-values>
+ <property name="text" value="Button" />
+ </initial-values>
+ </item>
+ <item class="javax.swing.JRadioButton" icon="/com/intellij/uiDesigner/icons/radioButton.png" removable="false" auto-create-binding="true" can-attach-label="false">
+ <default-constraints vsize-policy="0" hsize-policy="3" anchor="8" fill="0" />
+ <initial-values>
+ <property name="text" value="RadioButton" />
+ </initial-values>
+ </item>
+ <item class="javax.swing.JCheckBox" icon="/com/intellij/uiDesigner/icons/checkBox.png" removable="false" auto-create-binding="true" can-attach-label="false">
+ <default-constraints vsize-policy="0" hsize-policy="3" anchor="8" fill="0" />
+ <initial-values>
+ <property name="text" value="CheckBox" />
+ </initial-values>
+ </item>
+ <item class="javax.swing.JLabel" icon="/com/intellij/uiDesigner/icons/label.png" removable="false" auto-create-binding="false" can-attach-label="false">
+ <default-constraints vsize-policy="0" hsize-policy="0" anchor="8" fill="0" />
+ <initial-values>
+ <property name="text" value="Label" />
+ </initial-values>
+ </item>
+ <item class="javax.swing.JTextField" icon="/com/intellij/uiDesigner/icons/textField.png" removable="false" auto-create-binding="true" can-attach-label="true">
+ <default-constraints vsize-policy="0" hsize-policy="6" anchor="8" fill="1">
+ <preferred-size width="150" height="-1" />
+ </default-constraints>
+ </item>
+ <item class="javax.swing.JPasswordField" icon="/com/intellij/uiDesigner/icons/passwordField.png" removable="false" auto-create-binding="true" can-attach-label="true">
+ <default-constraints vsize-policy="0" hsize-policy="6" anchor="8" fill="1">
+ <preferred-size width="150" height="-1" />
+ </default-constraints>
+ </item>
+ <item class="javax.swing.JFormattedTextField" icon="/com/intellij/uiDesigner/icons/formattedTextField.png" removable="false" auto-create-binding="true" can-attach-label="true">
+ <default-constraints vsize-policy="0" hsize-policy="6" anchor="8" fill="1">
+ <preferred-size width="150" height="-1" />
+ </default-constraints>
+ </item>
+ <item class="javax.swing.JTextArea" icon="/com/intellij/uiDesigner/icons/textArea.png" removable="false" auto-create-binding="true" can-attach-label="true">
+ <default-constraints vsize-policy="6" hsize-policy="6" anchor="0" fill="3">
+ <preferred-size width="150" height="50" />
+ </default-constraints>
+ </item>
+ <item class="javax.swing.JTextPane" icon="/com/intellij/uiDesigner/icons/textPane.png" removable="false" auto-create-binding="true" can-attach-label="true">
+ <default-constraints vsize-policy="6" hsize-policy="6" anchor="0" fill="3">
+ <preferred-size width="150" height="50" />
+ </default-constraints>
+ </item>
+ <item class="javax.swing.JEditorPane" icon="/com/intellij/uiDesigner/icons/editorPane.png" removable="false" auto-create-binding="true" can-attach-label="true">
+ <default-constraints vsize-policy="6" hsize-policy="6" anchor="0" fill="3">
+ <preferred-size width="150" height="50" />
+ </default-constraints>
+ </item>
+ <item class="javax.swing.JComboBox" icon="/com/intellij/uiDesigner/icons/comboBox.png" removable="false" auto-create-binding="true" can-attach-label="true">
+ <default-constraints vsize-policy="0" hsize-policy="2" anchor="8" fill="1" />
+ </item>
+ <item class="javax.swing.JTable" icon="/com/intellij/uiDesigner/icons/table.png" removable="false" auto-create-binding="true" can-attach-label="false">
+ <default-constraints vsize-policy="6" hsize-policy="6" anchor="0" fill="3">
+ <preferred-size width="150" height="50" />
+ </default-constraints>
+ </item>
+ <item class="javax.swing.JList" icon="/com/intellij/uiDesigner/icons/list.png" removable="false" auto-create-binding="true" can-attach-label="false">
+ <default-constraints vsize-policy="6" hsize-policy="2" anchor="0" fill="3">
+ <preferred-size width="150" height="50" />
+ </default-constraints>
+ </item>
+ <item class="javax.swing.JTree" icon="/com/intellij/uiDesigner/icons/tree.png" removable="false" auto-create-binding="true" can-attach-label="false">
+ <default-constraints vsize-policy="6" hsize-policy="6" anchor="0" fill="3">
+ <preferred-size width="150" height="50" />
+ </default-constraints>
+ </item>
+ <item class="javax.swing.JTabbedPane" icon="/com/intellij/uiDesigner/icons/tabbedPane.png" removable="false" auto-create-binding="true" can-attach-label="false">
+ <default-constraints vsize-policy="3" hsize-policy="3" anchor="0" fill="3">
+ <preferred-size width="200" height="200" />
+ </default-constraints>
+ </item>
+ <item class="javax.swing.JSplitPane" icon="/com/intellij/uiDesigner/icons/splitPane.png" removable="false" auto-create-binding="false" can-attach-label="false">
+ <default-constraints vsize-policy="3" hsize-policy="3" anchor="0" fill="3">
+ <preferred-size width="200" height="200" />
+ </default-constraints>
+ </item>
+ <item class="javax.swing.JSpinner" icon="/com/intellij/uiDesigner/icons/spinner.png" removable="false" auto-create-binding="true" can-attach-label="true">
+ <default-constraints vsize-policy="0" hsize-policy="6" anchor="8" fill="1" />
+ </item>
+ <item class="javax.swing.JSlider" icon="/com/intellij/uiDesigner/icons/slider.png" removable="false" auto-create-binding="true" can-attach-label="false">
+ <default-constraints vsize-policy="0" hsize-policy="6" anchor="8" fill="1" />
+ </item>
+ <item class="javax.swing.JSeparator" icon="/com/intellij/uiDesigner/icons/separator.png" removable="false" auto-create-binding="false" can-attach-label="false">
+ <default-constraints vsize-policy="6" hsize-policy="6" anchor="0" fill="3" />
+ </item>
+ <item class="javax.swing.JProgressBar" icon="/com/intellij/uiDesigner/icons/progressbar.png" removable="false" auto-create-binding="true" can-attach-label="false">
+ <default-constraints vsize-policy="0" hsize-policy="6" anchor="0" fill="1" />
+ </item>
+ <item class="javax.swing.JToolBar" icon="/com/intellij/uiDesigner/icons/toolbar.png" removable="false" auto-create-binding="false" can-attach-label="false">
+ <default-constraints vsize-policy="0" hsize-policy="6" anchor="0" fill="1">
+ <preferred-size width="-1" height="20" />
+ </default-constraints>
+ </item>
+ <item class="javax.swing.JToolBar$Separator" icon="/com/intellij/uiDesigner/icons/toolbarSeparator.png" removable="false" auto-create-binding="false" can-attach-label="false">
+ <default-constraints vsize-policy="0" hsize-policy="0" anchor="0" fill="1" />
+ </item>
+ <item class="javax.swing.JScrollBar" icon="/com/intellij/uiDesigner/icons/scrollbar.png" removable="false" auto-create-binding="true" can-attach-label="false">
+ <default-constraints vsize-policy="6" hsize-policy="0" anchor="0" fill="2" />
+ </item>
+ </group>
+ </component>
+</project>
\ No newline at end of file
diff --git a/.idea/vcs.xml b/.idea/vcs.xml
new file mode 100644
index 0000000..94a25f7
--- /dev/null
+++ b/.idea/vcs.xml
@@ -0,0 +1,6 @@
+<?xml version="1.0" encoding="UTF-8"?>
+<project version="4">
+ <component name="VcsDirectoryMappings">
+ <mapping directory="$PROJECT_DIR$" vcs="Git" />
+ </component>
+</project>
\ No newline at end of file
diff --git a/baseLibrary/baseLibrary.iml b/baseLibrary/baseLibrary.iml
new file mode 100644
index 0000000..c6163c1
--- /dev/null
+++ b/baseLibrary/baseLibrary.iml
@@ -0,0 +1,20 @@
+<?xml version="1.0" encoding="UTF-8"?>
+<module external.linked.project.id=":dataBinding:baseLibrary" external.linked.project.path="$MODULE_DIR$" external.root.project.path="$MODULE_DIR$/.." external.system.id="GRADLE" external.system.module.group="com.android.databinding" external.system.module.version="1.1" type="JAVA_MODULE" version="4">
+ <component name="NewModuleRootManager" inherit-compiler-output="false">
+ <output url="file://$MODULE_DIR$/build/classes/main" />
+ <output-test url="file://$MODULE_DIR$/build/classes/test" />
+ <exclude-output />
+ <content url="file://$MODULE_DIR$">
+ <sourceFolder url="file://$MODULE_DIR$/src/main/java" isTestSource="false" />
+ <sourceFolder url="file://$MODULE_DIR$/src/test/java" isTestSource="true" />
+ <sourceFolder url="file://$MODULE_DIR$/src/main/resources" type="java-resource" />
+ <sourceFolder url="file://$MODULE_DIR$/src/test/resources" type="java-test-resource" />
+ <excludeFolder url="file://$MODULE_DIR$/.gradle" />
+ <excludeFolder url="file://$MODULE_DIR$/build" />
+ </content>
+ <orderEntry type="inheritedJdk" />
+ <orderEntry type="sourceFolder" forTests="false" />
+ <orderEntry type="library" scope="TEST" name="Gradle: junit:junit:4.12" level="project" />
+ <orderEntry type="library" scope="TEST" name="Gradle: org.hamcrest:hamcrest-core:1.3" level="project" />
+ </component>
+</module>
\ No newline at end of file
diff --git a/baseLibrary/build.gradle b/baseLibrary/build.gradle
index 241e4a8..7489e0d 100644
--- a/baseLibrary/build.gradle
+++ b/baseLibrary/build.gradle
@@ -15,10 +15,9 @@
*/
apply plugin: 'java'
-apply plugin: 'application'
-sourceCompatibility = config.javaTargetCompatibility
-targetCompatibility = config.javaSourceCompatibility
+sourceCompatibility = dataBindingConfig.javaTargetCompatibility
+targetCompatibility = dataBindingConfig.javaSourceCompatibility
sourceSets {
main {
@@ -37,25 +36,6 @@
testCompile 'junit:junit:4.12'
}
-def javadocTask = project.tasks.create(name: "javadocBaseLibrary", type: Javadoc) {
- source sourceSets.main.allJava
-}
-
-def javadocJarTask = project.tasks.create(name: "javadocJarBaseLibrary", type: Jar) {
- classifier = 'javadoc'
- from 'build/docs/javadoc'
-}
-javadocJarTask.dependsOn javadocTask
-
-def sourcesJarTask = project.tasks.create(name: "sourceJarBaseLibrary", type: Jar) {
- classifier = 'sources'
- from sourceSets.main.java.srcDirs
-}
-
-artifacts.add('archives', javadocJarTask);
-artifacts.add('archives', sourcesJarTask);
-
-
uploadArchives {
repositories {
mavenDeployer {
@@ -63,9 +43,9 @@
pom.project {
licenses {
license {
- name config.licenseName
- url config.licenseUrl
- distribution config.licenseDistribution
+ name dataBindingConfig.licenseName
+ url dataBindingConfig.licenseUrl
+ distribution dataBindingConfig.licenseDistribution
}
}
}
@@ -76,8 +56,12 @@
task prebuildJar(type : Copy) {
dependsOn uploadArchives
from "$buildDir/libs/baseLibrary-${version}.jar"
- into config.prebuildFolder
+ into dataBindingConfig.prebuildFolder
rename { String fileName ->
"databinding-baseLibrary.jar"
}
-}
\ No newline at end of file
+}
+
+project.ext.pomName = 'Data Binding Base Library'
+project.ext.pomDesc = 'Shared library between Data Binding runtime lib and compiler'
+enablePublishing(this, true)
\ No newline at end of file
diff --git a/baseLibrary/db-baseLibrary.iml b/baseLibrary/db-baseLibrary.iml
new file mode 100644
index 0000000..7cf9983
--- /dev/null
+++ b/baseLibrary/db-baseLibrary.iml
@@ -0,0 +1,36 @@
+<?xml version="1.0" encoding="UTF-8"?>
+<module relativePaths="true" type="JAVA_MODULE" version="4">
+ <component name="NewModuleRootManager" inherit-compiler-output="true">
+ <exclude-output />
+ <content url="file://$MODULE_DIR$">
+ <sourceFolder url="file://$MODULE_DIR$/src/main/java" isTestSource="false" />
+ <excludeFolder url="file://$MODULE_DIR$/../../../out/build/dataBinding/baseLibrary/build" />
+ <excludeFolder url="file://$MODULE_DIR$/.gradle" />
+ <excludeFolder url="file://$MODULE_DIR$/build" />
+ </content>
+ <orderEntry type="inheritedJdk" />
+ <orderEntry type="sourceFolder" forTests="false" />
+ <orderEntry type="module-library" scope="TEST">
+ <library>
+ <CLASSES>
+ <root url="jar://$MODULE_DIR$/../../../prebuilts/tools/common/m2/repository/junit/junit/4.12/junit-4.12.jar!/" />
+ </CLASSES>
+ <JAVADOC />
+ <SOURCES>
+ <root url="jar://$MODULE_DIR$/../../../prebuilts/tools/common/m2/repository/junit/junit/4.12/junit-4.12-sources.jar!/" />
+ </SOURCES>
+ </library>
+ </orderEntry>
+ <orderEntry type="module-library" scope="TEST">
+ <library>
+ <CLASSES>
+ <root url="jar://$MODULE_DIR$/../../../prebuilts/tools/common/m2/repository/org/hamcrest/hamcrest-core/1.3/hamcrest-core-1.3.jar!/" />
+ </CLASSES>
+ <JAVADOC />
+ <SOURCES>
+ <root url="jar://$MODULE_DIR$/../../../prebuilts/tools/common/m2/repository/org/hamcrest/hamcrest-core/1.3/hamcrest-core-1.3-sources.jar!/" />
+ </SOURCES>
+ </library>
+ </orderEntry>
+ </component>
+</module>
\ No newline at end of file
diff --git a/baseLibrary/src/main/java/android/databinding/BindingAdapter.java b/baseLibrary/src/main/java/android/databinding/BindingAdapter.java
index 09bc482..7409d02 100644
--- a/baseLibrary/src/main/java/android/databinding/BindingAdapter.java
+++ b/baseLibrary/src/main/java/android/databinding/BindingAdapter.java
@@ -72,4 +72,17 @@
* @return The attributes associated with this binding adapter.
*/
String[] value();
+
+ /**
+ * Whether every attribute must be assigned a binding expression or if some
+ * can be absent. When this is false, the BindingAdapter will be called
+ * when at least one associated attribute has a binding expression. The attributes
+ * for which there was no binding expression (even a normal XML value) will
+ * cause the associated parameter receive the Java default value. Care must be
+ * taken to ensure that a default value is not confused with a valid XML value.
+ *
+ * @return whether or not every attribute must be assigned a binding expression. The default
+ * value is true.
+ */
+ boolean requireAll() default true;
}
diff --git a/baseLibrary/src/main/java/android/databinding/BindingMethod.java b/baseLibrary/src/main/java/android/databinding/BindingMethod.java
index 3585c0c..d96b930 100644
--- a/baseLibrary/src/main/java/android/databinding/BindingMethod.java
+++ b/baseLibrary/src/main/java/android/databinding/BindingMethod.java
@@ -15,11 +15,15 @@
*/
package android.databinding;
+import java.lang.annotation.ElementType;
+import java.lang.annotation.Target;
+
/**
* Used within an {@link BindingMethods} annotation to describe a renaming of an attribute to
* the setter used to set that attribute. By default, an attribute attr will be associated with
* setter setAttr.
*/
+@Target(ElementType.ANNOTATION_TYPE)
public @interface BindingMethod {
/**
diff --git a/baseLibrary/src/main/java/android/databinding/InverseBindingAdapter.java b/baseLibrary/src/main/java/android/databinding/InverseBindingAdapter.java
new file mode 100644
index 0000000..0ac85ec
--- /dev/null
+++ b/baseLibrary/src/main/java/android/databinding/InverseBindingAdapter.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;
+
+import java.lang.annotation.ElementType;
+import java.lang.annotation.Target;
+
+/**
+ * InverseBindingAdapter is associated with a method used to retrieve the value for a View
+ * when setting values gathered from the View. This is similar to {@link BindingAdapter}s:
+ * <pre>
+ * @InverseBindingAdapter(attribute = "android:text", event = "android:textAttrChanged")
+ * public static String captureTextValue(TextView view, CharSequence originalValue) {
+ * CharSequence newValue = view.getText();
+ * CharSequence oldValue = value.get();
+ * if (oldValue == null) {
+ * value.set(newValue);
+ * } else if (!contentEquals(newValue, oldValue)) {
+ * value.set(newValue);
+ * }
+ * }
+ * </pre>
+ * <p>
+ * The default value for event is the attribute name suffixed with "AttrChanged". In the
+ * above example, the default value would have been <code>android:textAttrChanged</code> even
+ * if it wasn't provided.
+ * <p>
+ * The event attribute is used to notify the data binding system that the value has changed.
+ * The developer will typically create a {@link BindingAdapter} to assign the event. For example:
+ * <p>
+ * <pre>
+ * @BindingAdapter(value = {"android:beforeTextChanged", "android:onTextChanged",
+ * "android:afterTextChanged", "android:textAttrChanged"},
+ * requireAll = false)
+ * public static void setTextWatcher(TextView view, final BeforeTextChanged before,
+ * final OnTextChanged on, final AfterTextChanged after,
+ * final InverseBindingListener textAttrChanged) {
+ * TextWatcher newValue = new TextWatcher() {
+ * ...
+ * @Override
+ * public void onTextChanged(CharSequence s, int start, int before, int count) {
+ * if (on != null) {
+ * on.onTextChanged(s, start, before, count);
+ * }
+ * if (textAttrChanged != null) {
+ * textAttrChanged.onChange();
+ * }
+ * }
+ * }
+ * TextWatcher oldValue = ListenerUtil.trackListener(view, newValue, R.id.textWatcher);
+ * if (oldValue != null) {
+ * view.removeTextChangedListener(oldValue);
+ * }
+ * view.addTextChangedListener(newValue);
+ * }
+ * </pre>
+ * <p>
+ * Like <code>BindingAdapter</code>s, InverseBindingAdapter methods may also take
+ * {@link DataBindingComponent} as the first parameter and may be an instance method with the
+ * instance retrieved from the <code>DataBindingComponent</code>.
+ *
+ * @see DataBindingUtil#setDefaultComponent(DataBindingComponent)
+ * @see InverseBindingMethod
+ */
+@Target({ElementType.METHOD, ElementType.ANNOTATION_TYPE})
+public @interface InverseBindingAdapter {
+
+ /**
+ * The attribute that the value is to be retrieved for.
+ */
+ String attribute();
+
+ /**
+ * The event used to trigger changes. This is used in {@link BindingAdapter}s for the
+ * data binding system to set the event listener when two-way binding is used.
+ */
+ String event() default "";
+}
diff --git a/baseLibrary/src/main/java/android/databinding/InverseBindingListener.java b/baseLibrary/src/main/java/android/databinding/InverseBindingListener.java
new file mode 100644
index 0000000..ec5e6dd
--- /dev/null
+++ b/baseLibrary/src/main/java/android/databinding/InverseBindingListener.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;
+
+/**
+ * A listener implemented by all two-way bindings to be notified when a triggering change happens.
+ * For example, when there is a two-way binding for android:text, an implementation of
+ * <code>InverseBindingListener</code> will be generated in the layout's binding class.
+ * <pre>
+ * private static class InverseListenerTextView implements InverseBindingListener {
+ * @Override
+ * public void onChange() {
+ * mObj.setTextValue(mTextView.getText());
+ * }
+ * }
+ * </pre>
+ * <p>
+ * A {@link BindingAdapter} should be used to assign the event listener.
+ * For example, <code>android:onTextChanged</code> will need to trigger the event listener
+ * for the <code>android:text</code> attribute.
+ * <pre>
+ * @InverseBindingAdapter(attribute = "android:text", event = "android:textAttrChanged")
+ * public static void captureTextValue(TextView view, ObservableField<CharSequence> value) {
+ * CharSequence newValue = view.getText();
+ * CharSequence oldValue = value.get();
+ * if (oldValue == null) {
+ * value.set(newValue);
+ * } else if (!contentEquals(newValue, oldValue)) {
+ * value.set(newValue);
+ * }
+ * }
+ * @BindingAdapter(value = {"android:beforeTextChanged", "android:onTextChanged",
+ * "android:afterTextChanged", "android:textAttrChanged"},
+ * requireAll = false)
+ * public static void setTextWatcher(TextView view, final BeforeTextChanged before,
+ * final OnTextChanged on, final AfterTextChanged after,
+ * final InverseBindingListener textAttrChanged) {
+ * TextWatcher newValue = new TextWatcher() {
+ * ...
+ * @Override
+ * public void onTextChanged(CharSequence s, int start, int before, int count) {
+ * if (on != null) {
+ * on.onTextChanged(s, start, before, count);
+ * }
+ * if (textAttrChanged != null) {
+ * textAttrChanged.onChange();
+ * }
+ * }
+ * }
+ * TextWatcher oldValue = ListenerUtil.trackListener(view, newValue, R.id.textWatcher);
+ * if (oldValue != null) {
+ * view.removeTextChangedListener(oldValue);
+ * }
+ * view.addTextChangedListener(newValue);
+ * }
+ * </pre>
+ */
+public interface InverseBindingListener {
+ /**
+ * Notifies the data binding system that the attribute value has changed.
+ */
+ void onChange();
+}
diff --git a/baseLibrary/src/main/java/android/databinding/InverseBindingMethod.java b/baseLibrary/src/main/java/android/databinding/InverseBindingMethod.java
new file mode 100644
index 0000000..5f5c0d9
--- /dev/null
+++ b/baseLibrary/src/main/java/android/databinding/InverseBindingMethod.java
@@ -0,0 +1,98 @@
+/*
+ * Copyright (C) 2015 The Android Open Source Project
+ *
+ * Licensed under the Apache License, Version 2.0 (the "License");
+ * you may not use this file except in compliance with the License.
+ * You may obtain a copy of the License at
+ *
+ * http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing, software
+ * distributed under the License is distributed on an "AS IS" BASIS,
+ * WITHOUT 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;
+
+/**
+ * InverseBindingMethod is used to identify how to listen for changes to a View property and which
+ * getter method to call. InverseBindingMethod should be associated with any class as part of
+ * {@link InverseBindingMethods}.
+ * <p>
+ * <pre>
+ * @InverseBindingMethods({@InverseBindingMethod(
+ * type = android.widget.TextView.class,
+ * attribute = "android:text",
+ * event = "android:textAttrChanged",
+ * method = "getText")})
+ * public class MyTextViewBindingAdapters { ... }
+ * </pre>
+ * <p>
+ * <code>method</code> is optional. If it isn't provided, the attribute name is used to
+ * find the method name, either prefixing with "is" or "get". For the attribute
+ * <code>android:text</code>, data binding will search for a
+ * <code>public CharSequence getText()</code> method on {@link android.widget.TextView}.
+ * <p>
+ * <code>event</code> is optional. If it isn't provided, the event name is assigned the
+ * attribute name suffixed with <code>AttrChanged</code>. For the <code>android:text</code>
+ * attribute, the default event name would be <code>android:textAttrChanged</code>. The event
+ * should be set using a {@link BindingAdapter}. For example:
+ * <pre>
+ * @BindingAdapter(value = {"android:beforeTextChanged", "android:onTextChanged",
+ * "android:afterTextChanged", "android:textAttrChanged"},
+ * requireAll = false)
+ * public static void setTextWatcher(TextView view, final BeforeTextChanged before,
+ * final OnTextChanged on, final AfterTextChanged after,
+ * final InverseBindingListener textAttrChanged) {
+ * TextWatcher newValue = new TextWatcher() {
+ * ...
+ * @Override
+ * public void onTextChanged(CharSequence s, int start, int before, int count) {
+ * if (on != null) {
+ * on.onTextChanged(s, start, before, count);
+ * }
+ * if (textAttrChanged != null) {
+ * textAttrChanged.onChange();
+ * }
+ * }
+ * }
+ * TextWatcher oldValue = ListenerUtil.trackListener(view, newValue, R.id.textWatcher);
+ * if (oldValue != null) {
+ * view.removeTextChangedListener(oldValue);
+ * }
+ * view.addTextChangedListener(newValue);
+ * }
+ * </pre>
+ *
+ * @see InverseBindingAdapter
+ * @see InverseBindingListener
+ */
+@Target(ElementType.ANNOTATION_TYPE)
+public @interface InverseBindingMethod {
+
+ /**
+ * The View type that is associated with the attribute.
+ */
+ Class type();
+
+ /**
+ * The attribute that supports two-way binding.
+ */
+ String attribute();
+
+ /**
+ * The event used to notify the data binding system that the attribute value has changed.
+ * Defaults to attribute() + "AttrChanged"
+ */
+ String event() default "";
+
+ /**
+ * The getter method to retrieve the attribute value from the View. The default is
+ * the bean method name based on the attribute name.
+ */
+ String method() default "";
+}
diff --git a/developmentPlugins/bintrayPlugin/build.gradle b/baseLibrary/src/main/java/android/databinding/InverseBindingMethods.java
similarity index 63%
rename from developmentPlugins/bintrayPlugin/build.gradle
rename to baseLibrary/src/main/java/android/databinding/InverseBindingMethods.java
index 364a5bb..29e2374 100644
--- a/developmentPlugins/bintrayPlugin/build.gradle
+++ b/baseLibrary/src/main/java/android/databinding/InverseBindingMethods.java
@@ -13,22 +13,16 @@
* See the License for the specific language governing permissions and
* limitations under the License.
*/
-apply plugin: 'groovy'
+package android.databinding;
-repositories {
- mavenCentral()
+import java.lang.annotation.ElementType;
+import java.lang.annotation.Target;
+
+/**
+ * Used to enumerate attribute, getter, and event association. The value is an array of
+ * {@link InverseBindingMethod}s.
+ */
+@Target(ElementType.TYPE)
+public @interface InverseBindingMethods {
+ InverseBindingMethod[] value();
}
-
-dependencies {
- compile gradleApi()
- testCompile group: 'junit', name: 'junit', version: '4.12'
-}
-
-uploadArchives {
- repositories {
- mavenDeployer {
- pom.artifactId = 'bintray'
- repository(url: "file://${config.extraPluginsRepoDir}")
- }
- }
-}
\ No newline at end of file
diff --git a/build.gradle b/build.gradle
index acf68f4..c33a72c 100644
--- a/build.gradle
+++ b/build.gradle
@@ -1,67 +1,83 @@
+import groovy.xml.QName
buildscript {
- ext.rootFolder = project.projectDir
+ ext.dataBindingRootFolder = project.projectDir
apply from: 'propLoader.gradle'
ext.addRepos(repositories)
- if (ext.config.addRemoteRepos) {
+ if (ext.dataBindingConfig.addRemoteRepos) {
dependencies {
- classpath "com.android.databinding:localizemaven:${config.extraPluginsVersion}"
+ classpath "com.android.databinding:localizemaven:${dataBindingConfig.extraPluginsVersion}"
}
}
+ dependencies {
+ classpath 'net.sf.proguard:proguard-gradle:5.2.1'
+ }
}
+
subprojects {
- apply plugin: 'maven'
- if (config.addRemoteRepos) {
- apply plugin: 'com.android.databinding.localizemaven'
+ if (dataBindingConfig.isIndependent) {
+ apply plugin: 'maven'
+ if (dataBindingConfig.addRemoteRepos && !dataBindingConfig.forGradlePlugin) {
+ apply plugin: 'com.android.databinding.localizemaven'
+ }
+ } else {
+ apply from: "${project.projectDir}/../../buildSrc/base/baseJava.gradle"
}
- group = config.group
- version = config.version
- uploadArchives {
- repositories {
- mavenDeployer {
- repository(url: "file://${config.mavenRepoDir}")
+ if (it.name != "compilationTests") {
+ group = dataBindingConfig.group
+ version = dataBindingConfig.version
+ uploadArchives {
+ repositories {
+ mavenDeployer {
+ repository(url: "file://${dataBindingConfig.mavenRepoDir}")
+ }
}
}
}
+
buildscript {
addRepos(repositories)
- dependencies {
- classpath "com.android.databinding:bintray:${config.extraPluginsVersion}"
- }
}
+
}
-if (config.addRemoteRepos) {
- localizeMaven {
- localRepoDir = file(config.megaRepoDir)
- otherRepoDirs = config.localRepositories
+if (dataBindingConfig.addRemoteRepos && !dataBindingConfig.forGradlePlugin) {
+ rootProject.localizeMaven {
+ localRepoDir = file(dataBindingConfig.megaRepoDir)
+ otherRepoDirs = dataBindingConfig.localRepositories
}
}
task deleteRepo(type: Delete) {
- delete "${config.mavenRepoDir}/${config.group.replace('.', '/')}"
+ delete "${dataBindingConfig.mavenRepoDir}/${dataBindingConfig.group.replace('.', '/')}"
}
task deletePrebuildFolder(type: Delete) {
- delete "${config.prebuildFolder}"
+ delete "${dataBindingConfig.prebuildFolder}"
}
task deleteEap(type: Delete) {
- delete "${config.eapOutDir}"
+ delete "${dataBindingConfig.eapOutDir}"
}
def buildExtensionsTask = project.tasks.create "buildExtensionsTask", Exec
buildExtensionsTask.workingDir file('extensions').getAbsolutePath()
buildExtensionsTask.commandLine './gradlew'
-buildExtensionsTask.args 'clean', 'uploadArchives', '--info', '--stacktrace'
+buildExtensionsTask.args 'clean', 'uploadArchives', '--info', '--stacktrace',
+ "-PuseReleaseVersion=${dataBindingConfig.inReleaseBuild ? 'true' : 'false'}",
+ "-PaddRemoteRepos=${dataBindingConfig.addRemoteRepos ? 'true' : 'false'}",
+ "-PforGradlePlugin=${dataBindingConfig.forGradlePlugin ? 'true' : 'false'}"
buildExtensionsTask.dependsOn subprojects.uploadArchives
def prepareExtensionPrebuilds = project.tasks.create "prepareExtensionPrebuilds", Exec
prepareExtensionPrebuilds.workingDir file('extensions').getAbsolutePath()
prepareExtensionPrebuilds.commandLine './gradlew'
-prepareExtensionPrebuilds.args 'clean', 'preparePrebuilds', '--info', '--stacktrace'
+prepareExtensionPrebuilds.args 'clean', 'preparePrebuilds', '--info', '--stacktrace',
+ "-PuseReleaseVersion=${dataBindingConfig.inReleaseBuild ? 'true' : 'false'}",
+ "-PaddRemoteRepos=${dataBindingConfig.addRemoteRepos ? 'true' : 'false'}",
+ "-PforGradlePlugin=${dataBindingConfig.forGradlePlugin ? 'true' : 'false'}"
prepareExtensionPrebuilds.dependsOn subprojects.uploadArchives
file('integration-tests').listFiles().findAll { it.isDirectory() }.each {
@@ -70,9 +86,11 @@
testTask.workingDir it.getAbsolutePath()
//on linux
testTask.commandLine './gradlew'
- testTask.args 'clean', 'connectedCheck', '--info', '--stacktrace'
+ testTask.args 'clean', 'connectedCheck', '--info', '--stacktrace',
+ "-PuseReleaseVersion=${dataBindingConfig.inReleaseBuild ? 'true' : 'false'}",
+ "-PaddRemoteRepos=${dataBindingConfig.addRemoteRepos ? 'true' : 'false'}",
+ "-PforGradlePlugin=${dataBindingConfig.forGradlePlugin ? 'true' : 'false'}"
testTask.dependsOn subprojects.uploadArchives
- testTask.dependsOn buildExtensionsTask
}
task runIntegrationTests {
@@ -98,6 +116,7 @@
subprojects.uploadArchives.each { it.shouldRunAfter deleteRepo }
subprojects.uploadArchives.each { it.shouldRunAfter deletePrebuildFolder }
+
buildExtensionsTask.shouldRunAfter deleteRepo
tasks['runTestsOfMultiModuleTestApp'].dependsOn tasks['runTestsOfIndependentLibrary']
@@ -105,7 +124,6 @@
task rebuildRepo() {
dependsOn deleteRepo
dependsOn subprojects.uploadArchives
- dependsOn buildExtensionsTask
}
task copySamplesToEap(type : Copy) {
@@ -114,29 +132,161 @@
exclude "**/build"
exclude "**/local.properties"
}
- into "${config.eapOutDir}/samples"
+ into "${dataBindingConfig.eapOutDir}/samples"
}
task copyMavenRepoToEap(type : Copy) {
mustRunAfter deleteEap
dependsOn rebuildRepo
- from(config.mavenRepoDir)
- into "${config.eapOutDir}/${config.mavenRepoName}"
+ from(dataBindingConfig.mavenRepoDir)
+ into "${dataBindingConfig.eapOutDir}/${dataBindingConfig.mavenRepoName}"
}
-tasks.create(name : 'createEapConfigFile') << {
- def propsFile = new File("${config.eapOutDir}/databinding.properties")
+tasks.create(name : 'createEapdataBindingConfigFile') << {
+ def propsFile = new File("${dataBindingConfig.eapOutDir}/databinding.properties")
Properties props = new Properties()
- props.setProperty('version', config.version)
- props.setProperty('mavenRepoName', config.mavenRepoName)
+ props.setProperty('version', dataBindingConfig.version)
+ props.setProperty('mavenRepoName', dataBindingConfig.mavenRepoName)
props.store(propsFile.newWriter(), null)
}
-
task batchEAP() {
dependsOn deleteEap
dependsOn copyMavenRepoToEap
dependsOn copySamplesToEap
- dependsOn createEapConfigFile
+ dependsOn createEapdataBindingConfigFile
+}
+
+def findChildByTag(node, tag) {
+ return node.children().find {
+ it.name().getLocalPart().equals(tag)
+ }
+}
+
+def fullJar(project) {
+ def localizeTask = project.parent.tasks.findByName('localizeDependencies')
+ if (localizeTask != null) {
+ localizeTask.dependsOn project.tasks.findByName('buildLicenseNotice')
+ }
+
+ if (!dataBindingConfig.runProguard || !dataBindingConfig.inReleaseBuild) {
+ return
+ }
+ def jarName = project.uploadArchives.repositories.mavenDeployer.pom.artifactId
+ def workingDir = "${project.buildDir}/intermediates/fullJar"
+ def fatJar = "${workingDir}/${jarName}-fat.jar"
+ def proguardJar = "${workingDir}/${jarName}-proguard.jar"
+ def jarJar = project.jar.archivePath
+
+ project.configurations {
+ jarJarArchives
+ }
+
+ project.tasks.create(name: 'fatJar', type: Jar) {
+ baseName = jarName + '-fat'
+ doFirst {
+ file(workingDir).mkdirs()
+ }
+ def deps = new HashSet<ResolvedDependency>()
+ project.configurations.compile.resolvedConfiguration.firstLevelModuleDependencies.each {
+ if (!it.moduleGroup.startsWith('com.android.tools')) {
+ deps.addAll(it.allModuleArtifacts)
+ }
+ }
+ from { deps.findAll { !it.name.contains('baseLibrary') && !it.name.contains("juniversalchardet")}.collect {
+ it.file.isDirectory() ? it.file : project.zipTree(it.file)
+ } } {
+ exclude "META-INF/maven/**"
+ exclude "META-INF/MANIFEST.MF"
+ exclude "META-INF/LICENSE.txt"
+ exclude "META-INF/NOTICE.txt"
+ exclude "META-INF/services/javax.annotation.processing.Processor"
+ exclude "**/*.stg"
+ }
+ archiveName "${baseName}.jar"
+ destinationDir = new File(workingDir)
+ with project.jar
+ }
+ project.tasks.create(name: 'proguard', type: proguard.gradle.ProGuardTask) {
+ dependsOn 'fatJar'
+
+ injars fatJar
+ outjars proguardJar
+
+ configuration '../proguard.cfg'
+ }
+
+ project.tasks.create(name: 'jarJarFile') {
+ dependsOn 'proguard'
+ dependsOn project.jar
+ def inputLibrary = proguardJar
+ def outputLibrary = jarJar
+ inputs.file(inputLibrary)
+ outputs.file(outputLibrary)
+
+ doLast {
+ def jarJarLibrary = new File(dataBindingConfig.externalPrebuiltsBase,
+ 'tools/common/m2/repository/com/googlecode/jarjar/jarjar/1.3/jarjar-1.3.jar').
+ getCanonicalPath()
+ // in Ant
+ ant.taskdef(name: "jarjarIt",
+ classname: 'com.tonicsystems.jarjar.JarJarTask',
+ classpath: jarJarLibrary)
+ // start jarjar task
+ ant.jarjarIt(jarfile: outputLibrary) {
+ // input is our inputLibrary
+ zipfileset(src: inputLibrary)
+ // rule to repackage antlr to new package
+ rule pattern: 'org.antlr.**', result: 'com.google.repacked.antlr.@1'
+ rule pattern: 'com.tunnelvisionlabs.**', result: 'com.google.repacked.tunnelvision.@1'
+ rule pattern: 'org.abego.treelayout.**', result: 'com.google.repacked.treelayout.@1'
+ // rule to repackage commons
+ rule pattern: 'org.apache.**', result: 'com.google.repacked.apache.@1'
+ rule pattern: 'kotlin.**', result: 'com.google.repacked.kotlin.@1'
+ }
+ }
+ }
+
+ project.uploadArchives {
+ dependsOn 'jarJarFile'
+ repositories {
+ mavenDeployer {
+ pom.whenConfigured { pom ->
+ pom.dependencies.removeAll { dep ->
+ def isBaseLibrary = dep.groupId == 'com.android.databinding' &&
+ dep.artifactId == 'baseLibrary'
+ def isGradle = dep.groupId == 'com.android.tools.build' &&
+ dep.artifactId == 'gradle'
+ def isChardet = dep.groupId == 'com.googlecode.juniversalchardet' &&
+ dep.artifactId == 'juniversalchardet'
+ return !isBaseLibrary && !isGradle && !isChardet
+ }
+ }
+ }
+ }
+ outputs.upToDateWhen { false } // force it to re-run all the time.
+ }
+ project.bintrayUpload.dependsOn 'jarJarFile'
+ project.publishing.publications.mavenJava(MavenPublication) {
+ pom.withXml {
+ def deps = findChildByTag(asNode(), "dependencies")
+ if (deps == null) {
+ throw new IllegalArgumentException("cannot find dependencies in pom file")
+ }
+ def unwanted = deps.children().findAll {
+ def groupId = findChildByTag(it, "groupId").localText()[0]
+ def artifactId = findChildByTag(it, "artifactId").localText()[0]
+ def isBaseLibrary = groupId == 'com.android.databinding' &&
+ artifactId == 'baseLibrary'
+ def isGradle = groupId == 'com.android.tools.build' && artifactId == 'gradle'
+ def isChardet = groupId == 'com.googlecode.juniversalchardet' &&
+ artifactId == 'juniversalchardet'
+ return !isBaseLibrary && !isGradle && !isChardet
+ }
+ unwanted.each {
+ deps.remove(it)
+ }
+ }
+ }
}
diff --git a/buildForGradlePlugin.sh b/buildForGradlePlugin.sh
new file mode 100755
index 0000000..c222fb3
--- /dev/null
+++ b/buildForGradlePlugin.sh
@@ -0,0 +1 @@
+./gradlew :dB:compiler:uploadArchives :dB:compilerCommon:uploadArchives :dB:baseLibrary:uploadArchives -PaddRemoteRepos=true -PuseReleaseVersion=true -PforGradlePlugin=true
diff --git a/compilationTests/build.gradle b/compilationTests/build.gradle
index 743050c..0ca1877 100644
--- a/compilationTests/build.gradle
+++ b/compilationTests/build.gradle
@@ -1,12 +1,17 @@
apply plugin: 'java'
-sourceCompatibility = 1.7
+sourceCompatibility = 1.6
version = '1.0'
dependencies {
- testCompile group: 'junit', name: 'junit', version: '4.12'
+ testCompile 'junit:junit:4.12'
testCompile 'org.apache.commons:commons-lang3:3.3.2'
testCompile 'commons-io:commons-io:2.4'
testCompile 'commons-codec:commons-codec:1.10'
- testCompile project(':compilerCommon')
+ testCompile project(':dataBinding:compilerCommon')
+}
+
+afterEvaluate {
+ tasks['test'].systemProperties['useReleaseVersion'] = dataBindingConfig.inReleaseBuild ? 'true' : 'false'
+ tasks['test'].systemProperties['addRemoteRepos'] = dataBindingConfig.addRemoteRepos ? 'true' : 'false'
}
\ No newline at end of file
diff --git a/compilationTests/compilationTests.iml b/compilationTests/compilationTests.iml
new file mode 100644
index 0000000..751bf1e
--- /dev/null
+++ b/compilationTests/compilationTests.iml
@@ -0,0 +1,33 @@
+<?xml version="1.0" encoding="UTF-8"?>
+<module external.linked.project.id=":dataBinding:compilationTests" external.linked.project.path="$MODULE_DIR$" external.root.project.path="$MODULE_DIR$/.." external.system.id="GRADLE" external.system.module.group="data-binding.dataBinding" external.system.module.version="1.0" type="JAVA_MODULE" version="4">
+ <component name="NewModuleRootManager" inherit-compiler-output="false">
+ <output url="file://$MODULE_DIR$/build/classes/main" />
+ <output-test url="file://$MODULE_DIR$/build/classes/test" />
+ <exclude-output />
+ <content url="file://$MODULE_DIR$">
+ <sourceFolder url="file://$MODULE_DIR$/src/main/java" isTestSource="false" />
+ <sourceFolder url="file://$MODULE_DIR$/src/test/java" isTestSource="true" />
+ <sourceFolder url="file://$MODULE_DIR$/src/main/resources" type="java-resource" />
+ <sourceFolder url="file://$MODULE_DIR$/src/test/resources" type="java-test-resource" />
+ <excludeFolder url="file://$MODULE_DIR$/.gradle" />
+ <excludeFolder url="file://$MODULE_DIR$/build" />
+ </content>
+ <orderEntry type="inheritedJdk" />
+ <orderEntry type="sourceFolder" forTests="false" />
+ <orderEntry type="module" module-name="compilerCommon" scope="TEST" />
+ <orderEntry type="module" module-name="baseLibrary" scope="TEST" />
+ <orderEntry type="library" scope="TEST" name="Gradle: junit:junit:4.12" level="project" />
+ <orderEntry type="library" scope="TEST" name="Gradle: org.apache.commons:commons-lang3:3.3.2" level="project" />
+ <orderEntry type="library" scope="TEST" name="Gradle: commons-io:commons-io:2.4" level="project" />
+ <orderEntry type="library" scope="TEST" name="Gradle: commons-codec:commons-codec:1.10" level="project" />
+ <orderEntry type="library" scope="TEST" name="Gradle: org.hamcrest:hamcrest-core:1.3" level="project" />
+ <orderEntry type="library" scope="TEST" name="Gradle: com.tunnelvisionlabs:antlr4:4.5" level="project" />
+ <orderEntry type="library" scope="TEST" name="Gradle: com.googlecode.juniversalchardet:juniversalchardet:1.0.3" level="project" />
+ <orderEntry type="library" scope="TEST" name="Gradle: com.tunnelvisionlabs:antlr4-runtime:4.5" level="project" />
+ <orderEntry type="library" scope="TEST" name="Gradle: com.tunnelvisionlabs:antlr4-annotations:4.5" level="project" />
+ <orderEntry type="library" scope="TEST" name="Gradle: org.antlr:antlr-runtime:3.5.2" level="project" />
+ <orderEntry type="library" scope="TEST" name="Gradle: org.antlr:ST4:4.0.8" level="project" />
+ <orderEntry type="library" scope="TEST" name="Gradle: org.abego.treelayout:org.abego.treelayout.core:1.0.1" level="project" />
+ <orderEntry type="library" scope="TEST" name="Gradle: com.google.guava:guava:17.0" level="project" />
+ </component>
+</module>
\ No newline at end of file
diff --git a/compilationTests/src/test/java/android/databinding/compilationTest/BaseCompilationTest.java b/compilationTests/src/test/java/android/databinding/compilationTest/BaseCompilationTest.java
index 814f4aa..08bfde5 100644
--- a/compilationTests/src/test/java/android/databinding/compilationTest/BaseCompilationTest.java
+++ b/compilationTests/src/test/java/android/databinding/compilationTest/BaseCompilationTest.java
@@ -25,15 +25,15 @@
import android.databinding.tool.store.Location;
+import java.io.BufferedInputStream;
+import java.io.BufferedReader;
import java.io.File;
import java.io.FileOutputStream;
import java.io.IOException;
import java.io.InputStream;
+import java.io.InputStreamReader;
import java.net.URISyntaxException;
import java.net.URL;
-import java.nio.file.Files;
-import java.nio.file.Paths;
-import java.nio.file.attribute.PosixFilePermission;
import java.util.ArrayList;
import java.util.Collections;
import java.util.HashMap;
@@ -52,7 +52,7 @@
public class BaseCompilationTest {
private static final String PRINT_ENCODED_ERRORS_PROPERTY
- = "android.databinding.injected.print.encoded.errors";
+ = "android.injected.invoked.from.ide";
@Rule
public TestName name = new TestName();
static Pattern VARIABLES = Pattern.compile("!@\\{([A-Za-z0-9_-]*)}");
@@ -147,7 +147,7 @@
protected static Map<String, String> toMap(String... keysAndValues) {
assertEquals(0, keysAndValues.length % 2);
- Map<String, String> map = new HashMap<>();
+ Map<String, String> map = new HashMap<String, String>();
for (int i = 0; i < keysAndValues.length; i += 2) {
map.put(keysAndValues[i], keysAndValues[i + 1]);
}
@@ -191,7 +191,7 @@
private Map<String, String> addDefaults(Map<String, String> map) {
if (map == null) {
- map = new HashMap<>();
+ map = new HashMap<String, String>();
}
if (!map.containsKey(KEY_MANIFEST_PACKAGE)) {
map.put(KEY_MANIFEST_PACKAGE, DEFAULT_APP_PACKAGE);
@@ -216,7 +216,8 @@
if (localProperties.exists()) {
FileUtils.copyFile(localProperties, new File(testFolder, "local.properties"));
}
- FileUtils.copyFile(new File("../propLoader.gradle"), new File(testFolder, "propLoaderClone.gradle"));
+ FileUtils.copyFile(new File("../propLoader.gradle"),
+ new File(testFolder, "propLoaderClone.gradle"));
FileUtils.copyFile(new File("../gradlew"), new File(testFolder, "gradlew"));
FileUtils.copyDirectory(new File("../gradle"), new File(testFolder, "gradle"));
}
@@ -240,9 +241,15 @@
throws IOException, InterruptedException {
setExecutable();
File pathToExecutable = new File(testFolder, "gradlew");
- List<String> args = new ArrayList<>();
+ List<String> args = new ArrayList<String>();
args.add(pathToExecutable.getAbsolutePath());
args.add("-P" + PRINT_ENCODED_ERRORS_PROPERTY + "=true");
+ if ("true".equals(System.getProperties().getProperty("useReleaseVersion", "false"))) {
+ args.add("-PuseReleaseVersion=true");
+ }
+ if ("true".equals(System.getProperties().getProperty("addRemoteRepos", "false"))) {
+ args.add("-PaddRemoteRepos=true");
+ }
args.add("--project-cache-dir");
args.add(new File("../.caches/", name.getMethodName()).getAbsolutePath());
Collections.addAll(args, params);
@@ -254,25 +261,27 @@
}
builder.directory(testFolder);
Process process = builder.start();
- String output = IOUtils.toString(process.getInputStream());
- String error = IOUtils.toString(process.getErrorStream());
+ String output = collect(process.getInputStream());
+ String error = collect(process.getErrorStream());
int result = process.waitFor();
return new CompilationResult(result, output, error);
}
private void setExecutable() throws IOException {
- Set<PosixFilePermission> perms = new HashSet<PosixFilePermission>();
- //add owners permission
- perms.add(PosixFilePermission.OWNER_READ);
- perms.add(PosixFilePermission.OWNER_WRITE);
- perms.add(PosixFilePermission.OWNER_EXECUTE);
- //add group permissions
- perms.add(PosixFilePermission.GROUP_READ);
- //add others permissions
- perms.add(PosixFilePermission.OTHERS_READ);
- Files.setPosixFilePermissions(Paths.get(new File(testFolder, "gradlew").getAbsolutePath()),
- perms);
+ File gw = new File(testFolder, "gradlew");
+ gw.setExecutable(true);
}
-
+ /**
+ * Use this instead of IO utils so that we can easily log the output when necessary
+ */
+ private static String collect(InputStream stream) throws IOException {
+ StringBuilder sb = new StringBuilder();
+ String line;
+ final BufferedReader reader = new BufferedReader(new InputStreamReader(stream));
+ while ((line = reader.readLine()) != null) {
+ sb.append(line).append("\n");
+ }
+ return sb.toString();
+ }
}
diff --git a/compilationTests/src/test/java/android/databinding/compilationTest/MultiLayoutVerificationTest.java b/compilationTests/src/test/java/android/databinding/compilationTest/MultiLayoutVerificationTest.java
index 0591ea9..bbf86e7 100644
--- a/compilationTests/src/test/java/android/databinding/compilationTest/MultiLayoutVerificationTest.java
+++ b/compilationTests/src/test/java/android/databinding/compilationTest/MultiLayoutVerificationTest.java
@@ -58,25 +58,24 @@
assertTrue(file.exists());
assertEquals(1, report.getLocations().size());
Location location = report.getLocations().get(0);
- switch (file.getParentFile().getName()) {
- case "layout":
- assertEquals(new File(testFolder,
- "/app/src/main/res/layout/with_class_name.xml")
- .getCanonicalFile(), file.getCanonicalFile());
- String extract = extract("/app/src/main/res/layout/with_class_name.xml",
- location);
- assertEquals(extract, "AClassName");
- assertEquals(String.format(
- ErrorMessages.MULTI_CONFIG_LAYOUT_CLASS_NAME_MISMATCH,
- DEFAULT_APP_PACKAGE + ".databinding.AClassName",
- "layout/with_class_name"), exception.getBareMessage());
- foundNormal = true;
- break;
- case "layout-land":
+ String name = file.getParentFile().getName();
+ if ("layout".equals(name)) {
+ assertEquals(new File(testFolder,
+ "/app/src/main/res/layout/with_class_name.xml")
+ .getCanonicalFile(), file.getCanonicalFile());
+ String extract = extract("/app/src/main/res/layout/with_class_name.xml",
+ location);
+ assertEquals(extract, "AClassName");
+ assertEquals(String.format(
+ ErrorMessages.MULTI_CONFIG_LAYOUT_CLASS_NAME_MISMATCH,
+ DEFAULT_APP_PACKAGE + ".databinding.AClassName",
+ "layout/with_class_name"), exception.getBareMessage());
+ foundNormal = true;
+ } else if ("layout-land".equals(name)) {
assertEquals(new File(testFolder,
"/app/src/main/res/layout-land/with_class_name.xml")
.getCanonicalFile(), file.getCanonicalFile());
- extract = extract("/app/src/main/res/layout-land/with_class_name.xml",
+ String extract = extract("/app/src/main/res/layout-land/with_class_name.xml",
location);
assertEquals("SomeOtherClassName", extract);
assertEquals(String.format(
@@ -84,13 +83,12 @@
DEFAULT_APP_PACKAGE + ".databinding.SomeOtherClassName",
"layout-land/with_class_name"), exception.getBareMessage());
foundLandscape = true;
- break;
- default:
- fail("unexpected error file");
+ } else {
+ fail("unexpected error file");
}
}
- assertTrue(result.error, foundNormal);
- assertTrue(result.error, foundLandscape);
+ assertTrue("should find default config error\n" + result.error, foundNormal);
+ assertTrue("should find landscape error\n" + result.error, foundLandscape);
}
@Test
@@ -117,19 +115,17 @@
assertEquals(result.error, 1, report.getLocations().size());
Location location = report.getLocations().get(0);
// validated in switch
- String config = file.getParentFile().getName();
+ String name = file.getParentFile().getName();
+ String config = name;
String type = "???";
- switch (file.getParentFile().getName()) {
- case "layout":
- type = "String";
- foundNormal = true;
- break;
- case "layout-land":
- type = "CharSequence";
- foundLandscape = true;
- break;
- default:
- fail("unexpected error file");
+ if ("layout".equals(name)) {
+ type = "String";
+ foundNormal = true;
+ } else if ("layout-land".equals(name)) {
+ type = "CharSequence";
+ foundLandscape = true;
+ } else {
+ fail("unexpected error file");
}
assertEquals(new File(testFolder,
"/app/src/main/res/" + config + "/layout_with_variable_type.xml")
@@ -172,19 +168,17 @@
assertEquals(result.error, 1, report.getLocations().size());
Location location = report.getLocations().get(0);
// validated in switch
- String config = file.getParentFile().getName();
+ String name = file.getParentFile().getName();
+ String config = name;
String type = "???";
- switch (file.getParentFile().getName()) {
- case "layout":
- type = typeNormal;
- foundNormal = true;
- break;
- case "layout-land":
- type = typeLand;
- foundLandscape = true;
- break;
- default:
- fail("unexpected error file");
+ if ("layout".equals(name)) {
+ type = typeNormal;
+ foundNormal = true;
+ } else if ("layout-land".equals(name)) {
+ type = typeLand;
+ foundLandscape = true;
+ } else {
+ fail("unexpected error file");
}
assertEquals(new File(testFolder,
"/app/src/main/res/" + config + "/layout_with_import_type.xml")
@@ -227,22 +221,19 @@
Location location = report.getLocations().get(0);
// validated in switch
String config = file.getParentFile().getName();
- switch (file.getParentFile().getName()) {
- case "layout":
- String extract = extract("/app/src/main/res/" + config + "/foo.xml", location);
- assertEquals(extract, "<include layout=\"@layout/basic_layout\" "
- + "android:id=\"@+id/sharedId\" bind:myVariable=\"@{myVariable}\"/>");
- foundNormal = true;
- break;
- case "layout-land":
- extract = extract("/app/src/main/res/" + config + "/foo.xml", location);
- assertEquals(extract, "<TextView android:layout_width=\"wrap_content\" "
- + "android:layout_height=\"wrap_content\" android:id=\"@+id/sharedId\" "
- + "android:text=\"@{myVariable}\"/>");
- foundLandscape = true;
- break;
- default:
- fail("unexpected error file");
+ if ("layout".equals(config)) {
+ String extract = extract("/app/src/main/res/" + config + "/foo.xml", location);
+ assertEquals(extract, "<include layout=\"@layout/basic_layout\" "
+ + "android:id=\"@+id/sharedId\" bind:myVariable=\"@{myVariable}\"/>");
+ foundNormal = true;
+ } else if ("layout-land".equals(config)) {
+ String extract = extract("/app/src/main/res/" + config + "/foo.xml", location);
+ assertEquals(extract, "<TextView android:layout_width=\"wrap_content\" "
+ + "android:layout_height=\"wrap_content\" android:id=\"@+id/sharedId\" "
+ + "android:text=\"@{myVariable}\"/>");
+ foundLandscape = true;
+ } else {
+ fail("unexpected error file");
}
assertEquals(new File(testFolder,
"/app/src/main/res/" + config + "/foo.xml").getCanonicalFile(),
diff --git a/compilationTests/src/test/java/android/databinding/compilationTest/SimpleCompilationTest.java b/compilationTests/src/test/java/android/databinding/compilationTest/SimpleCompilationTest.java
index 43efbb8..229323f 100644
--- a/compilationTests/src/test/java/android/databinding/compilationTest/SimpleCompilationTest.java
+++ b/compilationTests/src/test/java/android/databinding/compilationTest/SimpleCompilationTest.java
@@ -17,6 +17,9 @@
package android.databinding.compilationTest;
+import org.apache.commons.io.FileUtils;
+import org.apache.commons.io.filefilter.PrefixFileFilter;
+import org.apache.commons.io.filefilter.SuffixFileFilter;
import org.apache.commons.lang3.StringUtils;
import org.junit.Test;
@@ -28,6 +31,7 @@
import java.io.File;
import java.io.IOException;
import java.net.URISyntaxException;
+import java.util.Collection;
import java.util.List;
import static org.junit.Assert.assertEquals;
@@ -36,6 +40,7 @@
import static org.junit.Assert.assertTrue;
import static org.junit.Assert.fail;
+@SuppressWarnings("ThrowableResultOfMethodCallIgnored")
public class SimpleCompilationTest extends BaseCompilationTest {
@Test
@@ -53,11 +58,39 @@
prepareProject();
CompilationResult result = runGradle("assembleDebug");
assertEquals(result.error, 0, result.resultCode);
- assertTrue("there should not be any errors " + result.error, StringUtils.isEmpty(result.error));
+ assertTrue("there should not be any errors " + result.error,
+ StringUtils.isEmpty(result.error));
assertTrue("Test sanity, should compile fine",
result.resultContainsText("BUILD SUCCESSFUL"));
}
+ @Test
+ public void testMultipleConfigs() throws IOException, URISyntaxException, InterruptedException {
+ prepareProject();
+ copyResourceTo("/layout/basic_layout.xml",
+ "/app/src/main/res/layout/main.xml");
+ copyResourceTo("/layout/basic_layout.xml",
+ "/app/src/main/res/layout-sw100dp/main.xml");
+ CompilationResult result = runGradle("assembleDebug");
+ assertEquals(result.error, 0, result.resultCode);
+ File debugOut = new File(testFolder,
+ "app/build/intermediates/data-binding-layout-out/debug");
+ Collection<File> layoutFiles = FileUtils.listFiles(debugOut, new SuffixFileFilter(".xml"),
+ new PrefixFileFilter("layout"));
+ assertTrue("test sanity", layoutFiles.size() > 1);
+ for (File layout : layoutFiles) {
+ final String contents = FileUtils.readFileToString(layout);
+ if (layout.getParent().contains("sw100")) {
+ assertTrue("File has wrong tag:" + layout.getPath(),
+ contents.indexOf("android:tag=\"layout-sw100dp/main_0\"") > 0);
+ } else {
+ assertTrue("File has wrong tag:" + layout.getPath() + "\n" + contents,
+ contents.indexOf("android:tag=\"layout/main_0\"")
+ > 0);
+ }
+ }
+ }
+
private ScopedException singleFileErrorTest(String resource, String targetFile,
String expectedExtract, String errorMessage)
throws IOException, URISyntaxException, InterruptedException {
@@ -123,6 +156,28 @@
}
@Test
+ public void testBadSyntax() throws IOException, URISyntaxException, InterruptedException {
+ singleFileErrorTest("/layout/layout_with_bad_syntax.xml",
+ "/app/src/main/res/layout/broken.xml",
+ "myVar.length())",
+ String.format(ErrorMessages.SYNTAX_ERROR,
+ "extraneous input ')' expecting {<EOF>, ',', '.', '[', '+', '-', '*', '/', "
+ + "'%', '<<', '>>>', '>>', '<=', '>=', '>', '<', 'instanceof', "
+ + "'==', '!=', '&', '^', '|', '&&', '||', '?', '??'}"));
+ }
+
+ @Test
+ public void testBrokenSyntax() throws IOException, URISyntaxException, InterruptedException {
+ singleFileErrorTest("/layout/layout_with_completely_broken_syntax.xml",
+ "/app/src/main/res/layout/broken.xml",
+ "new String()",
+ String.format(ErrorMessages.SYNTAX_ERROR,
+ "mismatched input 'String' expecting {<EOF>, ',', '.', '[', '+', '-', '*', "
+ + "'/', '%', '<<', '>>>', '>>', '<=', '>=', '>', '<', 'instanceof',"
+ + " '==', '!=', '&', '^', '|', '&&', '||', '?', '??'}"));
+ }
+
+ @Test
public void testUndefinedVariable() throws IOException, URISyntaxException,
InterruptedException {
ScopedException ex = singleFileErrorTest("/layout/undefined_variable_binding.xml",
@@ -141,6 +196,18 @@
}
@Test
+ public void testRootTag() throws IOException, URISyntaxException,
+ InterruptedException {
+ prepareProject();
+ copyResourceTo("/layout/root_tag.xml", "/app/src/main/res/layout/root_tag.xml");
+ CompilationResult result = runGradle("assembleDebug");
+ assertNotEquals(0, result.resultCode);
+ assertNotNull(result.error);
+ final String expected = String.format(ErrorMessages.ROOT_TAG_NOT_SUPPORTED, "hello");
+ assertTrue(result.error.contains(expected));
+ }
+
+ @Test
public void testInvalidVariableType() throws IOException, URISyntaxException,
InterruptedException {
prepareProject();
@@ -161,6 +228,25 @@
}
@Test
+ public void testModuleDependencyChange() throws IOException, URISyntaxException,
+ InterruptedException {
+ prepareApp(toMap(KEY_DEPENDENCIES, "compile project(':module1')",
+ KEY_SETTINGS_INCLUDES, "include ':app'\ninclude ':module1'"));
+ prepareModule("module1", "com.example.module1", toMap(
+ KEY_DEPENDENCIES, "compile 'com.android.support:appcompat-v7:23.1.1'"
+ ));
+ copyResourceTo("/layout/basic_layout.xml", "/module1/src/main/res/layout/module_layout.xml");
+ copyResourceTo("/layout/basic_layout.xml", "/app/src/main/res/layout/app_layout.xml");
+ CompilationResult result = runGradle("assembleDebug");
+ assertEquals(result.error, 0, result.resultCode);
+ File moduleFolder = new File(testFolder, "module1");
+ copyResourceTo("/module_build.gradle", new File(moduleFolder, "build.gradle"),
+ toMap());
+ result = runGradle("assembleDebug");
+ assertEquals(result.error, 0, result.resultCode);
+ }
+
+ @Test
public void testTwoLevelDependency() throws IOException, URISyntaxException, InterruptedException {
prepareApp(toMap(KEY_DEPENDENCIES, "compile project(':module1')",
KEY_SETTINGS_INCLUDES, "include ':app'\ninclude ':module1'\n"
diff --git a/compilationTests/src/test/resources/app_build.gradle b/compilationTests/src/test/resources/app_build.gradle
index 982f842..22632c5 100644
--- a/compilationTests/src/test/resources/app_build.gradle
+++ b/compilationTests/src/test/resources/app_build.gradle
@@ -1,10 +1,11 @@
apply plugin: 'com.android.application'
-apply plugin: 'com.android.databinding'
android {
- compileSdkVersion 21
- buildToolsVersion "22"
-
+ compileSdkVersion dataBindingConfig.compileSdkVersion
+ buildToolsVersion dataBindingConfig.buildToolsVersion
+ dataBinding {
+ enabled = true
+ }
defaultConfig {
minSdkVersion 7
targetSdkVersion 21
@@ -22,8 +23,8 @@
exclude 'META-INF/services/javax.annotation.processing.Processor'
}
compileOptions {
- sourceCompatibility JavaVersion.VERSION_1_7
- targetCompatibility JavaVersion.VERSION_1_7
+ sourceCompatibility JavaVersion.VERSION_1_6
+ targetCompatibility JavaVersion.VERSION_1_6
}
}
diff --git a/compilationTests/src/test/resources/layout/layout_with_bad_syntax.xml b/compilationTests/src/test/resources/layout/layout_with_bad_syntax.xml
new file mode 100644
index 0000000..6a90af8
--- /dev/null
+++ b/compilationTests/src/test/resources/layout/layout_with_bad_syntax.xml
@@ -0,0 +1,28 @@
+<?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.
+ -->
+
+<layout xmlns:android="http://schemas.android.com/apk/res/android"
+ xmlns:bind="http://schemas.android.com/apk/res-auto">
+ <data>
+ <variable name="myVar" type="String"/>
+ </data>
+ <LinearLayout
+ android:orientation="vertical"
+ android:layout_width="match_parent"
+ android:layout_height="match_parent">
+ <!-- undefined variable -->
+ <TextView android:layout_width="wrap_content" android:layout_height="wrap_content"
+ android:text="@{myVar.length())}"/>
+ </LinearLayout>
+</layout>
\ No newline at end of file
diff --git a/compilationTests/src/test/resources/layout/layout_with_completely_broken_syntax.xml b/compilationTests/src/test/resources/layout/layout_with_completely_broken_syntax.xml
new file mode 100644
index 0000000..dca87ae
--- /dev/null
+++ b/compilationTests/src/test/resources/layout/layout_with_completely_broken_syntax.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.
+ -->
+
+<layout xmlns:android="http://schemas.android.com/apk/res/android">
+ <LinearLayout
+ android:orientation="vertical"
+ android:layout_width="match_parent"
+ android:layout_height="match_parent">
+ <!-- undefined variable -->
+ <TextView android:layout_width="wrap_content" android:layout_height="wrap_content"
+ android:text="@{new String()}"/>
+ </LinearLayout>
+</layout>
\ No newline at end of file
diff --git a/compilationTests/src/test/resources/layout/root_tag.xml b/compilationTests/src/test/resources/layout/root_tag.xml
new file mode 100644
index 0000000..ed93ce0
--- /dev/null
+++ b/compilationTests/src/test/resources/layout/root_tag.xml
@@ -0,0 +1,29 @@
+<?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.
+ -->
+
+<layout xmlns:android="http://schemas.android.com/apk/res/android"
+ xmlns:bind="http://schemas.android.com/apk/res-auto">
+ <data>
+ <variable name="myVar" type="String"/>
+ </data>
+ <LinearLayout
+ android:tag="hello"
+ android:orientation="vertical"
+ android:layout_width="match_parent"
+ android:layout_height="match_parent">
+ <!-- undefined variable -->
+ <TextView android:layout_width="wrap_content" android:layout_height="wrap_content"
+ android:text="@{myVar}"/>
+ </LinearLayout>
+</layout>
\ No newline at end of file
diff --git a/compilationTests/src/test/resources/module_build.gradle b/compilationTests/src/test/resources/module_build.gradle
index 7287ad6..112b27b 100644
--- a/compilationTests/src/test/resources/module_build.gradle
+++ b/compilationTests/src/test/resources/module_build.gradle
@@ -15,12 +15,13 @@
*/
apply plugin: 'com.android.library'
-apply plugin: 'com.android.databinding'
android {
- compileSdkVersion 21
- buildToolsVersion "22"
-
+ compileSdkVersion dataBindingConfig.compileSdkVersion
+ buildToolsVersion dataBindingConfig.buildToolsVersion
+ dataBinding {
+ enabled = true
+ }
defaultConfig {
minSdkVersion 7
targetSdkVersion 21
@@ -38,8 +39,8 @@
exclude 'META-INF/services/javax.annotation.processing.Processor'
}
compileOptions {
- sourceCompatibility JavaVersion.VERSION_1_7
- targetCompatibility JavaVersion.VERSION_1_7
+ sourceCompatibility JavaVersion.VERSION_1_6
+ targetCompatibility JavaVersion.VERSION_1_6
}
}
diff --git a/compilationTests/src/test/resources/project_build.gradle b/compilationTests/src/test/resources/project_build.gradle
index fcf1b75..4622a5b 100644
--- a/compilationTests/src/test/resources/project_build.gradle
+++ b/compilationTests/src/test/resources/project_build.gradle
@@ -1,23 +1,23 @@
buildscript {
- ext.rootFolder = new File(project.projectDir, "../../..")
+ ext.dataBindingRootFolder = new File(project.projectDir, "../../..")
apply from: "${project.projectDir}/propLoaderClone.gradle"
ext.addRepos(repositories)
dependencies {
- classpath "com.android.databinding:dataBinder:${config.version}"
+ classpath "com.android.tools.build:gradle:${dataBindingConfig.androidPluginVersion}"
}
}
subprojects {
apply plugin: 'maven'
- group = config.group
- version = config.version
+ group = dataBindingConfig.group
+ version = dataBindingConfig.version
}
allprojects {
repositories {
jcenter()
maven {
- url config.mavenRepoDir
+ url dataBindingConfig.mavenRepoDir
}
}
}
diff --git a/compiler/build.gradle b/compiler/build.gradle
index 711bb39..8a9e991 100644
--- a/compiler/build.gradle
+++ b/compiler/build.gradle
@@ -14,31 +14,32 @@
* limitations under the License.
*/
apply plugin: 'java'
-apply plugin: "kotlin"
-apply plugin: 'com.android.databinding.bintray'
+apply plugin: 'kotlin'
+sourceCompatibility = dataBindingConfig.javaTargetCompatibility
+targetCompatibility = dataBindingConfig.javaSourceCompatibility
-sourceCompatibility = config.javaTargetCompatibility
-targetCompatibility = config.javaSourceCompatibility
+sourceSets {
+ main.java.srcDirs += 'src/main/kotlin'
+}
buildscript {
+ // to make IJ happy
+ ext.kotlin_version = dataBindingConfig.kotlinVersion
dependencies {
- classpath "org.jetbrains.kotlin:kotlin-gradle-plugin:${config.kotlinVersion}"
classpath 'commons-io:commons-io:2.4'
+ classpath "org.jetbrains.kotlin:kotlin-gradle-plugin:$kotlin_version"
}
}
dependencies {
- compile project(":compilerCommon")
- compile project(':baseLibrary')
- compile 'org.apache.commons:commons-lang3:3.3.2'
+ compile project(':dataBinding:compilerCommon')
+ compile project(':dataBinding:baseLibrary')
+ compile "org.jetbrains.kotlin:kotlin-stdlib:$kotlin_version"
compile 'commons-io:commons-io:2.4'
compile 'commons-codec:commons-codec:1.10'
- compile "org.jetbrains.kotlin:kotlin-stdlib:${config.kotlinVersion}"
-
compile 'com.tunnelvisionlabs:antlr4:4.5'
- testCompile files('../baseLibrary/src/main/java')
- testCompile files('../library/src/main/java')
+ compile 'com.googlecode.juniversalchardet:juniversalchardet:1.0.3'
testCompile 'junit:junit:4.12'
}
@@ -49,9 +50,9 @@
pom.project {
licenses {
license {
- name config.licenseName
- url config.licenseUrl
- distribution config.licenseDistribution
+ name dataBindingConfig.licenseName
+ url dataBindingConfig.licenseUrl
+ distribution dataBindingConfig.licenseDistribution
}
}
}
@@ -59,23 +60,10 @@
}
}
-project(':library').afterEvaluate { libProject ->
- tasks['compileTestKotlin'].dependsOn libProject.tasks['uploadJarArchives']
-}
-task fatJar(type: Jar) {
- baseName = project.name + '-all'
- doFirst {
- tasks.findByName("buildLicenseNoticeFor${project.name.capitalize()}").execute()
- }
- from { configurations.compile.collect { it.isDirectory() ? it : zipTree(it) } }
- from new File(project.buildDir,'NOTICE.txt')
- archiveName "databinding-studio-bundle.jar"
- destinationDir = new File(config.prebuildFolder)
- with jar
-}
-task prebuild() {
- dependsOn fatJar
-}
-
+project.ext.pomName = 'Data Binding Annotation Processor'
+project.ext.pomDesc = 'The annotation processor for Data Binding. Generates binding classes for runtime.'
+enablePublishing(this, true)
+fullJar(project)
+test.enabled = !System.getProperty("java.version").startsWith("1.6")
diff --git a/compiler/compiler.iml b/compiler/compiler.iml
new file mode 100644
index 0000000..b0d4edf
--- /dev/null
+++ b/compiler/compiler.iml
@@ -0,0 +1,36 @@
+<?xml version="1.0" encoding="UTF-8"?>
+<module external.linked.project.id=":dataBinding:compiler" external.linked.project.path="$MODULE_DIR$" external.root.project.path="$MODULE_DIR$/.." external.system.id="GRADLE" external.system.module.group="com.android.databinding" external.system.module.version="1.1" type="JAVA_MODULE" version="4">
+ <component name="NewModuleRootManager" inherit-compiler-output="false">
+ <output url="file://$MODULE_DIR$/build/classes/main" />
+ <output-test url="file://$MODULE_DIR$/build/classes/test" />
+ <exclude-output />
+ <content url="file://$MODULE_DIR$">
+ <sourceFolder url="file://$MODULE_DIR$/src/main/java" isTestSource="false" />
+ <sourceFolder url="file://$MODULE_DIR$/src/main/kotlin" isTestSource="false" />
+ <sourceFolder url="file://$MODULE_DIR$/src/test/java" isTestSource="true" />
+ <sourceFolder url="file://$MODULE_DIR$/src/test/kotlin" isTestSource="true" />
+ <sourceFolder url="file://$MODULE_DIR$/src/main/resources" type="java-resource" />
+ <sourceFolder url="file://$MODULE_DIR$/src/test/resources" type="java-test-resource" />
+ <excludeFolder url="file://$MODULE_DIR$/.gradle" />
+ <excludeFolder url="file://$MODULE_DIR$/build" />
+ </content>
+ <orderEntry type="inheritedJdk" />
+ <orderEntry type="sourceFolder" forTests="false" />
+ <orderEntry type="module" module-name="compilerCommon" />
+ <orderEntry type="module" module-name="baseLibrary" />
+ <orderEntry type="library" name="Gradle: org.jetbrains.kotlin:kotlin-stdlib:1.0.0-beta-4584" level="project" />
+ <orderEntry type="library" name="Gradle: commons-io:commons-io:2.4" level="project" />
+ <orderEntry type="library" name="Gradle: commons-codec:commons-codec:1.10" level="project" />
+ <orderEntry type="library" name="Gradle: com.tunnelvisionlabs:antlr4:4.5" level="project" />
+ <orderEntry type="library" name="Gradle: com.googlecode.juniversalchardet:juniversalchardet:1.0.3" level="project" />
+ <orderEntry type="library" name="Gradle: org.jetbrains.kotlin:kotlin-runtime:1.0.0-beta-4584" level="project" />
+ <orderEntry type="library" name="Gradle: com.tunnelvisionlabs:antlr4-runtime:4.5" level="project" />
+ <orderEntry type="library" name="Gradle: com.tunnelvisionlabs:antlr4-annotations:4.5" level="project" />
+ <orderEntry type="library" name="Gradle: org.antlr:antlr-runtime:3.5.2" level="project" />
+ <orderEntry type="library" name="Gradle: org.antlr:ST4:4.0.8" level="project" />
+ <orderEntry type="library" name="Gradle: org.abego.treelayout:org.abego.treelayout.core:1.0.1" level="project" />
+ <orderEntry type="library" scope="TEST" name="Gradle: junit:junit:4.12" level="project" />
+ <orderEntry type="library" scope="TEST" name="Gradle: org.hamcrest:hamcrest-core:1.3" level="project" />
+ <orderEntry type="library" name="Gradle: com.google.guava:guava:17.0" level="project" />
+ </component>
+</module>
\ No newline at end of file
diff --git a/compiler/db-compiler.iml b/compiler/db-compiler.iml
new file mode 100644
index 0000000..2f3a0d2
--- /dev/null
+++ b/compiler/db-compiler.iml
@@ -0,0 +1,41 @@
+<?xml version="1.0" encoding="UTF-8"?>
+<module relativePaths="true" type="JAVA_MODULE" version="4">
+ <component name="NewModuleRootManager" inherit-compiler-output="true">
+ <exclude-output />
+ <content url="file://$MODULE_DIR$">
+ <sourceFolder url="file://$MODULE_DIR$/src/main/java" isTestSource="false" />
+ <sourceFolder url="file://$MODULE_DIR$/src/main/kotlin" isTestSource="false" />
+ <sourceFolder url="file://$MODULE_DIR$/src/test/java" isTestSource="true" />
+ <excludeFolder url="file://$MODULE_DIR$/.gradle" />
+ <excludeFolder url="file://$MODULE_DIR$/build" />
+ <excludeFolder url="file://$MODULE_DIR$/gradle" />
+ </content>
+ <orderEntry type="inheritedJdk" />
+ <orderEntry type="sourceFolder" forTests="false" />
+ <orderEntry type="module" module-name="db-baseLibrary" exported="" />
+ <orderEntry type="module" module-name="db-compilerCommon" />
+ <orderEntry type="library" name="KotlinJavaRuntime" level="project" />
+ <orderEntry type="module-library" scope="TEST">
+ <library>
+ <CLASSES>
+ <root url="jar://$MODULE_DIR$/../../../prebuilts/tools/common/m2/repository/junit/junit/4.12/junit-4.12.jar!/" />
+ </CLASSES>
+ <JAVADOC />
+ <SOURCES>
+ <root url="jar://$MODULE_DIR$/../../../prebuilts/tools/common/m2/repository/junit/junit/4.12/junit-4.12-sources.jar!/" />
+ </SOURCES>
+ </library>
+ </orderEntry>
+ <orderEntry type="module-library" scope="TEST">
+ <library>
+ <CLASSES>
+ <root url="jar://$MODULE_DIR$/../../../prebuilts/tools/common/m2/repository/org/hamcrest/hamcrest-core/1.3/hamcrest-core-1.3.jar!/" />
+ </CLASSES>
+ <JAVADOC />
+ <SOURCES>
+ <root url="jar://$MODULE_DIR$/../../../prebuilts/tools/common/m2/repository/org/hamcrest/hamcrest-core/1.3/hamcrest-core-1.3-sources.jar!/" />
+ </SOURCES>
+ </library>
+ </orderEntry>
+ </component>
+</module>
\ No newline at end of file
diff --git a/compiler/gradle/wrapper/gradle-wrapper.properties b/compiler/gradle/wrapper/gradle-wrapper.properties
index 29fb85e..1f1bbb6 100644
--- a/compiler/gradle/wrapper/gradle-wrapper.properties
+++ b/compiler/gradle/wrapper/gradle-wrapper.properties
@@ -3,4 +3,4 @@
distributionPath=wrapper/dists
zipStoreBase=GRADLE_USER_HOME
zipStorePath=wrapper/dists
-distributionUrl=https\://services.gradle.org/distributions/gradle-2.1-bin.zip
+distributionUrl=https\://services.gradle.org/distributions/gradle-2.10-bin.zip
diff --git a/compiler/src/main/java/android/databinding/annotationprocessor/ProcessBindable.java b/compiler/src/main/java/android/databinding/annotationprocessor/ProcessBindable.java
index ae73d95..d96e5da 100644
--- a/compiler/src/main/java/android/databinding/annotationprocessor/ProcessBindable.java
+++ b/compiler/src/main/java/android/databinding/annotationprocessor/ProcessBindable.java
@@ -33,8 +33,6 @@
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;
@@ -45,7 +43,6 @@
import javax.lang.model.util.Types;
// binding app info and library info are necessary to trigger this.
-@SupportedSourceVersion(SourceVersion.RELEASE_7)
public class ProcessBindable extends ProcessDataBinding.ProcessingStep implements BindableHolder {
Intermediate mProperties;
HashMap<String, HashSet<String>> mLayoutVariables = new HashMap<String, HashSet<String>>();
diff --git a/compiler/src/main/java/android/databinding/annotationprocessor/ProcessDataBinding.java b/compiler/src/main/java/android/databinding/annotationprocessor/ProcessDataBinding.java
index e6d925e..4d1fadb 100644
--- a/compiler/src/main/java/android/databinding/annotationprocessor/ProcessDataBinding.java
+++ b/compiler/src/main/java/android/databinding/annotationprocessor/ProcessDataBinding.java
@@ -20,6 +20,7 @@
import android.databinding.tool.CompilerChef;
import android.databinding.tool.processing.Scope;
import android.databinding.tool.reflection.ModelAnalyzer;
+import android.databinding.tool.util.L;
import android.databinding.tool.util.Preconditions;
import android.databinding.tool.writer.AnnotationJavaFileWriter;
import android.databinding.tool.writer.BRWriter;
@@ -33,9 +34,9 @@
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;
+import javax.xml.bind.JAXBException;
@SupportedAnnotationTypes({
"android.databinding.BindingAdapter",
@@ -44,7 +45,6 @@
"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.
@@ -62,7 +62,11 @@
}
boolean done = true;
for (ProcessingStep step : mProcessingSteps) {
- done = step.runStep(roundEnv, processingEnv, buildInfo) && done;
+ try {
+ done = step.runStep(roundEnv, processingEnv, buildInfo) && done;
+ } catch (JAXBException e) {
+ L.e(e, "Exception while handling step %s", step);
+ }
}
if (roundEnv.processingOver()) {
for (ProcessingStep step : mProcessingSteps) {
@@ -73,6 +77,11 @@
return done;
}
+ @Override
+ public SourceVersion getSupportedSourceVersion() {
+ return SourceVersion.latest();
+ }
+
private void initProcessingSteps() {
final ProcessBindable processBindable = new ProcessBindable();
mProcessingSteps = Arrays.asList(
@@ -94,6 +103,7 @@
mLibraryProject = libraryProject;
mMinSdk = minSdk;
considerWritingMapper();
+ mChef.writeDynamicUtil();
}
private void considerWritingMapper() {
@@ -137,7 +147,7 @@
private boolean runStep(RoundEnvironment roundEnvironment,
ProcessingEnvironment processingEnvironment,
- BindingBuildInfo buildInfo) {
+ BindingBuildInfo buildInfo) throws JAXBException {
if (mDone) {
return true;
}
@@ -152,7 +162,7 @@
*/
abstract public boolean onHandleStep(RoundEnvironment roundEnvironment,
ProcessingEnvironment processingEnvironment,
- BindingBuildInfo buildInfo);
+ BindingBuildInfo buildInfo) throws JAXBException;
/**
* Invoked when processing is done. A good place to generate the output if the
diff --git a/compiler/src/main/java/android/databinding/annotationprocessor/ProcessExpressions.java b/compiler/src/main/java/android/databinding/annotationprocessor/ProcessExpressions.java
index ab2b8bf..e1c447c 100644
--- a/compiler/src/main/java/android/databinding/annotationprocessor/ProcessExpressions.java
+++ b/compiler/src/main/java/android/databinding/annotationprocessor/ProcessExpressions.java
@@ -16,23 +16,29 @@
package android.databinding.annotationprocessor;
+import com.google.common.base.Joiner;
+
import org.apache.commons.io.FileUtils;
import org.apache.commons.io.IOUtils;
-import org.apache.commons.lang3.StringUtils;
import android.databinding.BindingBuildInfo;
import android.databinding.tool.CompilerChef;
+import android.databinding.tool.LayoutXmlProcessor;
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 android.databinding.tool.util.Preconditions;
+import android.databinding.tool.util.StringUtils;
import java.io.File;
import java.io.FilenameFilter;
import java.io.IOException;
import java.io.InputStream;
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;
@@ -49,30 +55,55 @@
@Override
public boolean onHandleStep(RoundEnvironment roundEnvironment,
- ProcessingEnvironment processingEnvironment, BindingBuildInfo buildInfo) {
+ ProcessingEnvironment processingEnvironment, BindingBuildInfo buildInfo)
+ throws JAXBException {
ResourceBundle resourceBundle;
SdkUtil.initialize(buildInfo.minSdk(), new File(buildInfo.sdkRoot()));
resourceBundle = new ResourceBundle(buildInfo.modulePackage());
- List<Intermediate> intermediateList =
- GenerationalClassUtil.loadObjects(
- GenerationalClassUtil.ExtensionFilter.LAYOUT);
- IntermediateV1 mine = createIntermediateFromLayouts(buildInfo.layoutInfoDir());
+ List<IntermediateV2> intermediateList = loadDependencyIntermediates();
+ for (Intermediate intermediate : intermediateList) {
+ try {
+ intermediate.appendTo(resourceBundle);
+ } catch (Throwable throwable) {
+ L.e(throwable, "unable to prepare resource bundle");
+ }
+ }
+
+ IntermediateV2 mine = createIntermediateFromLayouts(buildInfo.layoutInfoDir(),
+ intermediateList);
if (mine != null) {
- mine.removeOverridden(intermediateList);
+ mine.updateOverridden(resourceBundle);
intermediateList.add(mine);
saveIntermediate(processingEnvironment, buildInfo, mine);
+ mine.appendTo(resourceBundle);
}
// generate them here so that bindable parser can read
try {
- generateBinders(resourceBundle, buildInfo, intermediateList);
+ writeResourceBundle(resourceBundle, buildInfo.isLibrary(), buildInfo.minSdk(),
+ buildInfo.exportClassListTo());
} catch (Throwable t) {
L.e(t, "cannot generate view binders");
}
return true;
}
+ private List<IntermediateV2> loadDependencyIntermediates() {
+ final List<Intermediate> original = GenerationalClassUtil.loadObjects(
+ GenerationalClassUtil.ExtensionFilter.LAYOUT);
+ final List<IntermediateV2> upgraded = new ArrayList<IntermediateV2>(original.size());
+ for (Intermediate intermediate : original) {
+ final Intermediate updatedIntermediate = intermediate.upgrade();
+ Preconditions.check(updatedIntermediate instanceof IntermediateV2, "Incompatible data"
+ + " binding dependency. Please update your dependencies or recompile them with"
+ + " application module's data binding version.");
+ //noinspection ConstantConditions
+ upgraded.add((IntermediateV2) updatedIntermediate);
+ }
+ return upgraded;
+ }
+
private void saveIntermediate(ProcessingEnvironment processingEnvironment,
- BindingBuildInfo buildInfo, IntermediateV1 intermediate) {
+ BindingBuildInfo buildInfo, IntermediateV2 intermediate) {
GenerationalClassUtil.writeIntermediateFile(processingEnvironment,
buildInfo.modulePackage(), buildInfo.modulePackage() +
GenerationalClassUtil.ExtensionFilter.LAYOUT.getExtension(),
@@ -84,27 +115,22 @@
ProcessingEnvironment processingEnvironment, BindingBuildInfo buildInfo) {
}
- private void generateBinders(ResourceBundle resourceBundle, BindingBuildInfo buildInfo,
- List<Intermediate> intermediates)
- throws Throwable {
- for (Intermediate intermediate : intermediates) {
- intermediate.appendTo(resourceBundle);
+ private IntermediateV2 createIntermediateFromLayouts(String layoutInfoFolderPath,
+ List<IntermediateV2> intermediateList) {
+ final Set<String> excludeList = new HashSet<String>();
+ for (IntermediateV2 lib : intermediateList) {
+ excludeList.addAll(lib.mLayoutInfoMap.keySet());
}
- writeResourceBundle(resourceBundle, buildInfo.isLibrary(), buildInfo.minSdk(),
- buildInfo.exportClassListTo());
- }
-
- 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();
+ IntermediateV2 result = new IntermediateV2();
for (File layoutFile : layoutInfoFolder.listFiles(new FilenameFilter() {
@Override
public boolean accept(File dir, String name) {
- return name.endsWith(".xml");
+ return name.endsWith(".xml") && !excludeList.contains(name);
}
})) {
try {
@@ -133,7 +159,7 @@
}
if (forLibraryModule) {
Set<String> classNames = compilerChef.getWrittenClassNames();
- String out = StringUtils.join(classNames, System.getProperty("line.separator"));
+ String out = Joiner.on(StringUtils.LINE_SEPARATOR).join(classNames);
L.d("Writing list of classes to %s . \nList:%s", exportClassNamesTo, out);
try {
//noinspection ConstantConditions
@@ -145,11 +171,11 @@
mCallback.onChefReady(compilerChef, forLibraryModule, minSdk);
}
- public static interface Intermediate extends Serializable {
+ public interface Intermediate extends Serializable {
Intermediate upgrade();
- public void appendTo(ResourceBundle resourceBundle) throws Throwable;
+ void appendTo(ResourceBundle resourceBundle) throws Throwable;
}
public static class IntermediateV1 implements Intermediate {
@@ -161,7 +187,10 @@
@Override
public Intermediate upgrade() {
- return this;
+ final IntermediateV2 updated = new IntermediateV2();
+ updated.mLayoutInfoMap = mLayoutInfoMap;
+ updated.mUnmarshaller = mUnmarshaller;
+ return updated;
}
@Override
@@ -188,19 +217,53 @@
mLayoutInfoMap.put(name, contents);
}
+ // keeping the method to match deserialized structure
+ @SuppressWarnings("unused")
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);
- }
- }
+ }
+ }
+
+ public static class IntermediateV2 extends IntermediateV1 {
+ // specify so that we can define updates ourselves.
+ private static final long serialVersionUID = 2L;
+ @Override
+ public void appendTo(ResourceBundle resourceBundle) throws JAXBException {
+ for (Map.Entry<String, String> entry : mLayoutInfoMap.entrySet()) {
+ final InputStream is = IOUtils.toInputStream(entry.getValue());
+ try {
+ final ResourceBundle.LayoutFileBundle bundle = ResourceBundle.LayoutFileBundle
+ .fromXML(is);
+ resourceBundle.addLayoutBundle(bundle);
+ L.d("loaded layout info file %s", bundle);
+ } finally {
+ IOUtils.closeQuietly(is);
+ }
+ }
+ }
+
+ /**
+ * if a layout is overridden from a module (which happens when layout is auto-generated),
+ * we need to update its contents from the class that overrides it.
+ * This must be done before this bundle is saved, otherwise, it will not be recognized
+ * when it is used in another project.
+ */
+ public void updateOverridden(ResourceBundle bundle) throws JAXBException {
+ // When a layout is copied from inherited module, it is eleminated while reading
+ // info files. (createIntermediateFromLayouts).
+ // Build process may also duplicate some files at compile time. This is where
+ // we detect those copies and force inherit their module and classname information.
+ final HashMap<String, List<ResourceBundle.LayoutFileBundle>> bundles = bundle
+ .getLayoutBundles();
+ for (Map.Entry<String, String> info : mLayoutInfoMap.entrySet()) {
+ String key = LayoutXmlProcessor.exportLayoutNameFromInfoFileName(info.getKey());
+ final List<ResourceBundle.LayoutFileBundle> existingList = bundles.get(key);
+ if (existingList != null && !existingList.isEmpty()) {
+ ResourceBundle.LayoutFileBundle myBundle = ResourceBundle.LayoutFileBundle
+ .fromXML(IOUtils.toInputStream(info.getValue()));
+ final ResourceBundle.LayoutFileBundle inheritFrom = existingList.get(0);
+ myBundle.inheritConfigurationFrom(inheritFrom);
+ L.d("inheriting data for %s (%s) from %s", info.getKey(), key, inheritFrom);
+ mLayoutInfoMap.put(info.getKey(), myBundle.toXML());
}
}
}
diff --git a/compiler/src/main/java/android/databinding/annotationprocessor/ProcessMethodAdapters.java b/compiler/src/main/java/android/databinding/annotationprocessor/ProcessMethodAdapters.java
index 72cfc62..36c4dd8 100644
--- a/compiler/src/main/java/android/databinding/annotationprocessor/ProcessMethodAdapters.java
+++ b/compiler/src/main/java/android/databinding/annotationprocessor/ProcessMethodAdapters.java
@@ -20,6 +20,9 @@
import android.databinding.BindingConversion;
import android.databinding.BindingMethod;
import android.databinding.BindingMethods;
+import android.databinding.InverseBindingAdapter;
+import android.databinding.InverseBindingMethod;
+import android.databinding.InverseBindingMethods;
import android.databinding.Untaggable;
import android.databinding.tool.reflection.ModelAnalyzer;
import android.databinding.tool.store.SetterStore;
@@ -43,9 +46,10 @@
import javax.lang.model.type.TypeMirror;
import javax.lang.model.util.Elements;
import javax.lang.model.util.Types;
-import javax.tools.Diagnostic;
public class ProcessMethodAdapters extends ProcessDataBinding.ProcessingStep {
+ private final static String INVERSE_BINDING_EVENT_ATTR_SUFFIX = "AttrChanged";
+
public ProcessMethodAdapters() {
}
@@ -60,9 +64,11 @@
clearIncrementalClasses(roundEnv, store);
addBindingAdapters(roundEnv, processingEnvironment, store);
- addRenamed(roundEnv, processingEnvironment, store);
- addConversions(roundEnv, processingEnvironment, store);
- addUntaggable(roundEnv, processingEnvironment, store);
+ addRenamed(roundEnv, store);
+ addConversions(roundEnv, store);
+ addUntaggable(roundEnv, store);
+ addInverseAdapters(roundEnv, processingEnvironment, store);
+ addInverseMethods(roundEnv, store);
try {
store.write(buildInfo.modulePackage(), processingEnvironment);
@@ -84,7 +90,7 @@
.getElementsAnnotatedWith(roundEnv, BindingAdapter.class)) {
if (element.getKind() != ElementKind.METHOD ||
!element.getModifiers().contains(Modifier.PUBLIC)) {
- L.e("@BindingAdapter on invalid element: %s", element);
+ L.e(element, "@BindingAdapter on invalid element: %s", element);
continue;
}
BindingAdapter bindingAdapter = element.getAnnotation(BindingAdapter.class);
@@ -92,7 +98,8 @@
ExecutableElement executableElement = (ExecutableElement) element;
List<? extends VariableElement> parameters = executableElement.getParameters();
if (bindingAdapter.value().length == 0) {
- L.e("@BindingAdapter requires at least one attribute. %s", element);
+ L.e(element, "@BindingAdapter requires at least one attribute. %s",
+ element);
continue;
}
@@ -107,9 +114,9 @@
for (int i = startIndex; i < numAttributes + startIndex; i++) {
if (!typeUtils.isSameType(parameters.get(i).asType(),
parameters.get(i + numAttributes).asType())) {
- L.e("BindingAdapter %s: old values should be followed by new values. " +
- "Parameter %d must be the same type as parameter %d.",
- executableElement, i + 1, i + numAttributes + 1);
+ L.e(executableElement, "BindingAdapter %s: old values should be followed " +
+ "by new values. Parameter %d must be the same type as parameter " +
+ "%d.", executableElement, i + 1, i + numAttributes + 1);
hasParameterError = true;
break;
}
@@ -118,24 +125,24 @@
continue;
}
} else if (numAdditionalArgs != numAttributes) {
- L.e("@BindingAdapter %s has %d attributes and %d value parameters. There should " +
- "be %d or %d value parameters.", executableElement, numAttributes,
- numAdditionalArgs, numAttributes, numAttributes * 2);
+ L.e(element, "@BindingAdapter %s has %d attributes and %d value " +
+ "parameters. There should be %d or %d value parameters.",
+ executableElement, numAttributes, numAdditionalArgs, numAttributes,
+ numAttributes * 2);
continue;
}
- warnAttributeNamespaces(bindingAdapter.value());
+ warnAttributeNamespaces(element, bindingAdapter.value());
try {
if (numAttributes == 1) {
final String attribute = bindingAdapter.value()[0];
- L.d("------------------ @BindingAdapter for %s", element);
store.addBindingAdapter(processingEnv, attribute, executableElement,
takesComponent);
} else {
store.addBindingAdapter(processingEnv, bindingAdapter.value(),
- executableElement, takesComponent);
+ executableElement, takesComponent, bindingAdapter.requireAll());
}
} catch (IllegalArgumentException e) {
- L.e(e, "@BindingAdapter for duplicate View and parameter type: %s", element);
+ L.e(element, "@BindingAdapter for duplicate View and parameter type: %s", element);
}
}
}
@@ -158,8 +165,8 @@
TypeMirror viewStubProxy = elementUtils.
getTypeElement("android.databinding.ViewStubProxy").asType();
if (!typeUtils.isAssignable(parameter1, viewStubProxy)) {
- L.e("@BindingAdapter %s is applied to a method that has two parameters, the " +
- "first must be a View type", executableElement);
+ L.e(executableElement, "@BindingAdapter %s is applied to a method that has two " +
+ "parameters, the first must be a View type", executableElement);
}
return false;
}
@@ -167,27 +174,27 @@
if (typeUtils.isAssignable(parameter2, viewElement)) {
return true; // second parameter is a View
}
- L.e("@BindingAdapter %s is applied to a method that doesn't take a View subclass as the " +
- "first or second parameter. When a BindingAdapter uses a DataBindingComponent, " +
- "the component parameter is first and the View parameter is second, otherwise " +
- "the View parameter is first.", executableElement);
+ L.e(executableElement, "@BindingAdapter %s is applied to a method that doesn't take a " +
+ "View subclass as the first or second parameter. When a BindingAdapter uses a " +
+ "DataBindingComponent, the component parameter is first and the View " +
+ "parameter is second, otherwise the View parameter is first.",
+ executableElement);
return false;
}
- private static void warnAttributeNamespace(String attribute) {
+ private static void warnAttributeNamespace(Element element, String attribute) {
if (attribute.contains(":") && !attribute.startsWith("android:")) {
- L.w("Application namespace for attribute %s will be ignored.", attribute);
+ L.w(element, "Application namespace for attribute %s will be ignored.", attribute);
}
}
- private static void warnAttributeNamespaces(String[] attributes) {
+ private static void warnAttributeNamespaces(Element element, String[] attributes) {
for (String attribute : attributes) {
- warnAttributeNamespace(attribute);
+ warnAttributeNamespace(element, attribute);
}
}
- private void addRenamed(RoundEnvironment roundEnv, ProcessingEnvironment processingEnv,
- SetterStore store) {
+ private void addRenamed(RoundEnvironment roundEnv, SetterStore store) {
for (Element element : AnnotationUtil
.getElementsAnnotatedWith(roundEnv, BindingMethods.class)) {
BindingMethods bindingMethods = element.getAnnotation(BindingMethods.class);
@@ -195,7 +202,7 @@
for (BindingMethod bindingMethod : bindingMethods.value()) {
final String attribute = bindingMethod.attribute();
final String method = bindingMethod.method();
- warnAttributeNamespace(attribute);
+ warnAttributeNamespace(element, attribute);
String type;
try {
type = bindingMethod.type().getCanonicalName();
@@ -207,38 +214,97 @@
}
}
- private void addConversions(RoundEnvironment roundEnv,
- ProcessingEnvironment processingEnv, SetterStore store) {
+ private void addConversions(RoundEnvironment roundEnv, 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);
+ L.e(element, "@BindingConversion is only allowed on public static methods %s",
+ element);
continue;
}
ExecutableElement executableElement = (ExecutableElement) element;
if (executableElement.getParameters().size() != 1) {
- processingEnv.getMessager().printMessage(Diagnostic.Kind.ERROR,
- "@BindingConversion method should have one parameter: " + element);
+ L.e(element, "@BindingConversion method should have one parameter %s", element);
continue;
}
if (executableElement.getReturnType().getKind() == TypeKind.VOID) {
- processingEnv.getMessager().printMessage(Diagnostic.Kind.ERROR,
- "@BindingConversion method must return a value: " + element);
+ L.e(element, "@BindingConversion method must return a value %s", element);
continue;
}
- processingEnv.getMessager().printMessage(Diagnostic.Kind.NOTE,
- "added conversion: " + element);
store.addConversionMethod(executableElement);
}
}
- private void addUntaggable(RoundEnvironment roundEnv,
+ private void addInverseAdapters(RoundEnvironment roundEnv,
ProcessingEnvironment processingEnv, SetterStore store) {
- for (Element element : AnnotationUtil.getElementsAnnotatedWith(roundEnv, Untaggable.class)) {
+ for (Element element : AnnotationUtil
+ .getElementsAnnotatedWith(roundEnv, InverseBindingAdapter.class)) {
+ if (!element.getModifiers().contains(Modifier.PUBLIC)) {
+ L.e(element, "@InverseBindingAdapter must be associated with a public method");
+ continue;
+ }
+ ExecutableElement executableElement = (ExecutableElement) element;
+ if (executableElement.getReturnType().getKind() == TypeKind.VOID) {
+ L.e(element, "@InverseBindingAdapter must have a non-void return type");
+ continue;
+ }
+ final InverseBindingAdapter inverseBindingAdapter =
+ executableElement.getAnnotation(InverseBindingAdapter.class);
+ final String attribute = inverseBindingAdapter.attribute();
+ warnAttributeNamespace(element, attribute);
+ final String event = inverseBindingAdapter.event().isEmpty()
+ ? inverseBindingAdapter.attribute() + INVERSE_BINDING_EVENT_ATTR_SUFFIX
+ : inverseBindingAdapter.event();
+ warnAttributeNamespace(element, event);
+ final boolean takesComponent = takesComponent(executableElement, processingEnv);
+ final int expectedArgs = takesComponent ? 2 : 1;
+ final int numParameters = executableElement.getParameters().size();
+ if (numParameters != expectedArgs) {
+ L.e(element, "@InverseBindingAdapter %s takes %s parameters, but %s parameters " +
+ "were expected", element, numParameters, expectedArgs);
+ continue;
+ }
+ try {
+ store.addInverseAdapter(processingEnv, attribute, event, executableElement,
+ takesComponent);
+ } catch (IllegalArgumentException e) {
+ L.e(element, "@InverseBindingAdapter for duplicate View and parameter type: %s",
+ element);
+ }
+ }
+ }
+
+ private void addInverseMethods(RoundEnvironment roundEnv, SetterStore store) {
+ for (Element element : AnnotationUtil
+ .getElementsAnnotatedWith(roundEnv, InverseBindingMethods.class)) {
+ InverseBindingMethods bindingMethods =
+ element.getAnnotation(InverseBindingMethods.class);
+
+ for (InverseBindingMethod bindingMethod : bindingMethods.value()) {
+ final String attribute = bindingMethod.attribute();
+ final String method = bindingMethod.method();
+ final String event = bindingMethod.event().isEmpty()
+ ? bindingMethod.attribute() + INVERSE_BINDING_EVENT_ATTR_SUFFIX
+ : bindingMethod.event();
+ warnAttributeNamespace(element, attribute);
+ warnAttributeNamespace(element, event);
+ String type;
+ try {
+ type = bindingMethod.type().getCanonicalName();
+ } catch (MirroredTypeException e) {
+ type = e.getTypeMirror().toString();
+ }
+ store.addInverseMethod(attribute, event, type, method, (TypeElement) element);
+ }
+ }
+ }
+
+ private void addUntaggable(RoundEnvironment roundEnv, SetterStore store) {
+ for (Element element : AnnotationUtil.
+ getElementsAnnotatedWith(roundEnv, Untaggable.class)) {
Untaggable untaggable = element.getAnnotation(Untaggable.class);
store.addUntaggableTypes(untaggable.value(), (TypeElement) element);
}
@@ -261,10 +327,10 @@
classes.add(((TypeElement) element.getEnclosingElement()).getQualifiedName().
toString());
}
- for (Element element : AnnotationUtil.getElementsAnnotatedWith(roundEnv, Untaggable.class)) {
+ for (Element element : AnnotationUtil.
+ getElementsAnnotatedWith(roundEnv, Untaggable.class)) {
classes.add(((TypeElement) element).getQualifiedName().toString());
}
store.clear(classes);
}
-
}
diff --git a/compiler/src/main/java/android/databinding/tool/Binding.java b/compiler/src/main/java/android/databinding/tool/Binding.java
index 5238edb..aba6b3e 100644
--- a/compiler/src/main/java/android/databinding/tool/Binding.java
+++ b/compiler/src/main/java/android/databinding/tool/Binding.java
@@ -24,24 +24,29 @@
import android.databinding.tool.reflection.ModelClass;
import android.databinding.tool.store.Location;
import android.databinding.tool.store.SetterStore;
+import android.databinding.tool.store.SetterStore.BindingSetterCall;
import android.databinding.tool.store.SetterStore.SetterCall;
import android.databinding.tool.util.L;
-import android.databinding.tool.writer.CodeGenUtil;
-import android.databinding.tool.writer.WriterPackage;
+import android.databinding.tool.writer.LayoutBinderWriterKt;
import java.util.List;
public class Binding implements LocationScopeProvider {
private final String mName;
- private final Expr mExpr;
+ private Expr mExpr;
private final BindingTarget mTarget;
- private SetterStore.SetterCall mSetterCall;
+ private BindingSetterCall mSetterCall;
public Binding(BindingTarget target, String name, Expr expr) {
+ this(target, name, expr, null);
+ }
+
+ public Binding(BindingTarget target, String name, Expr expr, BindingSetterCall setterCall) {
mTarget = target;
mName = name;
mExpr = expr;
+ mSetterCall = setterCall;
}
@Override
@@ -50,9 +55,18 @@
}
public void resolveListeners() {
- ModelClass listenerParameter = getListenerParameter();
- if (listenerParameter != null) {
- mExpr.resolveListeners(listenerParameter);
+ final ModelClass listenerParameter = getListenerParameter(mTarget, mName, mExpr);
+ Expr listenerExpr = mExpr.resolveListeners(listenerParameter, null);
+ if (listenerExpr != mExpr) {
+ listenerExpr.setBindingExpression(true);
+ mExpr = listenerExpr;
+ }
+ }
+
+ public void resolveTwoWayExpressions() {
+ Expr expr = mExpr.resolveTwoWayExpressions(null);
+ if (expr != mExpr) {
+ mExpr = expr;
}
}
@@ -76,19 +90,20 @@
private void resolveSetterCall() {
ModelClass viewType = mTarget.getResolvedType();
if (viewType != null && viewType.extendsViewStub()) {
- if (isListenerAttribute()) {
+ if (isListenerAttribute(mName)) {
ModelAnalyzer modelAnalyzer = ModelAnalyzer.getInstance();
ModelClass viewStubProxy = modelAnalyzer.
findClass("android.databinding.ViewStubProxy", null);
mSetterCall = SetterStore.get(modelAnalyzer).getSetterCall(mName,
viewStubProxy, mExpr.getResolvedType(), mExpr.getModel().getImports());
- } else if (isViewStubAttribute()) {
+ } else if (isViewStubAttribute(mName)) {
mSetterCall = new ViewStubDirectCall(mName, viewType, mExpr);
} else {
mSetterCall = new ViewStubSetterCall(mName);
}
} else {
- mSetterCall = SetterStore.get(ModelAnalyzer.getInstance()).getSetterCall(mName,
+ final SetterStore setterStore = SetterStore.get(ModelAnalyzer.getInstance());
+ mSetterCall = setterStore.getSetterCall(mName,
viewType, mExpr.getResolvedType(), mExpr.getModel().getImports());
}
}
@@ -96,31 +111,38 @@
/**
* Similar to getSetterCall, but assumes an Object parameter to find the best matching listener.
*/
- private ModelClass getListenerParameter() {
- ModelClass viewType = mTarget.getResolvedType();
+ private static ModelClass getListenerParameter(BindingTarget target, String name, Expr expr) {
+ ModelClass viewType = target.getResolvedType();
SetterCall setterCall;
ModelAnalyzer modelAnalyzer = ModelAnalyzer.getInstance();
ModelClass objectParameter = modelAnalyzer.findClass(Object.class);
+ SetterStore setterStore = SetterStore.get(modelAnalyzer);
if (viewType != null && viewType.extendsViewStub()) {
- if (isListenerAttribute()) {
+ if (isListenerAttribute(name)) {
ModelClass viewStubProxy = modelAnalyzer.
findClass("android.databinding.ViewStubProxy", null);
- setterCall = SetterStore.get(modelAnalyzer).getSetterCall(mName,
- viewStubProxy, objectParameter, mExpr.getModel().getImports());
- } else if (isViewStubAttribute()) {
- setterCall = SetterStore.get(ModelAnalyzer.getInstance()).getSetterCall(mName,
- viewType, objectParameter, mExpr.getModel().getImports());
+ setterCall = SetterStore.get(modelAnalyzer).getSetterCall(name,
+ viewStubProxy, objectParameter, expr.getModel().getImports());
+ } else if (isViewStubAttribute(name)) {
+ setterCall = new ViewStubDirectCall(name, viewType, expr);
} else {
- setterCall = new ViewStubSetterCall(mName);
+ setterCall = new ViewStubSetterCall(name);
}
} else {
- setterCall = SetterStore.get(ModelAnalyzer.getInstance()).getSetterCall(mName,
- viewType, objectParameter, mExpr.getModel().getImports());
+ setterCall = setterStore.getSetterCall(name, viewType, objectParameter,
+ expr.getModel().getImports());
}
- if (setterCall == null) {
+ if (setterCall != null) {
+ return setterCall.getParameterTypes()[0];
+ }
+ List<SetterStore.MultiAttributeSetter> setters =
+ setterStore.getMultiAttributeSetterCalls(new String[]{name}, viewType,
+ new ModelClass[] {modelAnalyzer.findClass(Object.class)});
+ if (setters.isEmpty()) {
return null;
+ } else {
+ return setters.get(0).getParameterTypes()[0];
}
- return setterCall.getParameterTypes()[0];
}
public BindingTarget getTarget() {
@@ -129,8 +151,8 @@
public String toJavaCode(String targetViewName, String bindingComponent) {
final String currentValue = requiresOldValue()
- ? "this." + WriterPackage.getOldValueName(mExpr) : null;
- final String argCode = CodeGenUtil.Companion.toCode(getExpr(), false).generate();
+ ? "this." + LayoutBinderWriterKt.getOldValueName(mExpr) : null;
+ final String argCode = getExpr().toCode().generate();
return getSetterCall().toJava(bindingComponent, targetViewName, currentValue, argCode);
}
@@ -138,10 +160,6 @@
return getSetterCall().getBindingAdapterInstanceClass();
}
- public void setBindingAdapterCall(String method) {
- getSetterCall().setBindingAdapterCall(method);
- }
-
public Expr[] getComponentExpressions() {
return new Expr[] { mExpr };
}
@@ -168,17 +186,16 @@
return mExpr;
}
- private boolean isViewStubAttribute() {
- return ("android:inflatedId".equals(mName) ||
- "android:layout".equals(mName) ||
- "android:visibility".equals(mName) ||
- "android:layoutInflater".equals(mName));
+ private static boolean isViewStubAttribute(String name) {
+ return ("android:inflatedId".equals(name) ||
+ "android:layout".equals(name) ||
+ "android:visibility".equals(name) ||
+ "android:layoutInflater".equals(name));
}
- private boolean isListenerAttribute() {
- return ("android:onInflate".equals(mName) ||
- "android:onInflateListener".equals(mName));
-
+ private static boolean isListenerAttribute(String name) {
+ return ("android:onInflate".equals(name) ||
+ "android:onInflateListener".equals(name));
}
private static class ViewStubSetterCall extends SetterCall {
@@ -222,10 +239,6 @@
public String getBindingAdapterInstanceClass() {
return null;
}
-
- @Override
- public void setBindingAdapterCall(String method) {
- }
}
private static class ViewStubDirectCall extends SetterCall {
@@ -275,10 +288,5 @@
public String getBindingAdapterInstanceClass() {
return mWrappedCall.getBindingAdapterInstanceClass();
}
-
- @Override
- public void setBindingAdapterCall(String method) {
- mWrappedCall.setBindingAdapterCall(method);
- }
}
}
diff --git a/compiler/src/main/java/android/databinding/tool/BindingTarget.java b/compiler/src/main/java/android/databinding/tool/BindingTarget.java
index 5c3fac5..d8db559 100644
--- a/compiler/src/main/java/android/databinding/tool/BindingTarget.java
+++ b/compiler/src/main/java/android/databinding/tool/BindingTarget.java
@@ -18,31 +18,32 @@
import android.databinding.tool.expr.Expr;
import android.databinding.tool.expr.ExprModel;
+import android.databinding.tool.processing.ErrorMessages;
import android.databinding.tool.processing.Scope;
-import android.databinding.tool.processing.scopes.FileScopeProvider;
import android.databinding.tool.processing.scopes.LocationScopeProvider;
import android.databinding.tool.reflection.ModelAnalyzer;
import android.databinding.tool.reflection.ModelClass;
import android.databinding.tool.store.Location;
import android.databinding.tool.store.ResourceBundle;
import android.databinding.tool.store.SetterStore;
+import android.databinding.tool.store.SetterStore.BindingGetterCall;
import android.databinding.tool.util.L;
import android.databinding.tool.util.Preconditions;
import java.util.ArrayList;
-import java.util.Arrays;
import java.util.HashMap;
import java.util.List;
import java.util.Map;
public class BindingTarget implements LocationScopeProvider {
List<Binding> mBindings = new ArrayList<Binding>();
+ List<InverseBinding> mInverseBindings = new ArrayList<InverseBinding>();
ExprModel mModel;
ModelClass mResolvedClass;
// if this target presents itself in multiple layout files with different view types,
// it receives an interface type and should use it in the getter instead.
- private ResourceBundle.BindingTargetBundle mBundle;
+ ResourceBundle.BindingTargetBundle mBundle;
public BindingTarget(ResourceBundle.BindingTargetBundle bundle) {
mBundle = bundle;
@@ -53,13 +54,38 @@
}
public void addBinding(String name, Expr expr) {
+ if (SetterStore.get(ModelAnalyzer.getInstance()).isTwoWayEventAttribute(name)) {
+ L.e(ErrorMessages.TWO_WAY_EVENT_ATTRIBUTE, name);
+ }
mBindings.add(new Binding(this, name, expr));
+ if (expr.isTwoWay()) {
+ try {
+ Scope.enter(expr);
+ expr.assertIsInvertible();
+ final InverseBinding inverseBinding = new InverseBinding(this, name, expr);
+ mInverseBindings.add(inverseBinding);
+ mBindings.add(new Binding(this, inverseBinding.getEventAttribute(),
+ mModel.twoWayListenerExpr(inverseBinding),
+ inverseBinding.getEventSetter()));
+ } finally {
+ Scope.exit();
+ }
+ }
}
public String getInterfaceType() {
return mBundle.getInterfaceType() == null ? mBundle.getFullClassName() : mBundle.getInterfaceType();
}
+ public InverseBinding addInverseBinding(String name, BindingGetterCall call) {
+ final InverseBinding inverseBinding = new InverseBinding(this, name, null);
+ inverseBinding.setGetterCall(call);
+ mInverseBindings.add(inverseBinding);
+ mBindings.add(new Binding(this, inverseBinding.getEventAttribute(),
+ mModel.twoWayListenerExpr(inverseBinding)));
+ return inverseBinding;
+ }
+
@Override
public List<Location> provideScopeLocation() {
return mBundle.provideScopeLocation();
@@ -83,8 +109,13 @@
public ModelClass getResolvedType() {
if (mResolvedClass == null) {
- mResolvedClass = ModelAnalyzer.getInstance().findClass(mBundle.getFullClassName(),
- mModel.getImports());
+ if (mBundle.isBinder()) {
+ mResolvedClass = ModelAnalyzer.getInstance().
+ findClass(ModelAnalyzer.VIEW_DATA_BINDING, mModel.getImports());
+ } else {
+ mResolvedClass = ModelAnalyzer.getInstance().findClass(mBundle.getFullClassName(),
+ mModel.getImports());
+ }
}
return mResolvedClass;
}
@@ -106,6 +137,10 @@
return mBindings;
}
+ public List<InverseBinding> getInverseBindings() {
+ return mInverseBindings;
+ }
+
public ExprModel getModel() {
return mModel;
}
@@ -114,6 +149,18 @@
mModel = model;
}
+ public void resolveListeners() {
+ for (Binding binding : mBindings) {
+ binding.resolveListeners();
+ }
+ }
+
+ public void resolveTwoWayExpressions() {
+ for (Binding binding : mBindings) {
+ binding.resolveTwoWayExpressions();
+ }
+ }
+
/**
* Called after BindingTarget is finalized.
* <p>
@@ -158,7 +205,7 @@
List<MergedBinding> mergeBindings = new ArrayList<MergedBinding>();
for (final SetterStore.MultiAttributeSetter setter : multiAttributeSetterCalls) {
L.d("resolved %s", setter);
- final List<Binding> mergedBindings = new ArrayList<>();
+ final List<Binding> mergedBindings = new ArrayList<Binding>();
for (String attribute : setter.attributes) {
Binding binding = lookup.get(attribute);
Preconditions.checkNotNull(binding, "cannot find binding for %s", attribute);
diff --git a/compiler/src/main/java/android/databinding/tool/CompilerChef.java b/compiler/src/main/java/android/databinding/tool/CompilerChef.java
index df36560..b7456da 100644
--- a/compiler/src/main/java/android/databinding/tool/CompilerChef.java
+++ b/compiler/src/main/java/android/databinding/tool/CompilerChef.java
@@ -13,10 +13,14 @@
package android.databinding.tool;
+import android.databinding.tool.reflection.ModelAnalyzer;
+import android.databinding.tool.reflection.ModelClass;
import android.databinding.tool.store.ResourceBundle;
import android.databinding.tool.util.L;
+import android.databinding.tool.util.Preconditions;
import android.databinding.tool.writer.BRWriter;
import android.databinding.tool.writer.DataBinderWriter;
+import android.databinding.tool.writer.DynamicUtilWriter;
import android.databinding.tool.writer.JavaFileWriter;
import java.util.Set;
@@ -27,6 +31,31 @@
* Different build systems can initiate a version of this to handle their work
*/
public class CompilerChef {
+ private static final String[] VERSION_CODES = {
+ "BASE", // 1
+ "BASE_1_1", // 2
+ "CUPCAKE", // 3
+ "DONUT", // 4
+ "ECLAIR", // 5
+ "ECLAIRE_0_1", // 6
+ "ECLAIR_MR1", // 7
+ "FROYO", // 8
+ "GINGERBREAD", // 9
+ "GINGERBREAD_MR1", // 10
+ "HONEYCOMB", // 11
+ "HONEYCOMB_MR1", // 12
+ "HONEYCOMB_MR2", // 13
+ "ICE_CREAM_SANDWICH", // 14
+ "ICE_CREAM_SANDWICH_MR1",// 15
+ "JELLY_BEAN", // 16
+ "JELLY_BEAN_MR1", // 17
+ "JELLY_BEAN_MR2", // 18
+ "KITKAT", // 19
+ "KITKAT_WATCH", // 20
+ "LOLLIPOP", // 21
+ "LOLLIPOP_MR1", // 22
+ "M", // 23
+ };
private JavaFileWriter mFileWriter;
private ResourceBundle mResourceBundle;
private DataBinder mDataBinder;
@@ -68,6 +97,23 @@
mFileWriter.writeToFile(pkg + "." + dbr.getClassName(), dbr.write(brWriter));
}
+ public void writeDynamicUtil() {
+ DynamicUtilWriter dynamicUtil = new DynamicUtilWriter();
+ // TODO: Replace this with targetSDK check from plugin
+ ModelClass versionCodes = ModelAnalyzer.getInstance().findClass(
+ "android.os.Build.VERSION_CODES", null);
+ Preconditions.checkNotNull(versionCodes, "Could not find compile SDK");
+ int compileVersion = 1;
+ for (int i = VERSION_CODES.length - 1; i >= 0; i--) {
+ if (versionCodes.findGetterOrField(VERSION_CODES[i], true) != null) {
+ compileVersion = i + 1;
+ break;
+ }
+ }
+ mFileWriter.writeToFile("android.databinding.DynamicUtil",
+ dynamicUtil.write(compileVersion).generate());
+ }
+
/**
* Adds variables to list of Bindables.
*/
diff --git a/compiler/src/main/java/android/databinding/tool/DataBinder.java b/compiler/src/main/java/android/databinding/tool/DataBinder.java
index 1acb16b..0280bd4 100644
--- a/compiler/src/main/java/android/databinding/tool/DataBinder.java
+++ b/compiler/src/main/java/android/databinding/tool/DataBinder.java
@@ -104,7 +104,7 @@
}
public void writeComponent() {
- ComponentWriter componentWriter = new ComponentWriter(mLayoutBinders);
+ ComponentWriter componentWriter = new ComponentWriter();
writtenClasses.add(COMPONENT_CLASS);
mFileWriter.writeToFile(COMPONENT_CLASS, componentWriter.createComponent());
diff --git a/compiler/src/main/java/android/databinding/tool/ExpressionParser.java b/compiler/src/main/java/android/databinding/tool/ExpressionParser.java
index 467f658..5468c93 100644
--- a/compiler/src/main/java/android/databinding/tool/ExpressionParser.java
+++ b/compiler/src/main/java/android/databinding/tool/ExpressionParser.java
@@ -17,8 +17,12 @@
package android.databinding.tool;
import org.antlr.v4.runtime.ANTLRInputStream;
+import org.antlr.v4.runtime.BaseErrorListener;
import org.antlr.v4.runtime.CommonTokenStream;
import org.antlr.v4.runtime.ParserRuleContext;
+import org.antlr.v4.runtime.RecognitionException;
+import org.antlr.v4.runtime.Recognizer;
+import org.antlr.v4.runtime.Token;
import org.antlr.v4.runtime.misc.Nullable;
import org.antlr.v4.runtime.tree.ErrorNode;
import org.antlr.v4.runtime.tree.ParseTreeListener;
@@ -28,6 +32,7 @@
import android.databinding.parser.BindingExpressionParser;
import android.databinding.tool.expr.Expr;
import android.databinding.tool.expr.ExprModel;
+import android.databinding.tool.processing.ErrorMessages;
import android.databinding.tool.store.Location;
import android.databinding.tool.util.L;
import android.databinding.tool.util.Preconditions;
@@ -49,6 +54,14 @@
BindingExpressionLexer lexer = new BindingExpressionLexer(inputStream);
CommonTokenStream tokenStream = new CommonTokenStream(lexer);
final BindingExpressionParser parser = new BindingExpressionParser(tokenStream);
+ parser.addErrorListener(new BaseErrorListener() {
+ @Override
+ public <T extends Token> void syntaxError(Recognizer<T, ?> recognizer,
+ @Nullable T offendingSymbol, int line, int charPositionInLine, String msg,
+ @Nullable RecognitionException e) {
+ L.e(ErrorMessages.SYNTAX_ERROR, msg);
+ }
+ });
BindingExpressionParser.BindingSyntaxContext root = parser.bindingSyntax();
try {
mModel.setCurrentLocationInFile(locationInFile);
diff --git a/compiler/src/main/java/android/databinding/tool/ExpressionVisitor.java b/compiler/src/main/java/android/databinding/tool/ExpressionVisitor.java
index 7507890..08e6744 100644
--- a/compiler/src/main/java/android/databinding/tool/ExpressionVisitor.java
+++ b/compiler/src/main/java/android/databinding/tool/ExpressionVisitor.java
@@ -16,12 +16,13 @@
package android.databinding.tool;
+import com.google.common.base.Objects;
+
import org.antlr.v4.runtime.ParserRuleContext;
import org.antlr.v4.runtime.misc.NotNull;
import org.antlr.v4.runtime.tree.ParseTree;
import org.antlr.v4.runtime.tree.ParseTreeListener;
import org.antlr.v4.runtime.tree.TerminalNode;
-import org.apache.commons.lang3.ObjectUtils;
import android.databinding.parser.BindingExpressionBaseVisitor;
import android.databinding.parser.BindingExpressionParser;
@@ -218,7 +219,7 @@
List<Expr> args = new ArrayList<Expr>();
if (ctx.args != null) {
for (ParseTree item : ctx.args.children) {
- if (ObjectUtils.equals(item.getText(), ",")) {
+ if (Objects.equal(item.getText(), ",")) {
continue;
}
args.add(item.accept(this));
@@ -298,7 +299,7 @@
final List<Expr> args = new ArrayList<Expr>();
if (ctx.resourceParameters() != null) {
for (ParseTree item : ctx.resourceParameters().expressionList().children) {
- if (ObjectUtils.equals(item.getText(), ",")) {
+ if (Objects.equal(item.getText(), ",")) {
continue;
}
args.add(item.accept(this));
diff --git a/compiler/src/main/java/android/databinding/tool/InverseBinding.java b/compiler/src/main/java/android/databinding/tool/InverseBinding.java
new file mode 100644
index 0000000..e04be28
--- /dev/null
+++ b/compiler/src/main/java/android/databinding/tool/InverseBinding.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.tool;
+
+import android.databinding.tool.expr.Expr;
+import android.databinding.tool.expr.ExprModel;
+import android.databinding.tool.expr.FieldAccessExpr;
+import android.databinding.tool.processing.ErrorMessages;
+import android.databinding.tool.processing.Scope;
+import android.databinding.tool.processing.scopes.LocationScopeProvider;
+import android.databinding.tool.reflection.ModelAnalyzer;
+import android.databinding.tool.reflection.ModelClass;
+import android.databinding.tool.store.Location;
+import android.databinding.tool.store.SetterStore;
+import android.databinding.tool.store.SetterStore.BindingGetterCall;
+import android.databinding.tool.store.SetterStore.BindingSetterCall;
+import android.databinding.tool.util.L;
+import android.databinding.tool.util.Preconditions;
+import android.databinding.tool.writer.FlagSet;
+import android.databinding.tool.writer.KCode;
+import android.databinding.tool.writer.LayoutBinderWriterKt;
+
+import kotlin.jvm.functions.Function2;
+
+import java.util.ArrayList;
+import java.util.List;
+
+public class InverseBinding implements LocationScopeProvider {
+
+ private final String mName;
+ private final Expr mExpr;
+ private final BindingTarget mTarget;
+ private BindingGetterCall mGetterCall;
+ private final ArrayList<FieldAccessExpr> mChainedExpressions = new ArrayList<FieldAccessExpr>();
+
+ public InverseBinding(BindingTarget target, String name, Expr expr) {
+ mTarget = target;
+ mName = name;
+ mExpr = expr;
+ }
+
+ @Override
+ public List<Location> provideScopeLocation() {
+ if (mExpr != null) {
+ return mExpr.getLocations();
+ } else {
+ return mChainedExpressions.get(0).getLocations();
+ }
+ }
+
+ void setGetterCall(BindingGetterCall getterCall) {
+ mGetterCall = getterCall;
+ }
+
+ public void addChainedExpression(FieldAccessExpr expr) {
+ mChainedExpressions.add(expr);
+ }
+
+ public boolean isOnBinder() {
+ return mTarget.getResolvedType().isViewDataBinding();
+ }
+
+ private SetterStore.BindingGetterCall getGetterCall() {
+ if (mGetterCall == null) {
+ if (mExpr != null) {
+ mExpr.getResolvedType(); // force resolve of ObservableFields
+ }
+ try {
+ Scope.enter(mTarget);
+ Scope.enter(this);
+ resolveGetterCall();
+ if (mGetterCall == null) {
+ L.e(ErrorMessages.CANNOT_FIND_GETTER_CALL, mName,
+ mExpr == null ? "Unknown" : mExpr.getResolvedType(),
+ mTarget.getResolvedType());
+ }
+ } finally {
+ Scope.exit();
+ Scope.exit();
+ }
+ }
+ return mGetterCall;
+ }
+
+ private void resolveGetterCall() {
+ ModelClass viewType = mTarget.getResolvedType();
+ final SetterStore setterStore = SetterStore.get(ModelAnalyzer.getInstance());
+ final ModelClass resolvedType = mExpr == null ? null : mExpr.getResolvedType();
+ mGetterCall = setterStore.getGetterCall(mName, viewType, resolvedType,
+ getModel().getImports());
+ }
+
+ public BindingTarget getTarget() {
+ return mTarget;
+ }
+
+ public KCode toJavaCode(String bindingComponent, final FlagSet flagField) {
+ final String targetViewName = LayoutBinderWriterKt.getFieldName(getTarget());
+ KCode code = new KCode();
+ // A chained expression will have substituted its chained value for the expression
+ // unless the attribute has no expression. Therefore, chaining and expressions are
+ // mutually exclusive.
+ Preconditions.check((mExpr == null) != mChainedExpressions.isEmpty(),
+ "Chained expressions are only against unbound attributes.");
+ if (mExpr != null) {
+ code.app("", mExpr.toInverseCode(new KCode(getGetterCall().toJava(bindingComponent,
+ targetViewName))));
+ } else { // !mChainedExpressions.isEmpty())
+ final String fieldName = flagField.getLocalName();
+ FlagSet flagSet = new FlagSet();
+ for (FieldAccessExpr expr : mChainedExpressions) {
+ flagSet = flagSet.or(new FlagSet(expr.getId()));
+ }
+ final FlagSet allFlags = flagSet;
+ code.nl(new KCode("synchronized(this) {"));
+ code.tab(LayoutBinderWriterKt
+ .mapOr(flagField, flagSet, new Function2<String, Integer, KCode>() {
+ @Override
+ public KCode invoke(String suffix, Integer index) {
+ return new KCode(fieldName)
+ .app(suffix)
+ .app(" |= ")
+ .app(LayoutBinderWriterKt.binaryCode(allFlags, index))
+ .app(";");
+ }
+ }));
+ code.nl(new KCode("}"));
+ code.nl(new KCode("requestRebind()"));
+ }
+ return code;
+ }
+
+ public String getBindingAdapterInstanceClass() {
+ return getGetterCall().getBindingAdapterInstanceClass();
+ }
+
+ /**
+ * The min api level in which this binding should be executed.
+ * <p>
+ * This should be the minimum value among the dependencies of this binding.
+ */
+ public int getMinApi() {
+ final BindingGetterCall getterCall = getGetterCall();
+ return Math.max(getterCall.getMinApi(), getterCall.getEvent().getMinApi());
+ }
+
+ public BindingSetterCall getEventSetter() {
+ final BindingGetterCall getterCall = getGetterCall();
+ return getterCall.getEvent();
+ }
+
+ public String getName() {
+ return mName;
+ }
+
+ public String getEventAttribute() {
+ return getGetterCall().getEventAttribute();
+ }
+
+ public ExprModel getModel() {
+ if (mExpr != null) {
+ return mExpr.getModel();
+ }
+ return mChainedExpressions.get(0).getModel();
+ }
+}
diff --git a/compiler/src/main/java/android/databinding/tool/LayoutBinder.java b/compiler/src/main/java/android/databinding/tool/LayoutBinder.java
index be523bd..7ed944a 100644
--- a/compiler/src/main/java/android/databinding/tool/LayoutBinder.java
+++ b/compiler/src/main/java/android/databinding/tool/LayoutBinder.java
@@ -16,37 +16,38 @@
package android.databinding.tool;
-import org.antlr.v4.runtime.misc.Nullable;
-
import android.databinding.tool.expr.Dependency;
import android.databinding.tool.expr.Expr;
import android.databinding.tool.expr.ExprModel;
-import android.databinding.tool.expr.ExprModel.ResolveListenersCallback;
import android.databinding.tool.expr.IdentifierExpr;
import android.databinding.tool.processing.Scope;
import android.databinding.tool.processing.scopes.FileScopeProvider;
import android.databinding.tool.store.Location;
import android.databinding.tool.store.ResourceBundle;
import android.databinding.tool.store.ResourceBundle.BindingTargetBundle;
+import android.databinding.tool.util.L;
import android.databinding.tool.util.Preconditions;
import android.databinding.tool.writer.LayoutBinderWriter;
-import android.databinding.tool.writer.WriterPackage;
+import android.databinding.tool.writer.LayoutBinderWriterKt;
+
+import org.antlr.v4.runtime.misc.Nullable;
import java.util.ArrayList;
import java.util.Collections;
import java.util.Comparator;
import java.util.HashMap;
+import java.util.HashSet;
import java.util.List;
/**
* Keeps all information about the bindings per layout file
*/
-public class LayoutBinder implements ResolveListenersCallback, FileScopeProvider {
+public class LayoutBinder implements FileScopeProvider {
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);
+ final String fieldName1 = LayoutBinderWriterKt.getFieldName(first);
+ final String fieldName2 = LayoutBinderWriterKt.getFieldName(second);
return fieldName1.compareTo(fieldName2);
}
};
@@ -172,27 +173,63 @@
mBindingTargets = new ArrayList<BindingTarget>();
mBundle = layoutBundle;
mModulePackage = layoutBundle.getModulePackage();
+ HashSet<String> names = new HashSet<String>();
// copy over data.
- for (ResourceBundle.NameTypeLocation variable : mBundle.getVariables()) {
- addVariable(variable.name, variable.type, variable.location);
+ for (ResourceBundle.VariableDeclaration variable : mBundle.getVariables()) {
+ addVariable(variable.name, variable.type, variable.location, variable.declared);
+ names.add(variable.name);
}
for (ResourceBundle.NameTypeLocation userImport : mBundle.getImports()) {
mExprModel.addImport(userImport.name, userImport.type, userImport.location);
+ names.add(userImport.name);
+ }
+ if (!names.contains("context")) {
+ mExprModel.builtInVariable("context", "android.content.Context",
+ "getRoot().getContext()");
+ names.add("context");
}
for (String javaLangClass : sJavaLangClasses) {
mExprModel.addImport(javaLangClass, "java.lang." + javaLangClass, null);
}
+ // First resolve all the View fields
+ // Ensure there are no conflicts with variable names
for (BindingTargetBundle targetBundle : mBundle.getBindingTargetBundles()) {
try {
Scope.enter(targetBundle);
final BindingTarget bindingTarget = createBindingTarget(targetBundle);
- for (BindingTargetBundle.BindingBundle bindingBundle : targetBundle
- .getBindingBundleList()) {
- bindingTarget.addBinding(bindingBundle.getName(),
- parse(bindingBundle.getExpr(), bindingBundle.getValueLocation()));
+ if (bindingTarget.getId() != null) {
+ final String fieldName = LayoutBinderWriterKt.
+ getReadableName(bindingTarget);
+ if (names.contains(fieldName)) {
+ L.w("View field %s collides with a variable or import", fieldName);
+ } else {
+ names.add(fieldName);
+ mExprModel.viewFieldExpr(bindingTarget);
+ }
}
+ } finally {
+ Scope.exit();
+ }
+ }
+
+ for (BindingTarget bindingTarget : mBindingTargets) {
+ try {
+ Scope.enter(bindingTarget.mBundle);
+ for (BindingTargetBundle.BindingBundle bindingBundle : bindingTarget.mBundle
+ .getBindingBundleList()) {
+ try {
+ Scope.enter(bindingBundle.getValueLocation());
+ bindingTarget.addBinding(bindingBundle.getName(),
+ parse(bindingBundle.getExpr(), bindingBundle.isTwoWay(),
+ bindingBundle.getValueLocation()));
+ } finally {
+ Scope.exit();
+ }
+ }
+ bindingTarget.resolveTwoWayExpressions();
bindingTarget.resolveMultiSetters();
+ bindingTarget.resolveListeners();
} finally {
Scope.exit();
}
@@ -223,7 +260,8 @@
}
}
- public IdentifierExpr addVariable(String name, String type, Location location) {
+ public IdentifierExpr addVariable(String name, String type, Location location,
+ boolean declared) {
Preconditions.check(!mUserDefinedVariables.containsKey(name),
"%s has already been defined as %s", name, type);
final IdentifierExpr id = mExprModel.identifier(name);
@@ -233,6 +271,9 @@
id.addLocation(location);
}
mUserDefinedVariables.put(name, type);
+ if (declared) {
+ id.setDeclared();
+ }
return id;
}
@@ -247,9 +288,10 @@
return target;
}
- public Expr parse(String input, @Nullable Location locationInFile) {
+ public Expr parse(String input, boolean isTwoWay, @Nullable Location locationInFile) {
final Expr parsed = mExpressionParser.parse(input, locationInFile);
parsed.setBindingExpression(true);
+ parsed.setTwoWay(isTwoWay);
return parsed;
}
@@ -276,7 +318,7 @@
}
public void sealModel() {
- mExprModel.seal(this);
+ mExprModel.seal();
}
public String writeViewBinderBaseClass(boolean forLibrary) {
@@ -328,15 +370,6 @@
}
@Override
- public void resolveListeners() {
- for (BindingTarget target : mBindingTargets) {
- for (Binding binding : target.getBindings()) {
- binding.resolveListeners();
- }
- }
- }
-
- @Override
public String provideScopeFilePath() {
return mBundle.getAbsoluteFilePath();
}
diff --git a/compiler/src/main/java/android/databinding/tool/MakeCopy.java b/compiler/src/main/java/android/databinding/tool/MakeCopy.java
index f143e21..7627e06 100644
--- a/compiler/src/main/java/android/databinding/tool/MakeCopy.java
+++ b/compiler/src/main/java/android/databinding/tool/MakeCopy.java
@@ -28,6 +28,7 @@
import java.io.FilenameFilter;
import java.io.IOException;
import java.util.ArrayList;
+import java.util.Collections;
import java.util.HashMap;
import java.util.List;
import java.util.Map;
@@ -166,13 +167,19 @@
private static void processLayoutFiles(String applicationPackage, File resTarget, File srcDir,
File xmlDir, 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);
+ makeFileWriter, minSdk, isLibrary, new LayoutXmlProcessor.OriginalFileLookup() {
+ @Override
+ public File getOriginalFileFor(File file) {
+ return file;
+ }
+ });
try {
- xmlProcessor.processResources(minSdk);
+ LayoutXmlProcessor.ResourceInput input = new LayoutXmlProcessor.ResourceInput(
+ false, resTarget,resTarget
+ );
+ xmlProcessor.processResources(input);
xmlProcessor.writeLayoutInfoFiles(xmlDir);
// TODO Looks like make does not support excluding from libs ?
xmlProcessor.writeInfoClass(null, xmlDir, null);
diff --git a/compiler/src/main/java/android/databinding/tool/MergedBinding.java b/compiler/src/main/java/android/databinding/tool/MergedBinding.java
index e420b95..0f64695 100644
--- a/compiler/src/main/java/android/databinding/tool/MergedBinding.java
+++ b/compiler/src/main/java/android/databinding/tool/MergedBinding.java
@@ -22,8 +22,7 @@
import android.databinding.tool.reflection.ModelClass;
import android.databinding.tool.store.SetterStore;
import android.databinding.tool.util.L;
-import android.databinding.tool.writer.CodeGenUtil;
-import android.databinding.tool.writer.WriterPackage;
+import android.databinding.tool.writer.LayoutBinderWriterKt;
import java.lang.reflect.Array;
import java.util.ArrayList;
@@ -42,8 +41,19 @@
mMultiAttributeSetter = multiAttributeSetter;
}
+ @Override
+ public void resolveListeners() {
+ ModelClass[] parameters = mMultiAttributeSetter.getParameterTypes();
+ List<Expr> children = getExpr().getChildren();
+ final Expr expr = getExpr();
+ for (int i = 0; i < children.size(); i++) {
+ final Expr child = children.get(i);
+ child.resolveListeners(parameters[i], expr);
+ }
+ }
+
private static Expr createArgListExpr(ExprModel model, final Iterable<Binding> bindings) {
- List<Expr> args = new ArrayList<>();
+ List<Expr> args = new ArrayList<Expr>();
for (Binding binding : bindings) {
args.add(binding.getExpr());
}
@@ -60,28 +70,18 @@
return sb.toString();
}
- @Override
- public void resolveListeners() {
- ModelClass[] params = mMultiAttributeSetter.getParameterTypes();
- List<Expr> expressions = getExpr().getChildren();
- for (int i = 0; i < params.length; i++) {
- expressions.get(i).resolveListeners(params[i]);
- }
- }
-
public Expr[] getComponentExpressions() {
ArgListExpr args = (ArgListExpr) getExpr();
return args.getChildren().toArray(new Expr[args.getChildren().size()]);
}
- @Override
- public String getBindingAdapterInstanceClass() {
- return mMultiAttributeSetter.getBindingAdapterInstanceClass();
+ public String[] getAttributes() {
+ return mMultiAttributeSetter.attributes;
}
@Override
- public void setBindingAdapterCall(String method) {
- mMultiAttributeSetter.setBindingAdapterCall(method);
+ public String getBindingAdapterInstanceClass() {
+ return mMultiAttributeSetter.getBindingAdapterInstanceClass();
}
@Override
@@ -97,15 +97,15 @@
@Override
public String toJavaCode(String targetViewName, String bindingComponent) {
final ArgListExpr args = (ArgListExpr) getExpr();
- final List<String> newValues = new ArrayList<>();
+ final List<String> newValues = new ArrayList<String>();
for (Expr expr : args.getChildren()) {
- newValues.add(CodeGenUtil.Companion.toCode(expr, false).generate());
+ newValues.add(expr.toCode().generate());
}
final List<String> oldValues;
if (requiresOldValue()) {
- oldValues = new ArrayList<>();
+ oldValues = new ArrayList<String>();
for (Expr expr : args.getChildren()) {
- oldValues.add("this." + WriterPackage.getOldValueName(expr));
+ oldValues.add("this." + LayoutBinderWriterKt.getOldValueName(expr));
}
} else {
oldValues = Arrays.asList(new String[args.getChildren().size()]);
@@ -116,7 +116,7 @@
}
private static <T> T[] concat(List<T> l1, List<T> l2, Class<T> klass) {
- List<T> result = new ArrayList<>();
+ List<T> result = new ArrayList<T>();
result.addAll(l1);
result.addAll(l2);
return result.toArray((T[]) Array.newInstance(klass, result.size()));
diff --git a/compiler/src/main/java/android/databinding/tool/expr/ArgListExpr.java b/compiler/src/main/java/android/databinding/tool/expr/ArgListExpr.java
index 91ae649..c8f6e2c 100644
--- a/compiler/src/main/java/android/databinding/tool/expr/ArgListExpr.java
+++ b/compiler/src/main/java/android/databinding/tool/expr/ArgListExpr.java
@@ -18,6 +18,7 @@
import android.databinding.tool.reflection.ModelAnalyzer;
import android.databinding.tool.reflection.ModelClass;
+import android.databinding.tool.writer.KCode;
import java.util.List;
@@ -41,6 +42,12 @@
}
@Override
+ protected KCode generateCode(boolean expand) {
+ throw new IllegalStateException("should never try to convert an argument expressions"
+ + " into code");
+ }
+
+ @Override
protected ModelClass resolveType(ModelAnalyzer modelAnalyzer) {
return modelAnalyzer.findClass(Void.class);
}
@@ -54,4 +61,9 @@
public boolean canBeEvaluatedToAVariable() {
return false;
}
+
+ @Override
+ public String getInvertibleError() {
+ return "Merged bindings are not invertible.";
+ }
}
diff --git a/compiler/src/main/java/android/databinding/tool/expr/BitShiftExpr.java b/compiler/src/main/java/android/databinding/tool/expr/BitShiftExpr.java
index e5db9df..cbc895b 100644
--- a/compiler/src/main/java/android/databinding/tool/expr/BitShiftExpr.java
+++ b/compiler/src/main/java/android/databinding/tool/expr/BitShiftExpr.java
@@ -18,6 +18,7 @@
import android.databinding.tool.reflection.ModelAnalyzer;
import android.databinding.tool.reflection.ModelClass;
+import android.databinding.tool.writer.KCode;
import java.util.List;
@@ -54,4 +55,17 @@
public Expr getRight() {
return getChildren().get(1);
}
+
+ @Override
+ protected KCode generateCode(boolean expand) {
+ return new KCode()
+ .app("", getLeft().toCode(expand))
+ .app(getOp())
+ .app("", getRight().toCode(expand));
+ }
+
+ @Override
+ public String getInvertibleError() {
+ return "Bit shift operators cannot be inverted in two-way binding";
+ }
}
diff --git a/compiler/src/main/java/android/databinding/tool/expr/BracketExpr.java b/compiler/src/main/java/android/databinding/tool/expr/BracketExpr.java
index a5ea5a7..392512c 100644
--- a/compiler/src/main/java/android/databinding/tool/expr/BracketExpr.java
+++ b/compiler/src/main/java/android/databinding/tool/expr/BracketExpr.java
@@ -18,12 +18,13 @@
import android.databinding.tool.reflection.ModelAnalyzer;
import android.databinding.tool.reflection.ModelClass;
+import android.databinding.tool.writer.KCode;
import java.util.List;
public class BracketExpr extends Expr {
- public static enum BracketAccessor {
+ public enum BracketAccessor {
ARRAY,
LIST,
MAP,
@@ -54,11 +55,23 @@
@Override
protected List<Dependency> constructDependencies() {
- return constructDynamicChildrenDependencies();
+ final List<Dependency> dependencies = constructDynamicChildrenDependencies();
+ for (Dependency dependency : dependencies) {
+ if (dependency.getOther() == getTarget()) {
+ dependency.setMandatory(true);
+ }
+ }
+ return dependencies;
}
protected String computeUniqueKey() {
- return join(getTarget().computeUniqueKey(), "$", getArg().computeUniqueKey(), "$");
+ final String targetKey = getTarget().computeUniqueKey();
+ return addTwoWay(join(targetKey, "$", getArg().computeUniqueKey(), "$"));
+ }
+
+ @Override
+ public String getInvertibleError() {
+ return null;
}
public Expr getTarget() {
@@ -74,6 +87,53 @@
}
public boolean argCastsInteger() {
- return Object.class.equals(getArg().getResolvedType());
+ return mAccessor != BracketAccessor.MAP && getArg().getResolvedType().isObject();
+ }
+
+ @Override
+ protected KCode generateCode(boolean expand) {
+ String cast = argCastsInteger() ? "(Integer) " : "";
+ switch (getAccessor()) {
+ case ARRAY: {
+ return new KCode().
+ app("getFromArray(", getTarget().toCode()).
+ app(", ").
+ app(cast, getArg().toCode()).app(")");
+ }
+ case LIST: {
+ ModelClass listType = ModelAnalyzer.getInstance().findClass(java.util.List.class).
+ erasure();
+ ModelClass targetType = getTarget().getResolvedType().erasure();
+ if (listType.isAssignableFrom(targetType)) {
+ return new KCode().
+ app("getFromList(", getTarget().toCode()).
+ app(", ").
+ app(cast, getArg().toCode()).
+ app(")");
+ } else {
+ return new KCode().
+ app("", getTarget().toCode()).
+ app(".get(").
+ app(cast, getArg().toCode()).
+ app(")");
+ }
+ }
+ case MAP:
+ return new KCode().
+ app("", getTarget().toCode()).
+ app(".get(", getArg().toCode()).
+ app(")");
+ }
+ throw new IllegalStateException("Invalid BracketAccessor type");
+ }
+
+ @Override
+ public KCode toInverseCode(KCode value) {
+ String cast = argCastsInteger() ? "(Integer) " : "";
+ return new KCode().
+ app("setTo(", getTarget().toCode(true)).
+ app(", ").
+ app(cast, getArg().toCode(true)).
+ app(", ", value).app(");");
}
}
diff --git a/compiler/src/main/java/android/databinding/tool/expr/BuiltInVariableExpr.java b/compiler/src/main/java/android/databinding/tool/expr/BuiltInVariableExpr.java
new file mode 100644
index 0000000..d2fdea1
--- /dev/null
+++ b/compiler/src/main/java/android/databinding/tool/expr/BuiltInVariableExpr.java
@@ -0,0 +1,68 @@
+/*
+ * Copyright (C) 2015 The Android Open Source Project
+ *
+ * Licensed under the Apache License, Version 2.0 (the "License");
+ * you may not use this file except in compliance with the License.
+ * You may obtain a copy of the License at
+ *
+ * http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing, software
+ * distributed under the License is distributed on an "AS IS" BASIS,
+ * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+ * See the License for the specific language governing permissions and
+ * limitations under the License.
+ */
+
+package android.databinding.tool.expr;
+
+import android.databinding.tool.reflection.ModelAnalyzer;
+import android.databinding.tool.reflection.ModelClass;
+import android.databinding.tool.writer.KCode;
+
+import java.util.ArrayList;
+import java.util.List;
+
+public class BuiltInVariableExpr extends IdentifierExpr {
+ private final String mAccessCode;
+
+ BuiltInVariableExpr(String name, String type, String accessCode) {
+ super(name);
+ super.setUserDefinedType(type);
+ this.mAccessCode = accessCode;
+ }
+
+ @Override
+ public boolean isDynamic() {
+ return false;
+ }
+
+ @Override
+ protected ModelClass resolveType(ModelAnalyzer modelAnalyzer) {
+ ModelClass modelClass = super.resolveType(modelAnalyzer);
+ return modelClass;
+ }
+
+ @Override
+ protected List<Dependency> constructDependencies() {
+ return new ArrayList<Dependency>();
+ }
+
+ @Override
+ protected KCode generateCode(boolean expand) {
+ if (mAccessCode == null) {
+ return new KCode().app(mName);
+ } else {
+ return new KCode().app(mAccessCode);
+ }
+ }
+
+ public boolean isDeclared() {
+ return false;
+ }
+
+ @Override
+ public String getInvertibleError() {
+ return "Built-in variables may not be the target of two-way binding";
+ }
+}
diff --git a/compiler/src/main/java/android/databinding/tool/expr/CastExpr.java b/compiler/src/main/java/android/databinding/tool/expr/CastExpr.java
index 552de30..9a4a36a 100644
--- a/compiler/src/main/java/android/databinding/tool/expr/CastExpr.java
+++ b/compiler/src/main/java/android/databinding/tool/expr/CastExpr.java
@@ -18,6 +18,7 @@
import android.databinding.tool.reflection.ModelAnalyzer;
import android.databinding.tool.reflection.ModelClass;
+import android.databinding.tool.writer.KCode;
import java.util.List;
@@ -45,7 +46,7 @@
}
protected String computeUniqueKey() {
- return join(mType, getCastExpr().computeUniqueKey());
+ return addTwoWay(join(mType, getCastExpr().computeUniqueKey()));
}
public Expr getCastExpr() {
@@ -55,4 +56,23 @@
public String getCastType() {
return getResolvedType().toJavaCode();
}
+
+ @Override
+ protected KCode generateCode(boolean expand) {
+ return new KCode()
+ .app("(")
+ .app(getCastType())
+ .app(") ", getCastExpr().toCode(expand));
+ }
+
+ @Override
+ public String getInvertibleError() {
+ return getCastExpr().getInvertibleError();
+ }
+
+ @Override
+ public KCode toInverseCode(KCode value) {
+ // assume no need to cast in reverse
+ return getCastExpr().toInverseCode(value);
+ }
}
diff --git a/compiler/src/main/java/android/databinding/tool/expr/ComparisonExpr.java b/compiler/src/main/java/android/databinding/tool/expr/ComparisonExpr.java
index 42b188a..172ea21 100644
--- a/compiler/src/main/java/android/databinding/tool/expr/ComparisonExpr.java
+++ b/compiler/src/main/java/android/databinding/tool/expr/ComparisonExpr.java
@@ -18,6 +18,7 @@
import android.databinding.tool.reflection.ModelAnalyzer;
import android.databinding.tool.reflection.ModelClass;
+import android.databinding.tool.writer.KCode;
import java.util.List;
@@ -59,4 +60,16 @@
public boolean isEqualityCheck() {
return "==".equals(mOp.trim());
}
+
+ @Override
+ protected KCode generateCode(boolean expand) {
+ return new KCode().app("", getLeft().toCode(expand))
+ .app(" ").app(getOp()).app(" ")
+ .app("", getRight().toCode(expand));
+ }
+
+ @Override
+ public String getInvertibleError() {
+ return "Comparison operators are not valid as targets of two-way binding";
+ }
}
diff --git a/compiler/src/main/java/android/databinding/tool/expr/Expr.java b/compiler/src/main/java/android/databinding/tool/expr/Expr.java
index 1712bb1..193501c 100644
--- a/compiler/src/main/java/android/databinding/tool/expr/Expr.java
+++ b/compiler/src/main/java/android/databinding/tool/expr/Expr.java
@@ -21,17 +21,19 @@
import android.databinding.tool.processing.ErrorMessages;
import android.databinding.tool.processing.Scope;
import android.databinding.tool.processing.scopes.LocationScopeProvider;
-import android.databinding.tool.processing.scopes.ScopeProvider;
import android.databinding.tool.reflection.ModelAnalyzer;
import android.databinding.tool.reflection.ModelClass;
import android.databinding.tool.store.Location;
import android.databinding.tool.util.L;
import android.databinding.tool.util.Preconditions;
+import android.databinding.tool.writer.KCode;
+import android.databinding.tool.writer.LayoutBinderWriterKt;
import java.util.ArrayList;
import java.util.BitSet;
import java.util.Collections;
import java.util.List;
+import java.util.Map;
abstract public class Expr implements VersionProvider, LocationScopeProvider {
@@ -61,7 +63,7 @@
private boolean mCanBeInvalidated = false;
@Nullable
- private List<Location> mLocations = new ArrayList<>();
+ private List<Location> mLocations = new ArrayList<Location>();
/**
* This set denotes the times when this expression is invalid.
@@ -96,6 +98,7 @@
*/
private boolean mRead;
private boolean mIsUsed = false;
+ private boolean mIsTwoWay = false;
Expr(Iterable<Expr> children) {
for (Expr expr : children) {
@@ -167,17 +170,21 @@
return getResolvedType().isObservable();
}
- public boolean resolveListeners(ModelClass valueType) {
- boolean resetResolvedType = false;
- for (Expr child : getChildren()) {
- if (child.resolveListeners(valueType)) {
- resetResolvedType = true;
- }
+ public Expr resolveListeners(ModelClass valueType, Expr parent) {
+ for (int i = mChildren.size() - 1; i >= 0; i--) {
+ Expr child = mChildren.get(i);
+ child.resolveListeners(valueType, this);
}
- if (resetResolvedType) {
- mResolvedType = null;
+ resetResolvedType();
+ return this;
+ }
+
+ public Expr resolveTwoWayExpressions(Expr parent) {
+ for (int i = mChildren.size() - 1; i >= 0; i--) {
+ final Expr child = mChildren.get(i);
+ child.resolveTwoWayExpressions(this);
}
- return resetResolvedType;
+ return this;
}
protected void resetResolvedType() {
@@ -203,6 +210,22 @@
mModel = model;
}
+ public void setTwoWay(boolean isTwoWay) {
+ mIsTwoWay = isTwoWay;
+ }
+
+ public boolean isTwoWay() {
+ return mIsTwoWay;
+ }
+
+ protected String addTwoWay(String uniqueKey) {
+ if (mIsTwoWay) {
+ return "twoWay(" + uniqueKey + ")";
+ } else {
+ return "oneWay(" + uniqueKey + ")";
+ }
+ }
+
private BitSet resolveShouldReadWithConditionals() {
// ensure we have invalid flags
BitSet bitSet = new BitSet();
@@ -212,7 +235,6 @@
}
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 {
@@ -411,7 +433,7 @@
*/
public int getRequirementFlagIndex(boolean expectedOutput) {
Preconditions.check(mRequirementId != NO_ID, "If this is an expression w/ conditional"
- + " dependencies, it must be assigned a requirement ID");
+ + " dependencies, it must be assigned a requirement ID. %s", this);
return expectedOutput ? mRequirementId + 1 : mRequirementId;
}
@@ -476,6 +498,7 @@
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
@@ -483,25 +506,29 @@
// 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
-
+ boolean allCovered = true;
for (int i = clone.nextSetBit(0); i != -1; i = clone.nextSetBit(i + 1)) {
final Expr expr = mModel.findFlagExpression(i);
- if (expr == null || !expr.isConditional()) {
+ if (expr == null) {
continue;
}
- final BitSet readForConditional = expr.findConditionalFlags();
+ if (!expr.isConditional()) {
+ allCovered = false;
+ break;
+ }
+ final BitSet readForConditional = (BitSet) expr.findConditionalFlags().clone();
+
+ // FIXME: this does not do full traversal so misses some cases
// 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
+ // if my read-so-far bits cover 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) {
+ readForConditional.andNot(mReadSoFar);
+ if (!readForConditional.isEmpty()) {
+ allCovered = false;
break;
}
}
-
+ mRead = allCovered;
}
if (mRead) {
mShouldReadFlags = null; // if we've been marked as read, clear should read flags
@@ -545,10 +572,12 @@
private Node mCalculationPaths = null;
+ /**
+ * All flag paths that will result in calculation of this expression.
+ */
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 {
@@ -561,6 +590,7 @@
cond.setConditionFlag(
dependant.getRequirementFlagIndex(dependency.getExpectedOutput()));
cond.mParents.add(dependant.getAllCalculationPaths());
+ node.mParents.add(cond);
} else {
node.mParents.add(dependant.getAllCalculationPaths());
}
@@ -612,6 +642,13 @@
}
public void updateExpr(ModelAnalyzer modelAnalyzer) {
+ final Map<String, Expr> exprMap = mModel.getExprMap();
+ for (int i = mParents.size() - 1; i >= 0; i--) {
+ final Expr parent = mParents.get(i);
+ if (exprMap.get(parent.getUniqueKey()) != parent) {
+ mParents.remove(i);
+ }
+ }
for (Expr child : mChildren) {
child.updateExpr(modelAnalyzer);
}
@@ -648,6 +685,54 @@
return mLocations;
}
+ public KCode toCode() {
+ return toCode(false);
+ }
+
+ protected KCode toCode(boolean expand) {
+ if (!expand && isDynamic()) {
+ return new KCode(LayoutBinderWriterKt.getExecutePendingLocalName(this));
+ }
+ return generateCode(expand);
+ }
+
+ public KCode toFullCode() {
+ return generateCode(false);
+ }
+
+ protected abstract KCode generateCode(boolean expand);
+
+ public KCode toInverseCode(KCode value) {
+ throw new IllegalStateException("expression does not support two-way binding");
+ }
+
+ public void assertIsInvertible() {
+ final String errorMessage = getInvertibleError();
+ if (errorMessage != null) {
+ L.e(ErrorMessages.EXPRESSION_NOT_INVERTIBLE, toFullCode().generate(),
+ errorMessage);
+ }
+ }
+
+ /**
+ * @return The reason the expression wasn't invertible or null if it was invertible.
+ */
+ protected abstract String getInvertibleError();
+
+ /**
+ * This expression is the predicate for 1 or more ternary expressions.
+ */
+ public boolean hasConditionalDependant() {
+ for (Dependency dependency : getDependants()) {
+ Expr dependant = dependency.getDependant();
+ if (dependant.isConditional() && dependant instanceof TernaryExpr) {
+ TernaryExpr ternary = (TernaryExpr) dependant;
+ return ternary.getPred() == this;
+ }
+ }
+ return false;
+ }
+
static class Node {
BitSet mBitSet = new BitSet();
@@ -656,19 +741,24 @@
public boolean areAllPathsSatisfied(BitSet readSoFar) {
if (mConditionFlag != -1) {
- return readSoFar.get(mConditionFlag) || mParents.get(0)
- .areAllPathsSatisfied(readSoFar);
+ return readSoFar.get(mConditionFlag)
+ || mParents.get(0).areAllPathsSatisfied(readSoFar);
} else {
- final BitSet clone = (BitSet) readSoFar.clone();
- clone.and(mBitSet);
- if (!clone.isEmpty()) {
- return true;
- }
- if (mParents.isEmpty()) {
+ final BitSet myBitsClone = (BitSet) mBitSet.clone();
+ myBitsClone.andNot(readSoFar);
+ if (!myBitsClone.isEmpty()) {
+ // read so far does not cover all of my invalidation. The only way I could be
+ // covered is that I only have 1 conditional dependent which is covered by this.
+ if (mParents.size() == 1 && mParents.get(0).mConditionFlag != -1) {
+ return mParents.get(0).areAllPathsSatisfied(readSoFar);
+ }
return false;
}
+ if (mParents.isEmpty()) {
+ return true;
+ }
for (Node parent : mParents) {
- if (!parent.areAllPathsSatisfied(clone)) {
+ if (!parent.areAllPathsSatisfied(readSoFar)) {
return false;
}
}
diff --git a/compiler/src/main/java/android/databinding/tool/expr/ExprModel.java b/compiler/src/main/java/android/databinding/tool/expr/ExprModel.java
index e1f6f79..058427e 100644
--- a/compiler/src/main/java/android/databinding/tool/expr/ExprModel.java
+++ b/compiler/src/main/java/android/databinding/tool/expr/ExprModel.java
@@ -16,15 +16,18 @@
package android.databinding.tool.expr;
-import org.antlr.v4.runtime.ParserRuleContext;
-
+import android.databinding.tool.BindingTarget;
+import android.databinding.tool.InverseBinding;
import android.databinding.tool.reflection.ModelAnalyzer;
import android.databinding.tool.reflection.ModelClass;
+import android.databinding.tool.reflection.ModelMethod;
import android.databinding.tool.store.Location;
import android.databinding.tool.util.L;
import android.databinding.tool.util.Preconditions;
import android.databinding.tool.writer.FlagSet;
+import org.antlr.v4.runtime.ParserRuleContext;
+
import java.util.ArrayList;
import java.util.Arrays;
import java.util.BitSet;
@@ -52,6 +55,7 @@
* Any expression can be invalidated by invalidating this flag.
*/
private BitSet mInvalidateAnyFlags;
+ private int mInvalidateAnyFlagIndex;
/**
* Used by code generation. Keeps the list of expressions that are waiting to be evaluated.
@@ -116,10 +120,6 @@
mCurrentParserContext = currentParserContext;
}
- public void unregister(Expr expr) {
- mExprMap.remove(expr.getUniqueKey());
- }
-
public Map<String, Expr> getExprMap() {
return mExprMap;
}
@@ -160,6 +160,14 @@
return register(new StaticIdentifierExpr(name));
}
+ public BuiltInVariableExpr builtInVariable(String name, String type, String accessCode) {
+ return register(new BuiltInVariableExpr(name, type, accessCode));
+ }
+
+ public ViewFieldExpr viewFieldExpr(BindingTarget bindingTarget) {
+ return register(new ViewFieldExpr(bindingTarget));
+ }
+
/**
* Creates a static identifier for the given class or returns the existing one.
*/
@@ -240,6 +248,9 @@
return register(new CastExpr(type, expr));
}
+ public TwoWayListenerExpr twoWayListenerExpr(InverseBinding inverseBinding) {
+ return register(new TwoWayListenerExpr(inverseBinding));
+ }
public List<Expr> getBindingExpressions() {
return mBindingExpressions;
}
@@ -275,31 +286,30 @@
return bindingExpr;
}
+ public void removeExpr(Expr expr) {
+ Preconditions.check(!mSealed, "Can't modify the expression list after sealing the model.");
+ mBindingExpressions.remove(expr);
+ mExprMap.remove(expr.computeUniqueKey());
+ }
+
public List<Expr> getObservables() {
return mObservables;
}
- public void seal() {
- seal(null);
- }
/**
* Give id to each expression. Will be useful if we serialize.
*/
- public void seal(ResolveListenersCallback resolveListeners) {
+ public void seal() {
L.d("sealing model");
List<Expr> notifiableExpressions = new ArrayList<Expr>();
//ensure class analyzer. We need to know observables at this point
final ModelAnalyzer modelAnalyzer = ModelAnalyzer.getInstance();
updateExpressions(modelAnalyzer);
- if (resolveListeners != null) {
- resolveListeners.resolveListeners();
- }
-
int counter = 0;
final Iterable<Expr> observables = filterObservables(modelAnalyzer);
- List<String> flagMapping = new ArrayList<>();
- mObservables = new ArrayList<>();
+ List<String> flagMapping = new ArrayList<String>();
+ mObservables = new ArrayList<Expr>();
for (Expr expr : observables) {
// observables gets initial ids
flagMapping.add(expr.getUniqueKey());
@@ -341,6 +351,17 @@
}
}
+ // now all 2-way bound view fields
+ for (Expr expr : mExprMap.values()) {
+ if (expr instanceof FieldAccessExpr) {
+ FieldAccessExpr fieldAccessExpr = (FieldAccessExpr) expr;
+ if (fieldAccessExpr.getChild() instanceof ViewFieldExpr) {
+ flagMapping.add(fieldAccessExpr.getUniqueKey());
+ fieldAccessExpr.setId(counter++);
+ }
+ }
+ }
+
// non-dynamic binding expressions receive some ids so that they can be invalidated
L.d("list of binding expressions");
for (int i = 0; i < mBindingExpressions.size(); i++) {
@@ -357,7 +378,7 @@
for (Expr expr : mExprMap.values()) {
expr.getDependencies();
}
- final int invalidateAnyFlagIndex = counter ++;
+ mInvalidateAnyFlagIndex = counter ++;
flagMapping.add("INVALIDATE ANY");
mInvalidateableFieldLimit = counter;
mInvalidateableFlags = new BitSet();
@@ -394,7 +415,7 @@
mFlagBucketCount = 1 + (getTotalFlagCount() / FlagSet.sBucketSize);
mInvalidateAnyFlags = new BitSet();
- mInvalidateAnyFlags.set(invalidateAnyFlagIndex, true);
+ mInvalidateAnyFlags.set(mInvalidateAnyFlagIndex, true);
for (Expr expr : mExprMap.values()) {
expr.getShouldReadFlagsWithConditionals();
@@ -446,7 +467,7 @@
}
private List<Expr> filterNonObservableIds(final ModelAnalyzer modelAnalyzer) {
- List<Expr> result = new ArrayList<>();
+ List<Expr> result = new ArrayList<Expr>();
for (Expr input : mExprMap.values()) {
if (input instanceof IdentifierExpr
&& !input.hasId()
@@ -459,7 +480,7 @@
}
private Iterable<Expr> filterObservables(final ModelAnalyzer modelAnalyzer) {
- List<Expr> result = new ArrayList<>();
+ List<Expr> result = new ArrayList<Expr>();
for (Expr input : mExprMap.values()) {
if (input.isObservable()) {
result.add(input);
@@ -470,9 +491,11 @@
public List<Expr> getPendingExpressions() {
if (mPendingExpressions == null) {
- mPendingExpressions = new ArrayList<>();
+ mPendingExpressions = new ArrayList<Expr>();
for (Expr expr : mExprMap.values()) {
- if (!expr.isRead() && expr.isDynamic()) {
+ // if an expression is NOT dynanic but has conditional dependants, still return it
+ // so that conditional flags can be set
+ if (!expr.isRead() && (expr.isDynamic() || expr.hasConditionalDependant())) {
mPendingExpressions.add(expr);
}
}
@@ -482,7 +505,7 @@
public boolean markBitsRead() {
// each has should read flags, we set them back on them
- List<Expr> markedSomeFlagsRead = new ArrayList<>();
+ List<Expr> markedSomeFlagsRead = new ArrayList<Expr>();
for (Expr expr : filterShouldRead(getPendingExpressions())) {
expr.markFlagsAsRead(expr.getShouldReadFlags());
markedSomeFlagsRead.add(expr);
@@ -492,7 +515,7 @@
private boolean pruneDone(List<Expr> markedSomeFlagsAsRead) {
boolean marked = true;
- List<Expr> markedAsReadList = new ArrayList<>();
+ List<Expr> markedAsReadList = new ArrayList<Expr>();
while (marked) {
marked = false;
for (Expr expr : mExprMap.values()) {
@@ -516,14 +539,15 @@
}
}
for (Expr partialRead : markedSomeFlagsAsRead) {
- boolean allPathsAreSatisfied = partialRead.getAllCalculationPaths()
- .areAllPathsSatisfied(partialRead.mReadSoFar);
- if (!allPathsAreSatisfied) {
- continue;
- }
+ // even if all paths are not satisfied, we can elevate certain conditional dependencies
+ // if all of their paths are satisfied.
for (Dependency dependency : partialRead.getDependants()) {
- if (dependency.getDependant().considerElevatingConditionals(partialRead)) {
- elevated = true;
+ Expr dependant = dependency.getDependant();
+ if (dependant.isConditional() && dependant.getAllCalculationPaths()
+ .areAllPathsSatisfied(partialRead.mReadSoFar)) {
+ if (dependant.considerElevatingConditionals(partialRead)) {
+ elevated = true;
+ }
}
}
}
@@ -549,7 +573,7 @@
}
public static List<Expr> filterShouldRead(Iterable<Expr> exprs) {
- List<Expr> result = new ArrayList<>();
+ List<Expr> result = new ArrayList<Expr>();
for (Expr expr : exprs) {
if (!expr.getShouldReadFlags().isEmpty() &&
!hasConditionalOrNestedCannotReadDependency(expr)) {
@@ -594,6 +618,10 @@
return mInvalidateAnyFlags;
}
+ public int getInvalidateAnyFlagIndex() {
+ return mInvalidateAnyFlagIndex;
+ }
+
public Expr argListExpr(Iterable<Expr> expressions) {
return register(new ArgListExpr(mArgListIdCounter ++, expressions));
}
@@ -602,7 +630,8 @@
mCurrentLocationInFile = location;
}
- public interface ResolveListenersCallback {
- void resolveListeners();
+ public Expr listenerExpr(Expr expression, String name, ModelClass listenerType,
+ ModelMethod listenerMethod) {
+ return register(new ListenerExpr(expression, name, listenerType, listenerMethod));
}
}
diff --git a/compiler/src/main/java/android/databinding/tool/expr/FieldAccessExpr.java b/compiler/src/main/java/android/databinding/tool/expr/FieldAccessExpr.java
index 220cb54..ff55a00 100644
--- a/compiler/src/main/java/android/databinding/tool/expr/FieldAccessExpr.java
+++ b/compiler/src/main/java/android/databinding/tool/expr/FieldAccessExpr.java
@@ -16,25 +16,33 @@
package android.databinding.tool.expr;
+import android.databinding.tool.ext.ExtKt;
+import android.databinding.tool.Binding;
+import android.databinding.tool.BindingTarget;
+import android.databinding.tool.InverseBinding;
import android.databinding.tool.processing.Scope;
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.BrNameUtil;
+import android.databinding.tool.store.SetterStore;
+import android.databinding.tool.store.SetterStore.BindingGetterCall;
import android.databinding.tool.util.L;
+import android.databinding.tool.util.Preconditions;
+import android.databinding.tool.writer.KCode;
-import java.util.ArrayList;
import java.util.List;
public class FieldAccessExpr extends Expr {
String mName;
+ // notification name for the field. Important when we map this to a method w/ different name
+ String mBrName;
Callable mGetter;
final boolean mIsObservableField;
- private List<ModelMethod> mListenerMethods;
- private List<ModelMethod> mCalledMethods;
- private List<ModelClass> mListenerTypes;
- private List<ModelMethod> mPotentialListeners;
+ boolean mIsListener;
+ boolean mIsViewAttributeAccess;
FieldAccessExpr(Expr parent, String name) {
super(parent);
@@ -59,29 +67,20 @@
return mGetter;
}
- public List<ModelMethod> getListenerMethods() {
- return mListenerMethods;
- }
-
- public List<ModelMethod> getCalledMethods() {
- return mCalledMethods;
- }
-
- public List<ModelClass> getListenerTypes() { return mListenerTypes; }
-
- public boolean isListener() {
- return mListenerMethods != null && !mListenerMethods.isEmpty();
+ @Override
+ public String getInvertibleError() {
+ if (getGetter().setterName == null) {
+ return "Two-way binding cannot resolve a setter for " + getResolvedType().toJavaCode() +
+ " property '" + mName + "'";
+ }
+ if (!mGetter.isDynamic()) {
+ return "Cannot change a final field in " + getResolvedType().toJavaCode() +
+ " property " + mName;
+ }
+ return null;
}
public int getMinApi() {
- if (isListener()) {
- int minApi = 1;
- for (ModelClass listener : mListenerTypes) {
- int listenerApi = listener.getMinApi();
- minApi = Math.max(minApi, listenerApi);
- }
- return minApi;
- }
return mGetter.getMinApi();
}
@@ -91,7 +90,7 @@
getResolvedType();
}
if (mGetter == null || mGetter.type == Type.METHOD) {
- return !isListener();
+ return true;
}
// if it is static final, gone
if (getChild().isDynamic()) {
@@ -99,6 +98,10 @@
return !mGetter.isStatic() || mGetter.isDynamic();
}
+ if (mIsViewAttributeAccess) {
+ return true; // must be able to invalidate this
+ }
+
// if owner is NOT dynamic, we can be dynamic if an only if getter is dynamic
return mGetter.isDynamic();
}
@@ -108,55 +111,71 @@
}
@Override
- public boolean resolveListeners(ModelClass listener) {
- if (mPotentialListeners == null) {
- return false;
+ public Expr resolveListeners(ModelClass listener, Expr parent) {
+ if (mName == null || mName.isEmpty()) {
+ return this; // ObservableFields aren't listeners
}
-
+ final ModelClass childType = getChild().getResolvedType();
+ if (getGetter() == null) {
+ if (listener == null || !mIsListener) {
+ L.e("Could not resolve %s.%s as an accessor or listener on the attribute.",
+ childType.getCanonicalName(), mName);
+ return this;
+ }
+ getChild().getParents().remove(this);
+ } else if (listener == null) {
+ return this; // Not a listener, but we have a getter.
+ }
List<ModelMethod> abstractMethods = listener.getAbstractMethods();
int numberOfAbstractMethods = abstractMethods == null ? 0 : abstractMethods.size();
if (numberOfAbstractMethods != 1) {
if (mGetter == null) {
L.e("Could not find accessor %s.%s and %s has %d abstract methods, so is" +
" not resolved as a listener",
- getChild().getResolvedType().getCanonicalName(), mName,
+ childType.getCanonicalName(), mName,
listener.getCanonicalName(), numberOfAbstractMethods);
}
- return false;
- }
-
- // See if we've already resolved this listener type
- if (mListenerMethods == null) {
- mListenerMethods = new ArrayList<ModelMethod>();
- mCalledMethods = new ArrayList<ModelMethod>();
- mListenerTypes = new ArrayList<ModelClass>();
- } else {
- for (ModelClass previousListeners : mListenerTypes) {
- if (previousListeners.equals(listener)) {
- return false;
- }
- }
+ return this;
}
// Look for a signature matching the abstract method
final ModelMethod listenerMethod = abstractMethods.get(0);
final ModelClass[] listenerParameters = listenerMethod.getParameterTypes();
- for (ModelMethod method : mPotentialListeners) {
- if (acceptsParameters(method, listenerParameters)) {
- mListenerTypes.add(listener);
- mListenerMethods.add(listenerMethod);
- mCalledMethods.add(method);
+ boolean isStatic = getChild() instanceof StaticIdentifierExpr;
+ List<ModelMethod> methods = childType.findMethods(mName, isStatic);
+ if (methods == null) {
+ return this;
+ }
+ for (ModelMethod method : methods) {
+ if (acceptsParameters(method, listenerParameters) &&
+ method.getReturnType(null).equals(listenerMethod.getReturnType(null))) {
resetResolvedType();
- return true;
+ // replace this with ListenerExpr in parent
+ Expr listenerExpr = getModel().listenerExpr(getChild(), mName, listener,
+ listenerMethod);
+ if (parent != null) {
+ int index;
+ while ((index = parent.getChildren().indexOf(this)) != -1) {
+ parent.getChildren().set(index, listenerExpr);
+ }
+ }
+ if (getModel().mBindingExpressions.contains(this)) {
+ getModel().bindingExpr(listenerExpr);
+ }
+ getParents().remove(parent);
+ if (getParents().isEmpty()) {
+ getModel().removeExpr(this);
+ }
+ return listenerExpr;
}
}
if (mGetter == null) {
L.e("Listener class %s with method %s did not match signature of any method %s.%s",
listener.getCanonicalName(), listenerMethod.getName(),
- getChild().getResolvedType().getCanonicalName(), mName);
+ childType.getCanonicalName(), mName);
}
- return false;
+ return this;
}
private boolean acceptsParameters(ModelMethod method, ModelClass[] listenerParameters) {
@@ -186,15 +205,28 @@
@Override
protected String computeUniqueKey() {
if (mIsObservableField) {
- return join(mName, "..", super.computeUniqueKey());
+ return addTwoWay(join(mName, "..", super.computeUniqueKey()));
}
- return join(mName, ".", super.computeUniqueKey());
+ return addTwoWay(join(mName, ".", super.computeUniqueKey()));
}
public String getName() {
return mName;
}
+ public String getBrName() {
+ if (mIsListener) {
+ return null;
+ }
+ try {
+ Scope.enter(this);
+ Preconditions.checkNotNull(mGetter, "cannot get br name before resolving the getter");
+ return mBrName;
+ } finally {
+ Scope.exit();
+ }
+ }
+
@Override
public void updateExpr(ModelAnalyzer modelAnalyzer) {
try {
@@ -208,10 +240,10 @@
@Override
protected ModelClass resolveType(ModelAnalyzer modelAnalyzer) {
+ if (mIsListener) {
+ return modelAnalyzer.findClass(Object.class);
+ }
if (mGetter == null) {
- if (mPotentialListeners != null) {
- return modelAnalyzer.findClass(Object.class);
- }
Expr child = getChild();
child.getResolvedType();
boolean isStatic = child instanceof StaticIdentifierExpr;
@@ -219,10 +251,10 @@
L.d("resolving %s. Resolved class type: %s", this, resolvedType);
mGetter = resolvedType.findGetterOrField(mName, isStatic);
- mPotentialListeners = resolvedType.findMethods(mName, isStatic);
if (mGetter == null) {
- if (mPotentialListeners == null) {
+ mIsListener = resolvedType.findMethods(mName, isStatic) != null;
+ if (!mIsListener) {
L.e("Could not find accessor %s.%s", resolvedType.getCanonicalName(), mName);
}
return modelAnalyzer.findClass(Object.class);
@@ -248,19 +280,134 @@
getChildren().add(observableField);
observableField.getParents().add(this);
- mGetter = mGetter.resolvedType.findGetterOrField("get", false);
+ mGetter = mGetter.resolvedType.findGetterOrField("", false);
mName = "";
+ mBrName = ExtKt.br(mName);
+ } else if (hasBindableAnnotations()) {
+ mBrName = ExtKt.br(BrNameUtil.brKey(mGetter));
}
}
- if (isListener()) {
- return modelAnalyzer.findClass(Object.class);
- }
return mGetter.resolvedType;
}
@Override
+ public Expr resolveTwoWayExpressions(Expr parent) {
+ final Expr child = getChild();
+ if (!(child instanceof ViewFieldExpr)) {
+ return this;
+ }
+ final ViewFieldExpr expr = (ViewFieldExpr) child;
+ final BindingTarget bindingTarget = expr.getBindingTarget();
+
+ // This is a binding to a View's attribute, so look for matching attribute
+ // on that View's BindingTarget. If there is an expression, we simply replace
+ // the binding with that binding expression.
+ for (Binding binding : bindingTarget.getBindings()) {
+ if (attributeMatchesName(binding.getName(), mName)) {
+ final Expr replacement = binding.getExpr();
+ replaceExpression(parent, replacement);
+ return replacement;
+ }
+ }
+
+ // There was no binding expression to bind to. This should be a two-way binding.
+ // This is a synthesized two-way binding because we must capture the events from
+ // the View and change the value when the target View's attribute changes.
+ final SetterStore setterStore = SetterStore.get(ModelAnalyzer.getInstance());
+ final ModelClass targetClass = expr.getResolvedType();
+ BindingGetterCall getter = setterStore.getGetterCall(mName, targetClass, null, null);
+ if (getter == null) {
+ getter = setterStore.getGetterCall("android:" + mName, targetClass, null, null);
+ if (getter == null) {
+ L.e("Could not resolve the two-way binding attribute '%s' on type '%s'",
+ mName, targetClass);
+ }
+ }
+ InverseBinding inverseBinding = null;
+ for (Binding binding : bindingTarget.getBindings()) {
+ final Expr testExpr = binding.getExpr();
+ if (testExpr instanceof TwoWayListenerExpr &&
+ getter.getEventAttribute().equals(binding.getName())) {
+ inverseBinding = ((TwoWayListenerExpr) testExpr).mInverseBinding;
+ break;
+ }
+ }
+ if (inverseBinding == null) {
+ inverseBinding = bindingTarget.addInverseBinding(mName, getter);
+ }
+ inverseBinding.addChainedExpression(this);
+ mIsViewAttributeAccess = true;
+ enableDirectInvalidation();
+ return this;
+ }
+
+ private static boolean attributeMatchesName(String attribute, String field) {
+ int colonIndex = attribute.indexOf(':');
+ return attribute.substring(colonIndex + 1).equals(field);
+ }
+
+ private void replaceExpression(Expr parent, Expr replacement) {
+ if (parent != null) {
+ List<Expr> children = parent.getChildren();
+ int index;
+ while ((index = children.indexOf(this)) >= 0) {
+ children.set(index, replacement);
+ replacement.getParents().add(parent);
+ }
+ while (getParents().remove(parent)) {
+ // just remove all copies of parent.
+ }
+ }
+ if (getParents().isEmpty()) {
+ getModel().removeExpr(this);
+ }
+ }
+
+ @Override
protected String asPackage() {
String parentPackage = getChild().asPackage();
return parentPackage == null ? null : parentPackage + "." + mName;
}
+
+ @Override
+ protected KCode generateCode(boolean expand) {
+ KCode code = new KCode();
+ if (expand) {
+ String defaultValue = ModelAnalyzer.getInstance().getDefaultValue(
+ getResolvedType().toJavaCode());
+ code.app("(", getChild().toCode(true))
+ .app(" == null) ? ")
+ .app(defaultValue)
+ .app(" : ");
+ }
+ code.app("", getChild().toCode(expand)).app(".");
+ if (getGetter().type == Callable.Type.FIELD) {
+ return code.app(getGetter().name);
+ } else {
+ return code.app(getGetter().name).app("()");
+ }
+ }
+
+ @Override
+ public KCode toInverseCode(KCode value) {
+ if (mGetter.setterName == null) {
+ throw new IllegalStateException("There is no inverse for " + toCode().generate());
+ }
+ KCode castValue = new KCode("(").app(getResolvedType().toJavaCode() + ")(", value).app(")");
+ String type = getChild().getResolvedType().toJavaCode();
+ KCode code = new KCode("targetObj_.");
+ if (getGetter().type == Callable.Type.FIELD) {
+ code.app(getGetter().setterName).app(" = ", castValue).app(";");
+ } else {
+ code.app(getGetter().setterName).app("(", castValue).app(")").app(";");
+ }
+ return new KCode()
+ .app("final ")
+ .app(type)
+ .app(" targetObj_ = ", getChild().toCode(true))
+ .app(";")
+ .nl(new KCode("if (targetObj_ != null) {"))
+ .tab(code)
+ .nl(new KCode("}"));
+ }
}
diff --git a/compiler/src/main/java/android/databinding/tool/expr/GroupExpr.java b/compiler/src/main/java/android/databinding/tool/expr/GroupExpr.java
index bacfe8d..4a76688 100644
--- a/compiler/src/main/java/android/databinding/tool/expr/GroupExpr.java
+++ b/compiler/src/main/java/android/databinding/tool/expr/GroupExpr.java
@@ -18,6 +18,7 @@
import android.databinding.tool.reflection.ModelAnalyzer;
import android.databinding.tool.reflection.ModelClass;
+import android.databinding.tool.writer.KCode;
import java.util.List;
@@ -36,7 +37,23 @@
return getWrapped().constructDependencies();
}
+ @Override
+ protected KCode generateCode(boolean expand) {
+ return new KCode().app("(", getWrapped().toCode(expand)).app(")");
+ }
+
public Expr getWrapped() {
return getChildren().get(0);
}
+
+ @Override
+ public KCode toInverseCode(KCode value) {
+ // Nothing to do here. Other expressions should automatically take care of grouping.
+ return getWrapped().toInverseCode(value);
+ }
+
+ @Override
+ public String getInvertibleError() {
+ return getWrapped().getInvertibleError();
+ }
}
diff --git a/compiler/src/main/java/android/databinding/tool/expr/IdentifierExpr.java b/compiler/src/main/java/android/databinding/tool/expr/IdentifierExpr.java
index f68e3d6..efe4fb1 100644
--- a/compiler/src/main/java/android/databinding/tool/expr/IdentifierExpr.java
+++ b/compiler/src/main/java/android/databinding/tool/expr/IdentifierExpr.java
@@ -19,8 +19,9 @@
import android.databinding.tool.processing.ErrorMessages;
import android.databinding.tool.reflection.ModelAnalyzer;
import android.databinding.tool.reflection.ModelClass;
-import android.databinding.tool.util.L;
import android.databinding.tool.util.Preconditions;
+import android.databinding.tool.writer.KCode;
+import android.databinding.tool.writer.LayoutBinderWriterKt;
import java.util.ArrayList;
import java.util.List;
@@ -28,6 +29,8 @@
public class IdentifierExpr extends Expr {
String mName;
String mUserDefinedType;
+ private boolean mIsDeclared;
+
IdentifierExpr(String name) {
mName = name;
}
@@ -66,11 +69,38 @@
@Override
protected List<Dependency> constructDependencies() {
- return new ArrayList<>();
+ return new ArrayList<Dependency>();
}
@Override
protected String asPackage() {
return mUserDefinedType == null ? mName : null;
}
+
+ @Override
+ protected KCode generateCode(boolean expand) {
+ if (expand) {
+ return new KCode(LayoutBinderWriterKt.getFieldName(this));
+ } else {
+ return new KCode(LayoutBinderWriterKt.getExecutePendingLocalName(this));
+ }
+ }
+
+ public void setDeclared() {
+ mIsDeclared = true;
+ }
+
+ public boolean isDeclared() {
+ return mIsDeclared;
+ }
+
+ @Override
+ public String getInvertibleError() {
+ return null;
+ }
+
+ @Override
+ public KCode toInverseCode(KCode value) {
+ return new KCode().app(LayoutBinderWriterKt.getSetterName(this)).app("(", value).app(");");
+ }
}
diff --git a/compiler/src/main/java/android/databinding/tool/expr/InstanceOfExpr.java b/compiler/src/main/java/android/databinding/tool/expr/InstanceOfExpr.java
index a3ebd13..980d635 100644
--- a/compiler/src/main/java/android/databinding/tool/expr/InstanceOfExpr.java
+++ b/compiler/src/main/java/android/databinding/tool/expr/InstanceOfExpr.java
@@ -18,6 +18,7 @@
import android.databinding.tool.reflection.ModelAnalyzer;
import android.databinding.tool.reflection.ModelClass;
+import android.databinding.tool.writer.KCode;
import java.util.List;
@@ -36,6 +37,14 @@
}
@Override
+ protected KCode generateCode(boolean expand) {
+ return new KCode()
+ .app("", getExpr().toCode(expand))
+ .app(" instanceof ")
+ .app(getType().toJavaCode());
+ }
+
+ @Override
protected ModelClass resolveType(ModelAnalyzer modelAnalyzer) {
mType = modelAnalyzer.findClass(mTypeStr, getModel().getImports());
return modelAnalyzer.loadPrimitive("boolean");
@@ -53,4 +62,9 @@
public ModelClass getType() {
return mType;
}
+
+ @Override
+ public String getInvertibleError() {
+ return "two-way binding can't target a value with the 'instanceof' operator";
+ }
}
diff --git a/compiler/src/main/java/android/databinding/tool/expr/ListenerExpr.java b/compiler/src/main/java/android/databinding/tool/expr/ListenerExpr.java
new file mode 100644
index 0000000..6adf997
--- /dev/null
+++ b/compiler/src/main/java/android/databinding/tool/expr/ListenerExpr.java
@@ -0,0 +1,114 @@
+/*
+ * Copyright (C) 2015 The Android Open Source Project
+ *
+ * Licensed under the Apache License, Version 2.0 (the "License");
+ * you may not use this file except in compliance with the License.
+ * You may obtain a copy of the License at
+ *
+ * http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing, software
+ * distributed under the License is distributed on an "AS IS" BASIS,
+ * WITHOUT 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 android.databinding.tool.reflection.ModelMethod;
+import android.databinding.tool.writer.KCode;
+import android.databinding.tool.writer.LayoutBinderWriterKt;
+
+import java.util.ArrayList;
+import java.util.List;
+
+/**
+ * This wraps an expression, but makes it unique for a particular event listener type.
+ * This is used to differentiate listener methods. For example:
+ * <pre>
+ * public void onFoo(String str) {...}
+ * public void onFoo(int i) {...}
+ * </pre>
+ */
+public class ListenerExpr extends Expr {
+ private final String mName;
+ private final ModelClass mListenerType;
+ private final ModelMethod mMethod;
+
+ ListenerExpr(Expr expr, String name, ModelClass listenerType, ModelMethod method) {
+ super(expr);
+ mName = name;
+ mListenerType = listenerType;
+ mMethod = method;
+ }
+
+ @Override
+ protected ModelClass resolveType(ModelAnalyzer modelAnalyzer) {
+ return mListenerType;
+ }
+
+ public ModelMethod getMethod() {
+ return mMethod;
+ }
+
+ public Expr getChild() {
+ return getChildren().get(0);
+ }
+
+ public String getName() {
+ return mName;
+ }
+
+ @Override
+ public boolean isDynamic() {
+ return getChild().isDynamic();
+ }
+
+ @Override
+ protected List<Dependency> constructDependencies() {
+ final List<Dependency> dependencies = new ArrayList<Dependency>();
+ Dependency dependency = new Dependency(this, getChild());
+ dependency.setMandatory(true);
+ dependencies.add(dependency);
+ return dependencies;
+ }
+
+ protected String computeUniqueKey() {
+ return join(getResolvedType().getCanonicalName(), getChild().computeUniqueKey(), mName);
+ }
+
+ @Override
+ public KCode generateCode(boolean expand) {
+ KCode code = new KCode("(");
+ final int minApi = Math.max(mListenerType.getMinApi(), mMethod.getMinApi());
+ if (minApi > 1) {
+ code.app("(getBuildSdkInt() < " + minApi + ") ? null : ");
+ }
+ final String fieldName = LayoutBinderWriterKt.getFieldName(this);
+ final String listenerClassName = LayoutBinderWriterKt.getListenerClassName(this);
+ final KCode value = getChild().toCode();
+ code.app("((")
+ .app(fieldName)
+ .app(" == null) ? (")
+ .app(fieldName)
+ .app(" = new ")
+ .app(listenerClassName)
+ .app("()) : ")
+ .app(fieldName)
+ .app(")");
+ if (getChild().isDynamic()) {
+ code.app(".setValue(", value)
+ .app(")");
+ }
+ code.app(")");
+ return code;
+ }
+
+ @Override
+ public String getInvertibleError() {
+ return "Listeners cannot be the target of a two-way binding";
+ }
+}
diff --git a/compiler/src/main/java/android/databinding/tool/expr/MathExpr.java b/compiler/src/main/java/android/databinding/tool/expr/MathExpr.java
index a105a34..a302659 100644
--- a/compiler/src/main/java/android/databinding/tool/expr/MathExpr.java
+++ b/compiler/src/main/java/android/databinding/tool/expr/MathExpr.java
@@ -18,6 +18,7 @@
import android.databinding.tool.reflection.ModelAnalyzer;
import android.databinding.tool.reflection.ModelClass;
+import android.databinding.tool.writer.KCode;
import java.util.List;
@@ -30,7 +31,7 @@
@Override
protected String computeUniqueKey() {
- return join(getLeft().getUniqueKey(), mOp, getRight().getUniqueKey());
+ return addTwoWay(join(getLeft().getUniqueKey(), mOp, getRight().getUniqueKey()));
}
@Override
@@ -62,4 +63,83 @@
public Expr getRight() {
return getChildren().get(1);
}
+
+ @Override
+ protected KCode generateCode(boolean expand) {
+ return new KCode().app("", getLeft().toCode(expand)).app(mOp, getRight().toCode(expand));
+ }
+
+ @Override
+ public String getInvertibleError() {
+ if (mOp.equals("%")) {
+ return "The modulus operator (%) is not supported in two-way binding.";
+ } else if (getResolvedType().isString()) {
+ return "String concatenation operator (+) is not supported in two-way binding.";
+ }
+ if (!getLeft().isDynamic()) {
+ return getRight().getInvertibleError();
+ } else if (!getRight().isDynamic()) {
+ return getLeft().getInvertibleError();
+ } else {
+ return "Arithmetic operator " + mOp + " is not supported with two dynamic expressions.";
+ }
+ }
+
+ private String inverseCast() {
+ if (!getLeft().isDynamic()) {
+ return inverseCast(getRight());
+ } else {
+ return inverseCast(getLeft());
+ }
+ }
+
+ private String inverseCast(Expr expr) {
+ if (!expr.getResolvedType().isAssignableFrom(getResolvedType())) {
+ return "(" + getResolvedType() + ")";
+ }
+ return null;
+ }
+
+ @Override
+ public KCode toInverseCode(KCode value) {
+ if (!isDynamic()) {
+ return toCode();
+ }
+ final Expr left = getLeft();
+ final Expr right = getRight();
+ final Expr constExpr = left.isDynamic() ? right : left;
+ final Expr varExpr = left.isDynamic() ? left : right;
+ final String cast = inverseCast();
+ if (cast != null) {
+ value = new KCode(cast).app("(", value).app(")");
+ }
+ switch (mOp.charAt(0)) {
+ case '+': // const + x = value => x = value - const
+ return varExpr.toInverseCode(value.app(" - (", constExpr.toCode()).app(")"));
+ case '*': // const * x = value => x = value / const
+ return varExpr.toInverseCode(value.app(" / (", constExpr.toCode()).app(")"));
+ case '-':
+ if (!left.isDynamic()) { // const - x = value => x = const - value)
+ return varExpr.toInverseCode(new KCode()
+ .app("(", constExpr.toCode())
+ .app(") - (", value)
+ .app(")"));
+ } else { // x - const = value => x = value + const)
+ return varExpr.toInverseCode(value.app(" + ", constExpr.toCode()));
+ }
+ case '/':
+ if (!left.isDynamic()) { // const / x = value => x = const / value
+ return varExpr.toInverseCode(new KCode("(")
+ .app("", constExpr.toCode())
+ .app(") / (", value)
+ .app(")"));
+ } else { // x / const = value => x = value * const
+ return varExpr.toInverseCode(new KCode("(")
+ .app("", value)
+ .app(") * (", constExpr.toCode())
+ .app(")"));
+ }
+ }
+ throw new IllegalStateException("Invalid math operation is not invertible: " + mOp);
+ }
}
diff --git a/compiler/src/main/java/android/databinding/tool/expr/MethodCallExpr.java b/compiler/src/main/java/android/databinding/tool/expr/MethodCallExpr.java
index a96e8f8..4990981 100644
--- a/compiler/src/main/java/android/databinding/tool/expr/MethodCallExpr.java
+++ b/compiler/src/main/java/android/databinding/tool/expr/MethodCallExpr.java
@@ -16,6 +16,9 @@
package android.databinding.tool.expr;
+import static android.databinding.tool.reflection.Callable.DYNAMIC;
+import static android.databinding.tool.reflection.Callable.STATIC;
+
import android.databinding.tool.processing.Scope;
import android.databinding.tool.reflection.Callable;
import android.databinding.tool.reflection.Callable.Type;
@@ -23,13 +26,10 @@
import android.databinding.tool.reflection.ModelClass;
import android.databinding.tool.reflection.ModelMethod;
import android.databinding.tool.util.L;
+import android.databinding.tool.writer.KCode;
import java.util.ArrayList;
-import java.util.Arrays;
import java.util.List;
-import static android.databinding.tool.reflection.Callable.STATIC;
-import static android.databinding.tool.reflection.Callable.DYNAMIC;
-import static android.databinding.tool.reflection.Callable.CAN_BE_INVALIDATED;
public class MethodCallExpr extends Expr {
@@ -38,7 +38,7 @@
Callable mGetter;
static List<Expr> concat(Expr e, List<Expr> list) {
- List<Expr> merged = new ArrayList<>();
+ List<Expr> merged = new ArrayList<Expr>();
merged.add(e);
merged.addAll(list);
return merged;
@@ -61,6 +61,26 @@
}
@Override
+ protected KCode generateCode(boolean expand) {
+ KCode code = new KCode()
+ .app("", getTarget().toCode(expand))
+ .app(".")
+ .app(getGetter().name)
+ .app("(");
+ boolean first = true;
+ for (Expr arg : getArgs()) {
+ if (first) {
+ first = false;
+ } else {
+ code.app(", ");
+ }
+ code.app("", arg.toCode(expand));
+ }
+ code.app(")");
+ return code;
+ }
+
+ @Override
protected ModelClass resolveType(ModelAnalyzer modelAnalyzer) {
if (mGetter == null) {
List<ModelClass> args = new ArrayList<ModelClass>();
@@ -94,7 +114,8 @@
if (method.isStatic()) {
flags |= STATIC;
}
- mGetter = new Callable(Type.METHOD, method.getName(), method.getReturnType(args), flags);
+ mGetter = new Callable(Type.METHOD, method.getName(), null, method.getReturnType(args),
+ method.getParameterTypes().length, flags);
}
return mGetter.resolvedType;
}
@@ -131,4 +152,9 @@
public Callable getGetter() {
return mGetter;
}
+
+ @Override
+ public String getInvertibleError() {
+ return "Method calls may not be used in two-way expressions";
+ }
}
diff --git a/compiler/src/main/java/android/databinding/tool/expr/ResourceExpr.java b/compiler/src/main/java/android/databinding/tool/expr/ResourceExpr.java
index fbf7166..752cb9f 100644
--- a/compiler/src/main/java/android/databinding/tool/expr/ResourceExpr.java
+++ b/compiler/src/main/java/android/databinding/tool/expr/ResourceExpr.java
@@ -17,7 +17,7 @@
import android.databinding.tool.reflection.ModelAnalyzer;
import android.databinding.tool.reflection.ModelClass;
-import android.databinding.tool.writer.WriterPackage;
+import android.databinding.tool.writer.KCode;
import java.util.HashMap;
import java.util.List;
@@ -27,7 +27,7 @@
private final static Map<String, String> RESOURCE_TYPE_TO_R_OBJECT;
static {
- RESOURCE_TYPE_TO_R_OBJECT = new HashMap<>();
+ RESOURCE_TYPE_TO_R_OBJECT = new HashMap<String, String>();
RESOURCE_TYPE_TO_R_OBJECT.put("colorStateList", "color ");
RESOURCE_TYPE_TO_R_OBJECT.put("dimenOffset", "dimen ");
RESOURCE_TYPE_TO_R_OBJECT.put("dimenSize", "dimen ");
@@ -60,7 +60,7 @@
private Map<String, ModelClass> getResourceToTypeMapping(ModelAnalyzer modelAnalyzer) {
if (mResourceToTypeMapping == null) {
final Map<String, String> imports = getModel().getImports();
- mResourceToTypeMapping = new HashMap<>();
+ mResourceToTypeMapping = new HashMap<String, ModelClass>();
mResourceToTypeMapping.put("anim", modelAnalyzer.findClass("android.view.animation.Animation",
imports));
mResourceToTypeMapping.put("animator", modelAnalyzer.findClass("android.animation.Animator",
@@ -129,10 +129,21 @@
return join(base, computeChildrenKey());
}
+ @Override
+ protected KCode generateCode(boolean expand) {
+ return new KCode(toJava());
+ }
+
public String getResourceId() {
return mPackage + "R." + getResourceObject() + "." + mResourceId;
}
+ @Override
+ public String getInvertibleError() {
+ return "Resources may not be the target of a two-way binding expression: " +
+ computeUniqueKey();
+ }
+
public String toJava() {
final String context = "getRoot().getContext()";
final String resources = "getRoot().getResources()";
@@ -140,12 +151,12 @@
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 ("color".equals(mResourceType)) return "android.databinding.DynamicUtil.getColorFromResource(getRoot(), " + resourceName + ")";
+ if ("colorStateList".equals(mResourceType)) return "getColorStateListFromResource(" + 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 ("drawable".equals(mResourceType)) return "getDrawableFromResource(" + resourceName + ")";
if ("fraction".equals(mResourceType)) {
String base = getChildCode(0, "1");
String pbase = getChildCode(1, "1");
@@ -179,7 +190,7 @@
if (getChildren().size() <= childIndex) {
return defaultValue;
} else {
- return WriterPackage.toCode(getChildren().get(childIndex), false).generate();
+ return getChildren().get(childIndex).toCode().generate();
}
}
@@ -187,7 +198,7 @@
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(", ").append(expr.toCode().generate());
}
sb.append(")");
return sb.toString();
diff --git a/compiler/src/main/java/android/databinding/tool/expr/StaticIdentifierExpr.java b/compiler/src/main/java/android/databinding/tool/expr/StaticIdentifierExpr.java
index 8ca5128..7618e94 100644
--- a/compiler/src/main/java/android/databinding/tool/expr/StaticIdentifierExpr.java
+++ b/compiler/src/main/java/android/databinding/tool/expr/StaticIdentifierExpr.java
@@ -16,6 +16,8 @@
package android.databinding.tool.expr;
+import android.databinding.tool.writer.KCode;
+
public class StaticIdentifierExpr extends IdentifierExpr {
StaticIdentifierExpr(String name) {
@@ -31,4 +33,19 @@
public boolean isDynamic() {
return false;
}
+
+ @Override
+ public String getInvertibleError() {
+ return "Class " + getResolvedType().toJavaCode() +
+ " may not be the target of a two-way binding expression";
+ }
+
+ @Override
+ public KCode toInverseCode(KCode value) {
+ throw new IllegalStateException("StaticIdentifierExpr is not invertible.");
+ }
+ @Override
+ protected KCode generateCode(boolean expand) {
+ return new KCode(getResolvedType().toJavaCode());
+ }
}
diff --git a/compiler/src/main/java/android/databinding/tool/expr/SymbolExpr.java b/compiler/src/main/java/android/databinding/tool/expr/SymbolExpr.java
index ca4ad72..38708c0 100644
--- a/compiler/src/main/java/android/databinding/tool/expr/SymbolExpr.java
+++ b/compiler/src/main/java/android/databinding/tool/expr/SymbolExpr.java
@@ -18,6 +18,7 @@
import android.databinding.tool.reflection.ModelAnalyzer;
import android.databinding.tool.reflection.ModelClass;
+import android.databinding.tool.writer.KCode;
import java.util.ArrayList;
import java.util.List;
@@ -47,7 +48,17 @@
}
@Override
+ public String getInvertibleError() {
+ return "Symbol '" + mText + "' cannot be the target of a two-way binding expression";
+ }
+
+ @Override
+ protected KCode generateCode(boolean expand) {
+ return new KCode(getText());
+ }
+
+ @Override
protected List<Dependency> constructDependencies() {
- return new ArrayList<>();
+ return new ArrayList<Dependency>();
}
}
diff --git a/compiler/src/main/java/android/databinding/tool/expr/TernaryExpr.java b/compiler/src/main/java/android/databinding/tool/expr/TernaryExpr.java
index e49c8ce..d4a3727 100644
--- a/compiler/src/main/java/android/databinding/tool/expr/TernaryExpr.java
+++ b/compiler/src/main/java/android/databinding/tool/expr/TernaryExpr.java
@@ -18,6 +18,7 @@
import android.databinding.tool.reflection.ModelAnalyzer;
import android.databinding.tool.reflection.ModelClass;
+import android.databinding.tool.writer.KCode;
import java.util.ArrayList;
import java.util.BitSet;
@@ -46,20 +47,46 @@
}
@Override
+ public String getInvertibleError() {
+ if (getPred().isDynamic()) {
+ return "The condition of a ternary operator must be constant: " +
+ getPred().toFullCode();
+ }
+ final String trueInvertible = getIfTrue().getInvertibleError();
+ if (trueInvertible != null) {
+ return trueInvertible;
+ } else {
+ return getIfFalse().getInvertibleError();
+ }
+ }
+
+ @Override
protected ModelClass resolveType(ModelAnalyzer modelAnalyzer) {
+ final Expr ifTrue = getIfTrue();
+ final Expr ifFalse = getIfFalse();
+ if (isNullLiteral(ifTrue)) {
+ return ifFalse.getResolvedType();
+ } else if (isNullLiteral(ifFalse)) {
+ return ifTrue.getResolvedType();
+ }
return modelAnalyzer.findCommonParentOf(getIfTrue().getResolvedType(),
getIfFalse().getResolvedType());
}
+ private static boolean isNullLiteral(Expr expr) {
+ final ModelClass type = expr.getResolvedType();
+ return (type.isObject() && (expr instanceof SymbolExpr) &&
+ "null".equals(((SymbolExpr)expr).getText()));
+ }
+
@Override
protected List<Dependency> constructDependencies() {
- List<Dependency> deps = new ArrayList<>();
+ List<Dependency> deps = new ArrayList<Dependency>();
Expr predExpr = getPred();
- if (predExpr.isDynamic()) {
- final Dependency pred = new Dependency(this, predExpr);
- pred.setMandatory(true);
- deps.add(pred);
- }
+ 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));
@@ -77,6 +104,26 @@
}
@Override
+ protected KCode generateCode(boolean expand) {
+ return new KCode()
+ .app("", getPred().toCode(expand))
+ .app(" ? ", getIfTrue().toCode(expand))
+ .app(" : ", getIfFalse().toCode(expand));
+
+ }
+
+ @Override
+ public KCode toInverseCode(KCode variable) {
+ return new KCode()
+ .app("if (", getPred().toCode(true))
+ .app(") {")
+ .tab(getIfTrue().toInverseCode(variable))
+ .nl(new KCode("} else {"))
+ .tab(getIfFalse().toInverseCode(variable))
+ .nl(new KCode("}"));
+ }
+
+ @Override
public boolean isConditional() {
return true;
}
diff --git a/compiler/src/main/java/android/databinding/tool/expr/TwoWayListenerExpr.java b/compiler/src/main/java/android/databinding/tool/expr/TwoWayListenerExpr.java
new file mode 100644
index 0000000..1a65673
--- /dev/null
+++ b/compiler/src/main/java/android/databinding/tool/expr/TwoWayListenerExpr.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.expr;
+
+import android.databinding.InverseBindingListener;
+import android.databinding.tool.InverseBinding;
+import android.databinding.tool.reflection.ModelAnalyzer;
+import android.databinding.tool.reflection.ModelClass;
+import android.databinding.tool.writer.KCode;
+import android.databinding.tool.writer.LayoutBinderWriterKt;
+
+import java.util.List;
+
+/**
+ * TwoWayListenerExpr is used to set the event listener for a two-way binding expression.
+ */
+public class TwoWayListenerExpr extends Expr {
+ final InverseBinding mInverseBinding;
+
+ public TwoWayListenerExpr(InverseBinding inverseBinding) {
+ mInverseBinding = inverseBinding;
+ }
+
+ @Override
+ protected ModelClass resolveType(ModelAnalyzer modelAnalyzer) {
+ return modelAnalyzer.findClass(InverseBindingListener.class);
+ }
+
+ @Override
+ protected List<Dependency> constructDependencies() {
+ return constructDynamicChildrenDependencies();
+ }
+
+ @Override
+ protected KCode generateCode(boolean expand) {
+ final String fieldName = LayoutBinderWriterKt.getFieldName(mInverseBinding);
+ return new KCode(fieldName);
+ }
+
+ @Override
+ protected String computeUniqueKey() {
+ return "event(" + mInverseBinding.getEventAttribute() + ", " +
+ System.identityHashCode(mInverseBinding) + ")";
+ }
+
+ @Override
+ public String getInvertibleError() {
+ return "Inverted expressions are already inverted!";
+ }
+}
diff --git a/compiler/src/main/java/android/databinding/tool/expr/UnaryExpr.java b/compiler/src/main/java/android/databinding/tool/expr/UnaryExpr.java
index 1aff945..881a352 100644
--- a/compiler/src/main/java/android/databinding/tool/expr/UnaryExpr.java
+++ b/compiler/src/main/java/android/databinding/tool/expr/UnaryExpr.java
@@ -18,6 +18,7 @@
import android.databinding.tool.reflection.ModelAnalyzer;
import android.databinding.tool.reflection.ModelClass;
+import android.databinding.tool.writer.KCode;
import java.util.List;
@@ -29,8 +30,23 @@
}
@Override
+ public String getInvertibleError() {
+ return getExpr().getInvertibleError();
+ }
+
+ @Override
protected String computeUniqueKey() {
- return join(getOpStr(), getExpr().getUniqueKey());
+ return addTwoWay(join(getOpStr(), getExpr().getUniqueKey()));
+ }
+
+ @Override
+ public KCode toInverseCode(KCode value) {
+ return getExpr().toInverseCode(new KCode().app(mOp, value));
+ }
+
+ @Override
+ protected KCode generateCode(boolean expand) {
+ return new KCode().app(getOp(), getExpr().toCode(expand));
}
@Override
diff --git a/compiler/src/main/java/android/databinding/tool/expr/VersionProvider.java b/compiler/src/main/java/android/databinding/tool/expr/VersionProvider.java
index cef58f7..8e066a4 100644
--- a/compiler/src/main/java/android/databinding/tool/expr/VersionProvider.java
+++ b/compiler/src/main/java/android/databinding/tool/expr/VersionProvider.java
@@ -21,5 +21,5 @@
* This number is used when caching values in code generation. (like flags)
*/
public interface VersionProvider {
- public int getVersion();
+ int getVersion();
}
diff --git a/compiler/src/main/java/android/databinding/tool/expr/ViewFieldExpr.java b/compiler/src/main/java/android/databinding/tool/expr/ViewFieldExpr.java
new file mode 100644
index 0000000..0a6b15b
--- /dev/null
+++ b/compiler/src/main/java/android/databinding/tool/expr/ViewFieldExpr.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.tool.expr;
+
+import android.databinding.tool.BindingTarget;
+import android.databinding.tool.reflection.ModelAnalyzer;
+import android.databinding.tool.reflection.ModelClass;
+import android.databinding.tool.writer.LayoutBinderWriterKt;
+
+public class ViewFieldExpr extends BuiltInVariableExpr {
+ private final BindingTarget mBindingTarget;
+
+ ViewFieldExpr(BindingTarget bindingTarget) {
+ super(LayoutBinderWriterKt.getFieldName(bindingTarget), initialType(bindingTarget),
+ LayoutBinderWriterKt.getFieldName(bindingTarget));
+ mBindingTarget = bindingTarget;
+ }
+
+ @Override
+ public String getInvertibleError() {
+ return "View fields may not be the target of two-way binding";
+ }
+
+ private static String initialType(BindingTarget bindingTarget) {
+ return bindingTarget.isBinder()
+ ? "android.databinding.ViewDataBinding"
+ : bindingTarget.getInterfaceType();
+ }
+
+ public BindingTarget getBindingTarget() {
+ return mBindingTarget;
+ }
+
+ @Override
+ protected ModelClass resolveType(ModelAnalyzer modelAnalyzer) {
+ final ModelClass type = modelAnalyzer.findClass(mBindingTarget.getInterfaceType(), null);
+ if (type == null) {
+ return modelAnalyzer.findClass("android.databinding.ViewDataBinding", null);
+ }
+ return type;
+ }
+}
diff --git a/compiler/src/main/java/android/databinding/tool/reflection/Callable.java b/compiler/src/main/java/android/databinding/tool/reflection/Callable.java
index 4bd0906..5b9acf2 100644
--- a/compiler/src/main/java/android/databinding/tool/reflection/Callable.java
+++ b/compiler/src/main/java/android/databinding/tool/reflection/Callable.java
@@ -15,13 +15,9 @@
*/
package android.databinding.tool.reflection;
-import android.databinding.tool.util.L;
-
-import java.util.List;
-
public class Callable {
- public static enum Type {
+ public enum Type {
METHOD,
FIELD
}
@@ -34,14 +30,21 @@
public final String name;
+ public final String setterName;
+
public final ModelClass resolvedType;
private final int mFlags;
- public Callable(Type type, String name, ModelClass resolvedType, int flags) {
+ private final int mParameterCount;
+
+ public Callable(Type type, String name, String setterName, ModelClass resolvedType,
+ int parameterCount, int flags) {
this.type = type;
this.name = name;
this.resolvedType = resolvedType;
+ mParameterCount = parameterCount;
+ this.setterName = setterName;
mFlags = flags;
}
@@ -49,6 +52,10 @@
return resolvedType.toJavaCode();
}
+ public int getParameterCount() {
+ return mParameterCount;
+ }
+
public boolean isDynamic() {
return (mFlags & DYNAMIC) != 0;
}
diff --git a/compiler/src/main/java/android/databinding/tool/reflection/ModelAnalyzer.java b/compiler/src/main/java/android/databinding/tool/reflection/ModelAnalyzer.java
index 739c9e0..b1de46e 100644
--- a/compiler/src/main/java/android/databinding/tool/reflection/ModelAnalyzer.java
+++ b/compiler/src/main/java/android/databinding/tool/reflection/ModelAnalyzer.java
@@ -94,6 +94,12 @@
curr = curr.getSuperclass();
}
if (curr == null) {
+ if (modelClass1.isObject() && modelClass2.isInterface()) {
+ return modelClass1;
+ } else if (modelClass2.isObject() && modelClass1.isInterface()) {
+ return modelClass2;
+ }
+
ModelClass primitive1 = modelClass1.unbox();
ModelClass primitive2 = modelClass2.unbox();
if (!modelClass1.equals(primitive1) || !modelClass2.equals(primitive2)) {
diff --git a/compiler/src/main/java/android/databinding/tool/reflection/ModelClass.java b/compiler/src/main/java/android/databinding/tool/reflection/ModelClass.java
index 2bc2c86..6fbc5a0 100644
--- a/compiler/src/main/java/android/databinding/tool/reflection/ModelClass.java
+++ b/compiler/src/main/java/android/databinding/tool/reflection/ModelClass.java
@@ -15,10 +15,9 @@
*/
package android.databinding.tool.reflection;
-import org.apache.commons.lang3.StringUtils;
-
import android.databinding.tool.reflection.Callable.Type;
import android.databinding.tool.util.L;
+import android.databinding.tool.util.StringUtils;
import java.util.ArrayList;
import java.util.Arrays;
@@ -143,6 +142,11 @@
public abstract boolean isTypeVar();
/**
+ * @return whether this is a wildcard type argument or not.
+ */
+ public abstract boolean isWildcard();
+
+ /**
* @return whether or not this ModelClass is java.lang.Object and not a primitive or subclass.
*/
public boolean isObject() {
@@ -321,6 +325,19 @@
}
/**
+ * @return The class or interface name of this type or the primitive type if it isn't a
+ * reference type.
+ */
+ public String getSimpleName() {
+ final String canonicalName = getCanonicalName();
+ final int dotIndex = canonicalName.lastIndexOf('.');
+ if (dotIndex >= 0) {
+ return canonicalName.substring(dotIndex + 1);
+ }
+ return canonicalName;
+ }
+
+ /**
* Returns this class type without any generic type arguments.
* @return this class type without any generic type arguments.
*/
@@ -367,8 +384,8 @@
*/
public Callable findGetterOrField(String name, boolean staticOnly) {
if ("length".equals(name) && isArray()) {
- return new Callable(Type.FIELD, name, ModelAnalyzer.getInstance().loadPrimitive("int"),
- 0);
+ return new Callable(Type.FIELD, name, null,
+ ModelAnalyzer.getInstance().loadPrimitive("int"), 0, 0);
}
String capitalized = StringUtils.capitalize(name);
String[] methodNames = {
@@ -396,8 +413,11 @@
flags |= CAN_BE_INVALIDATED;
}
}
+ final ModelMethod setterMethod = findSetter(method, name);
+ final String setterName = setterMethod == null ? null : setterMethod.getName();
final Callable result = new Callable(Callable.Type.METHOD, methodName,
- method.getReturnType(null), flags);
+ setterName, method.getReturnType(null), method.getParameterTypes().length,
+ flags);
return result;
}
}
@@ -420,16 +440,37 @@
}
ModelClass fieldType = publicField.getFieldType();
int flags = 0;
+ String setterFieldName = name;
+ if (publicField.isStatic()) {
+ flags |= STATIC;
+ }
if (!publicField.isFinal()) {
+ setterFieldName = null;
flags |= DYNAMIC;
}
if (publicField.isBindable()) {
flags |= CAN_BE_INVALIDATED;
}
- if (publicField.isStatic()) {
- flags |= STATIC;
+ return new Callable(Callable.Type.FIELD, name, setterFieldName, fieldType, 0, flags);
+ }
+
+ public ModelMethod findInstanceGetter(String name) {
+ String capitalized = StringUtils.capitalize(name);
+ String[] methodNames = {
+ "get" + capitalized,
+ "is" + capitalized,
+ name
+ };
+ for (String methodName : methodNames) {
+ ModelMethod[] methods = getMethods(methodName, new ArrayList<ModelClass>(), false);
+ for (ModelMethod method : methods) {
+ if (method.isPublic() && !method.isStatic() &&
+ !method.getReturnType(Arrays.asList(method.getParameterTypes())).isVoid()) {
+ return method;
+ }
+ }
}
- return new Callable(Callable.Type.FIELD, name, fieldType, flags);
+ return null;
}
private ModelField getField(String name, boolean allowPrivate, boolean isStatic) {
@@ -445,6 +486,33 @@
return null;
}
+ private ModelMethod findSetter(ModelMethod getter, String originalName) {
+ final String capitalized = StringUtils.capitalize(originalName);
+ final String[] possibleNames;
+ if (originalName.equals(getter.getName())) {
+ possibleNames = new String[] { originalName, "set" + capitalized };
+ } else if (getter.getName().startsWith("is")){
+ possibleNames = new String[] { "set" + capitalized, "setIs" + capitalized };
+ } else {
+ possibleNames = new String[] { "set" + capitalized };
+ }
+ for (String name : possibleNames) {
+ List<ModelMethod> methods = findMethods(name, getter.isStatic());
+ if (methods != null) {
+ ModelClass param = getter.getReturnType(null);
+ for (ModelMethod method : methods) {
+ ModelClass[] parameterTypes = method.getParameterTypes();
+ if (parameterTypes != null && parameterTypes.length == 1 &&
+ parameterTypes[0].equals(param) &&
+ method.isStatic() == getter.isStatic()) {
+ return method;
+ }
+ }
+ }
+ }
+ return null;
+ }
+
/**
* Finds public methods that matches the given name exactly. These may be resolved into
* listener methods during Expr.resolveListeners.
@@ -464,6 +532,21 @@
return matching;
}
+ public boolean isIncomplete() {
+ if (isTypeVar() || isWildcard()) {
+ return true;
+ }
+ List<ModelClass> typeArgs = getTypeArguments();
+ if (typeArgs != null) {
+ for (ModelClass typeArg : typeArgs) {
+ if (typeArg.isIncomplete()) {
+ return true;
+ }
+ }
+ }
+ return false;
+ }
+
protected abstract ModelField[] getDeclaredFields();
protected abstract ModelMethod[] getDeclaredMethods();
diff --git a/compiler/src/main/java/android/databinding/tool/reflection/SdkUtil.java b/compiler/src/main/java/android/databinding/tool/reflection/SdkUtil.java
index 4b5e09f..da73f66 100644
--- a/compiler/src/main/java/android/databinding/tool/reflection/SdkUtil.java
+++ b/compiler/src/main/java/android/databinding/tool/reflection/SdkUtil.java
@@ -64,7 +64,7 @@
int result = sApiChecker.getMinApi(classDesc, methodDesc);
L.d("checking method api for %s, class:%s method:%s. result: %d", modelMethod.getName(),
classDesc, methodDesc, result);
- if (result > 1) {
+ if (result > 0) {
return result;
}
declaringClass = declaringClass.getSuperclass();
@@ -74,7 +74,7 @@
static class ApiChecker {
- private Map<String, Integer> mFullLookup = new HashMap<String, Integer>();
+ private Map<String, Integer> mFullLookup;
private Document mDoc;
@@ -103,6 +103,7 @@
private void buildFullLookup() throws XPathExpressionException {
NodeList allClasses = mDoc.getChildNodes().item(0).getChildNodes();
+ mFullLookup = new HashMap<String, Integer>(allClasses.getLength() * 4);
for (int j = 0; j < allClasses.getLength(); j++) {
Node node = allClasses.item(j);
if (node.getNodeType() != Node.ELEMENT_NODE || !"class"
@@ -122,16 +123,17 @@
}
int methodSince = getSince(child);
int since = Math.max(classSince, methodSince);
- if (since > SdkUtil.sMinSdk) {
- String methodDesc = child.getAttributes().getNamedItem("name")
- .getNodeValue();
- String key = cacheKey(classDesc, methodDesc);
- mFullLookup.put(key, since);
- }
+ String methodDesc = child.getAttributes().getNamedItem("name")
+ .getNodeValue();
+ String key = cacheKey(classDesc, methodDesc);
+ mFullLookup.put(key, since);
}
}
}
+ /**
+ * Returns 0 if we cannot find the API level for the method.
+ */
public int getMinApi(String classDesc, String methodOrFieldDesc) {
if (mDoc == null || mXPath == null) {
return 1;
@@ -141,7 +143,7 @@
}
final String key = cacheKey(classDesc, methodOrFieldDesc);
Integer since = mFullLookup.get(key);
- return since == null ? 1 : since;
+ return since == null ? 0 : since;
}
private static String cacheKey(String classDesc, String methodOrFieldDesc) {
diff --git a/compiler/src/main/java/android/databinding/tool/reflection/annotation/AnnotationAnalyzer.java b/compiler/src/main/java/android/databinding/tool/reflection/annotation/AnnotationAnalyzer.java
index f02e051..4773f17 100644
--- a/compiler/src/main/java/android/databinding/tool/reflection/annotation/AnnotationAnalyzer.java
+++ b/compiler/src/main/java/android/databinding/tool/reflection/annotation/AnnotationAnalyzer.java
@@ -24,7 +24,10 @@
import java.util.HashMap;
import java.util.Map;
+import javax.annotation.processing.Messager;
import javax.annotation.processing.ProcessingEnvironment;
+import javax.lang.model.element.AnnotationMirror;
+import javax.lang.model.element.Element;
import javax.lang.model.element.TypeElement;
import javax.lang.model.type.DeclaredType;
import javax.lang.model.type.TypeKind;
@@ -37,7 +40,7 @@
public static final Map<String, TypeKind> PRIMITIVE_TYPES;
static {
- PRIMITIVE_TYPES = new HashMap<>();
+ PRIMITIVE_TYPES = new HashMap<String, TypeKind>();
PRIMITIVE_TYPES.put("boolean", TypeKind.BOOLEAN);
PRIMITIVE_TYPES.put("byte", TypeKind.BYTE);
PRIMITIVE_TYPES.put("short", TypeKind.SHORT);
@@ -55,8 +58,13 @@
setInstance(this);
L.setClient(new L.Client() {
@Override
- public void printMessage(Diagnostic.Kind kind, String message) {
- mProcessingEnv.getMessager().printMessage(kind, message);
+ public void printMessage(Diagnostic.Kind kind, String message, Element element) {
+ Messager messager = mProcessingEnv.getMessager();
+ if (element != null) {
+ messager.printMessage(kind, message, element);
+ } else {
+ messager.printMessage(kind, message);
+ }
}
});
}
diff --git a/compiler/src/main/java/android/databinding/tool/reflection/annotation/AnnotationClass.java b/compiler/src/main/java/android/databinding/tool/reflection/annotation/AnnotationClass.java
index 52a7add..02e767e 100644
--- a/compiler/src/main/java/android/databinding/tool/reflection/annotation/AnnotationClass.java
+++ b/compiler/src/main/java/android/databinding/tool/reflection/annotation/AnnotationClass.java
@@ -15,8 +15,6 @@
*/
package android.databinding.tool.reflection.annotation;
-import org.antlr.v4.codegen.model.decl.Decl;
-
import android.databinding.tool.reflection.ModelAnalyzer;
import android.databinding.tool.reflection.ModelClass;
import android.databinding.tool.reflection.ModelField;
@@ -57,6 +55,9 @@
@Override
public String toJavaCode() {
+ if (isIncomplete()) {
+ return getCanonicalName();
+ }
return mTypeMirror.toString();
}
@@ -247,6 +248,11 @@
}
@Override
+ public boolean isWildcard() {
+ return mTypeMirror.getKind() == TypeKind.WILDCARD;
+ }
+
+ @Override
public boolean isInterface() {
return mTypeMirror.getKind() == TypeKind.DECLARED &&
((DeclaredType)mTypeMirror).asElement().getKind() == ElementKind.INTERFACE;
diff --git a/compiler/src/main/java/android/databinding/tool/reflection/annotation/AnnotationMethod.java b/compiler/src/main/java/android/databinding/tool/reflection/annotation/AnnotationMethod.java
index 26ca573..d7caa45 100644
--- a/compiler/src/main/java/android/databinding/tool/reflection/annotation/AnnotationMethod.java
+++ b/compiler/src/main/java/android/databinding/tool/reflection/annotation/AnnotationMethod.java
@@ -23,12 +23,16 @@
import java.util.List;
+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.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.Elements;
import javax.lang.model.util.Types;
class AnnotationMethod extends ModelMethod {
@@ -36,6 +40,7 @@
final DeclaredType mDeclaringType;
final ExecutableElement mExecutableElement;
int mApiLevel = -1; // calculated on demand
+ ModelClass mReceiverType;
public AnnotationMethod(DeclaredType declaringType, ExecutableElement executableElement) {
mDeclaringType = declaringType;
@@ -46,7 +51,46 @@
@Override
public ModelClass getDeclaringClass() {
- return new AnnotationClass(mDeclaringType);
+ if (mReceiverType == null) {
+ mReceiverType = findReceiverType(mDeclaringType);
+ if (mReceiverType == null) {
+ mReceiverType = new AnnotationClass(mDeclaringType);
+ }
+ }
+ return mReceiverType;
+ }
+
+ // TODO: When going to Java 1.8, use mExecutableElement.getReceiverType()
+ private ModelClass findReceiverType(DeclaredType subType) {
+ List<? extends TypeMirror> supers = getTypeUtils().directSupertypes(subType);
+ for (TypeMirror superType : supers) {
+ if (superType.getKind() == TypeKind.DECLARED) {
+ DeclaredType declaredType = (DeclaredType) superType;
+ ModelClass inSuper = findReceiverType(declaredType);
+ if (inSuper != null) {
+ return inSuper;
+ } else if (hasExecutableMethod(declaredType)) {
+ return new AnnotationClass(declaredType);
+ }
+ }
+ }
+ return null;
+ }
+
+ private boolean hasExecutableMethod(DeclaredType declaredType) {
+ Elements elementUtils = getElementUtils();
+ TypeElement enclosing = (TypeElement) mExecutableElement.getEnclosingElement();
+ TypeElement typeElement = (TypeElement) declaredType.asElement();
+ for (Element element : typeElement.getEnclosedElements()) {
+ if (element.getKind() == ElementKind.METHOD) {
+ ExecutableElement executableElement = (ExecutableElement) element;
+ if (executableElement.equals(mExecutableElement) ||
+ elementUtils.overrides(mExecutableElement, executableElement, enclosing)) {
+ return true;
+ }
+ }
+ }
+ return false;
}
@Override
@@ -115,6 +159,14 @@
return mExecutableElement.isVarArgs();
}
+ private static Types getTypeUtils() {
+ return AnnotationAnalyzer.get().mProcessingEnv.getTypeUtils();
+ }
+
+ private static Elements getElementUtils() {
+ return AnnotationAnalyzer.get().mProcessingEnv.getElementUtils();
+ }
+
@Override
public String toString() {
return "AnnotationMethod{" +
diff --git a/compiler/src/main/java/android/databinding/tool/store/SetterStore.java b/compiler/src/main/java/android/databinding/tool/store/SetterStore.java
index e9f34db..9568810 100644
--- a/compiler/src/main/java/android/databinding/tool/store/SetterStore.java
+++ b/compiler/src/main/java/android/databinding/tool/store/SetterStore.java
@@ -15,14 +15,14 @@
*/
package android.databinding.tool.store;
-import org.apache.commons.lang3.StringUtils;
-
+import android.databinding.InverseBindingListener;
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 android.databinding.tool.util.Preconditions;
+import android.databinding.tool.util.StringUtils;
import java.io.IOException;
import java.io.Serializable;
@@ -31,6 +31,7 @@
import java.util.Collections;
import java.util.Comparator;
import java.util.HashMap;
+import java.util.HashSet;
import java.util.Iterator;
import java.util.List;
import java.util.Map;
@@ -50,8 +51,10 @@
public class SetterStore {
private static SetterStore sStore;
- private final IntermediateV1 mStore;
+ private final IntermediateV2 mStore;
private final ModelAnalyzer mClassAnalyzer;
+ private HashMap<String, List<String>> mInstanceAdapters;
+ private final HashSet<String> mInverseEventAttributes = new HashSet<String>();
private Comparator<MultiAttributeSetter> COMPARE_MULTI_ATTRIBUTE_SETTERS =
new Comparator<MultiAttributeSetter>() {
@@ -140,9 +143,19 @@
}
};
- private SetterStore(ModelAnalyzer modelAnalyzer, IntermediateV1 store) {
+ private SetterStore(ModelAnalyzer modelAnalyzer, IntermediateV2 store) {
mClassAnalyzer = modelAnalyzer;
mStore = store;
+ for (HashMap<AccessorKey, InverseDescription> adapter : mStore.inverseAdapters.values()) {
+ for (InverseDescription inverseDescription : adapter.values()) {
+ mInverseEventAttributes.add(inverseDescription.event);
+ }
+ }
+ for (HashMap<String, InverseDescription> method : mStore.inverseMethods.values()) {
+ for (InverseDescription inverseDescription : method.values()) {
+ mInverseEventAttributes.add(inverseDescription.event);
+ }
+ }
}
public static SetterStore get(ModelAnalyzer modelAnalyzer) {
@@ -153,7 +166,7 @@
}
private static SetterStore load(ModelAnalyzer modelAnalyzer) {
- IntermediateV1 store = new IntermediateV1();
+ IntermediateV2 store = new IntermediateV2();
List<Intermediate> previousStores = GenerationalClassUtil
.loadObjects(GenerationalClassUtil.ExtensionFilter.SETTER_STORE);
for (Intermediate intermediate : previousStores) {
@@ -176,6 +189,21 @@
renamed.put(declaringClass, methodDescription);
}
+ public void addInverseMethod(String attribute, String event, String declaringClass,
+ String method, TypeElement declaredOn) {
+ attribute = stripNamespace(attribute);
+ event = stripNamespace(event);
+ HashMap<String, InverseDescription> inverseMethods = mStore.inverseMethods.get(attribute);
+ if (inverseMethods == null) {
+ inverseMethods = new HashMap<String, InverseDescription>();
+ mStore.inverseMethods.put(attribute, inverseMethods);
+ }
+ InverseDescription methodDescription = new InverseDescription(
+ declaredOn.getQualifiedName().toString(), method, event);
+ L.d("STORE addInverseMethod desc %s", methodDescription);
+ inverseMethods.put(declaringClass, methodDescription);
+ }
+
public void addBindingAdapter(ProcessingEnvironment processingEnv, String attribute,
ExecutableElement bindingMethod, boolean takesComponent) {
attribute = stripNamespace(attribute);
@@ -201,6 +229,32 @@
adapters.put(key, new MethodDescription(bindingMethod, 1, takesComponent));
}
+ public void addInverseAdapter(ProcessingEnvironment processingEnv, String attribute,
+ String event, ExecutableElement bindingMethod, boolean takesComponent) {
+ attribute = stripNamespace(attribute);
+ event = stripNamespace(event);
+ L.d("STORE addInverseAdapter %s %s", attribute, bindingMethod);
+ HashMap<AccessorKey, InverseDescription> adapters = mStore.inverseAdapters.get(attribute);
+
+ if (adapters == null) {
+ adapters = new HashMap<AccessorKey, InverseDescription>();
+ mStore.inverseAdapters.put(attribute, adapters);
+ }
+ List<? extends VariableElement> parameters = bindingMethod.getParameters();
+ final int viewIndex = takesComponent ? 1 : 0;
+ TypeMirror viewType = eraseType(processingEnv, parameters.get(viewIndex).asType());
+ String view = getQualifiedName(viewType);
+ TypeMirror returnType = eraseType(processingEnv, bindingMethod.getReturnType());
+ String value = getQualifiedName(returnType);
+
+ AccessorKey key = new AccessorKey(view, value);
+ if (adapters.containsKey(key)) {
+ throw new IllegalArgumentException("Already exists!");
+ }
+
+ adapters.put(key, new InverseDescription(bindingMethod, event, takesComponent));
+ }
+
private static TypeMirror eraseType(ProcessingEnvironment processingEnv,
TypeMirror typeMirror) {
if (hasTypeVar(typeMirror)) {
@@ -261,10 +315,10 @@
}
public void addBindingAdapter(ProcessingEnvironment processingEnv, String[] attributes,
- ExecutableElement bindingMethod, boolean takesComponent) {
+ ExecutableElement bindingMethod, boolean takesComponent, boolean requireAll) {
L.d("STORE add multi-value BindingAdapter %d %s", attributes.length, bindingMethod);
MultiValueAdapterKey key = new MultiValueAdapterKey(processingEnv, bindingMethod,
- attributes, takesComponent);
+ attributes, takesComponent, requireAll);
MethodDescription methodDescription = new MethodDescription(bindingMethod,
attributes.length, takesComponent);
mStore.multiValueAdapters.put(key, methodDescription);
@@ -273,7 +327,9 @@
private static String[] stripAttributes(String[] attributes) {
String[] strippedAttributes = new String[attributes.length];
for (int i = 0; i < attributes.length; i++) {
- strippedAttributes[i] = stripNamespace(attributes[i]);
+ if (attributes[i] != null) {
+ strippedAttributes[i] = stripNamespace(attributes[i]);
+ }
}
return strippedAttributes;
}
@@ -287,13 +343,36 @@
}
private static String getQualifiedName(TypeMirror type) {
- if (type.getKind() == TypeKind.ARRAY) {
+ final TypeKind kind = type.getKind();
+ if (kind == TypeKind.ARRAY) {
return getQualifiedName(((ArrayType) type).getComponentType()) + "[]";
+ } else if (kind == TypeKind.DECLARED && isIncompleteType(type)) {
+ DeclaredType declaredType = (DeclaredType) type;
+ return declaredType.asElement().toString();
} else {
return type.toString();
}
}
+ private static boolean isIncompleteType(TypeMirror type) {
+ final TypeKind kind = type.getKind();
+ if (kind == TypeKind.TYPEVAR || kind == TypeKind.WILDCARD) {
+ return true;
+ } else if (kind == TypeKind.DECLARED) {
+ DeclaredType declaredType = (DeclaredType) type;
+ List<? extends TypeMirror> typeArgs = declaredType.getTypeArguments();
+ if (typeArgs == null) {
+ return false;
+ }
+ for (TypeMirror arg : typeArgs) {
+ if (isIncompleteType(arg)) {
+ return true;
+ }
+ }
+ }
+ return false;
+ }
+
public void addConversionMethod(ExecutableElement conversionMethod) {
L.d("STORE addConversionMethod %s", conversionMethod);
List<? extends VariableElement> parameters = conversionMethod.getParameters();
@@ -374,6 +453,10 @@
return attribute;
}
+ public boolean isTwoWayEventAttribute(String attribute) {
+ attribute = stripNamespace(attribute);
+ return mInverseEventAttributes.contains(attribute);
+ }
public List<MultiAttributeSetter> getMultiAttributeSetterCalls(String[] attributes,
ModelClass viewType, ModelClass[] valueType) {
attributes = stripAttributes(attributes);
@@ -396,6 +479,72 @@
return calls;
}
+ private static String simpleName(String className) {
+ int dotIndex = className.lastIndexOf('.');
+ if (dotIndex < 0) {
+ return className;
+ } else {
+ return className.substring(dotIndex + 1);
+ }
+ }
+
+ public Map<String, List<String>> getComponentBindingAdapters() {
+ ensureInstanceAdapters();
+ return mInstanceAdapters;
+ }
+
+ private String getBindingAdapterCall(String className) {
+ ensureInstanceAdapters();
+ final String simpleName = simpleName(className);
+ List<String> adapters = mInstanceAdapters.get(simpleName);
+ if (adapters.size() == 1) {
+ return "get" + simpleName + "()";
+ } else {
+ int index = adapters.indexOf(className) + 1;
+ return "get" + simpleName + index + "()";
+ }
+ }
+
+ private void ensureInstanceAdapters() {
+ if (mInstanceAdapters == null) {
+ HashSet<String> adapters = new HashSet<String>();
+ for (HashMap<AccessorKey, MethodDescription> methods : mStore.adapterMethods.values()) {
+ for (MethodDescription method : methods.values()) {
+ if (!method.isStatic) {
+ adapters.add(method.type);
+ }
+ }
+ }
+ for (MethodDescription method : mStore.multiValueAdapters.values()) {
+ if (!method.isStatic) {
+ adapters.add(method.type);
+ }
+ }
+ for (Map<AccessorKey, InverseDescription> methods : mStore.inverseAdapters.values()) {
+ for (InverseDescription method : methods.values()) {
+ if (!method.isStatic) {
+ adapters.add(method.type);
+ }
+ }
+ }
+ mInstanceAdapters = new HashMap<String, List<String>>();
+ for (String adapter : adapters) {
+ final String simpleName = simpleName(adapter);
+ List<String> list = mInstanceAdapters.get(simpleName);
+ if (list == null) {
+ list = new ArrayList<String>();
+ mInstanceAdapters.put(simpleName, list);
+ }
+ list.add(adapter);
+ }
+ for (List<String> list : mInstanceAdapters.values()) {
+ if (list.size() > 1) {
+ Collections.sort(list);
+ }
+ }
+ }
+ }
+
// Removes all MultiAttributeSetters that require any of the values in attributes
private static void removeConsumedAttributes(ArrayList<MultiAttributeSetter> matching,
String[] attributes) {
@@ -428,7 +577,7 @@
ModelClass viewType, ModelClass[] valueType) {
final ArrayList<MultiAttributeSetter> setters = new ArrayList<MultiAttributeSetter>();
for (MultiValueAdapterKey adapter : mStore.multiValueAdapters.keySet()) {
- if (adapter.attributes.length > attributes.length) {
+ if (adapter.requireAll && adapter.attributes.length > attributes.length) {
continue;
}
ModelClass viewClass = mClassAnalyzer.findClass(adapter.viewType, null);
@@ -453,10 +602,12 @@
int matchingAttributes = 0;
String[] casts = new String[adapter.attributes.length];
MethodDescription[] conversions = new MethodDescription[adapter.attributes.length];
+ boolean[] supplied = new boolean[adapter.attributes.length];
for (int i = 0; i < allAttributes.length; i++) {
Integer index = adapter.attributeIndices.get(allAttributes[i]);
if (index != null) {
+ supplied[index] = true;
matchingAttributes++;
final String parameterTypeStr = adapter.parameterTypes[index];
final ModelClass parameterType = eraseType(
@@ -485,11 +636,11 @@
}
}
- if (matchingAttributes != adapter.attributes.length) {
+ if ((adapter.requireAll && matchingAttributes != adapter.attributes.length) ||
+ matchingAttributes == 0) {
return null;
} else {
- return new MultiAttributeSetter(adapter, adapter.attributes, method, conversions,
- casts);
+ return new MultiAttributeSetter(adapter, supplied, method, conversions, casts);
}
}
@@ -524,7 +675,7 @@
adapters.get(key).method, adapterValueType.toJavaCode(),
valueType.toJavaCode());
boolean isBetterView = bestViewType == null ||
- bestValueType.isAssignableFrom(adapterValueType);
+ bestViewType.isAssignableFrom(adapterViewType);
if (isBetterParameter(valueType, adapterValueType, bestValueType,
isBetterView, imports)) {
bestViewType = adapterViewType;
@@ -558,6 +709,76 @@
return setterCall;
}
+ public BindingGetterCall getGetterCall(String attribute, ModelClass viewType,
+ ModelClass valueType, Map<String, String> imports) {
+ if (viewType == null) {
+ return null;
+ } else if (viewType.isViewDataBinding()) {
+ return new ViewDataBindingGetterCall(attribute);
+ }
+
+ attribute = stripNamespace(attribute);
+ viewType = viewType.erasure();
+
+ InverseMethod bestMethod = getBestGetter(viewType, valueType, attribute, imports);
+ HashMap<AccessorKey, InverseDescription> adapters = mStore.inverseAdapters.get(attribute);
+ if (adapters != null) {
+ for (AccessorKey key : adapters.keySet()) {
+ try {
+ ModelClass adapterViewType = mClassAnalyzer
+ .findClass(key.viewType, imports).erasure();
+ if (adapterViewType != null && adapterViewType.isAssignableFrom(viewType)) {
+ try {
+ L.d("getter return type is %s", key.valueType);
+ final ModelClass adapterValueType = eraseType(mClassAnalyzer
+ .findClass(key.valueType, imports));
+ L.d("getter %s returns type %s, compared to %s",
+ adapters.get(key).method, adapterValueType.toJavaCode(),
+ valueType);
+ boolean isBetterView = bestMethod.viewType == null ||
+ bestMethod.viewType.isAssignableFrom(adapterViewType);
+ if (valueType == null ||
+ isBetterParameter(adapterValueType, valueType,
+ bestMethod.returnType, isBetterView, imports)) {
+ bestMethod.viewType = adapterViewType;
+ bestMethod.returnType = adapterValueType;
+ InverseDescription inverseDescription = adapters.get(key);
+ ModelClass listenerType = ModelAnalyzer.getInstance().findClass(
+ InverseBindingListener.class);
+ BindingSetterCall eventCall = getSetterCall(
+ inverseDescription.event, viewType, listenerType, imports);
+ if (eventCall == null) {
+ List<MultiAttributeSetter> setters =
+ getMultiAttributeSetterCalls(
+ new String[]{inverseDescription.event},
+ viewType, new ModelClass[] {listenerType});
+ if (setters.size() != 1) {
+ L.e("Could not find event '%s' on View type '%s'",
+ inverseDescription.event,
+ viewType.getCanonicalName());
+ } else {
+ bestMethod.call = new AdapterGetter(inverseDescription,
+ setters.get(0));
+ }
+ } else {
+ bestMethod.call = new AdapterGetter(inverseDescription,
+ eventCall);
+ }
+ }
+
+ } catch (Exception e) {
+ L.e(e, "Unknown class: %s", key.valueType);
+ }
+ }
+ } catch (Exception e) {
+ L.e(e, "Unknown class: %s", key.viewType);
+ }
+ }
+ }
+
+ return bestMethod.call;
+ }
+
public boolean isUntaggable(String viewType) {
return mStore.untaggableTypes.containsKey(viewType);
}
@@ -606,6 +827,72 @@
return bestMethod;
}
+ private InverseMethod getBestGetter(ModelClass viewType, ModelClass valueType,
+ String attribute, Map<String, String> imports) {
+ if (viewType.isGeneric()) {
+ if (valueType != null) {
+ valueType = eraseType(valueType, viewType.getTypeArguments());
+ }
+ viewType = viewType.erasure();
+ }
+ ModelClass bestReturnType = null;
+ InverseDescription bestDescription = null;
+ ModelClass bestViewType = null;
+ ModelMethod bestMethod = null;
+
+ HashMap<String, InverseDescription> inverseMethods = mStore.inverseMethods.get(attribute);
+ if (inverseMethods != null) {
+ for (String className : inverseMethods.keySet()) {
+ try {
+ ModelClass methodViewType = mClassAnalyzer.findClass(className, imports);
+ if (methodViewType.erasure().isAssignableFrom(viewType)) {
+ boolean isBetterViewType = bestViewType == null ||
+ bestViewType.isAssignableFrom(methodViewType);
+ final InverseDescription inverseDescription = inverseMethods.get(className);
+ final String name = inverseDescription.method.isEmpty() ?
+ trimAttributeNamespace(attribute) : inverseDescription.method;
+ ModelMethod method = methodViewType.findInstanceGetter(name);
+ ModelClass returnType = method.getReturnType(null); // no parameters
+ if (valueType == null || bestReturnType == null ||
+ isBetterParameter(returnType, valueType, bestReturnType,
+ isBetterViewType, imports)) {
+ bestDescription = inverseDescription;
+ bestReturnType = returnType;
+ bestViewType = methodViewType;
+ bestMethod = method;
+ }
+ }
+ } catch (Exception e) {
+ //printMessage(Diagnostic.Kind.NOTE, "Unknown class: " + className);
+ }
+ }
+ }
+
+ BindingGetterCall call = null;
+ if (bestDescription != null) {
+ final ModelClass listenerType = ModelAnalyzer.getInstance().findClass(
+ InverseBindingListener.class);
+ SetterCall eventSetter = getSetterCall(bestDescription.event, viewType,
+ listenerType, imports);
+ if (eventSetter == null) {
+ List<MultiAttributeSetter> setters = getMultiAttributeSetterCalls(
+ new String[] {bestDescription.event}, viewType,
+ new ModelClass[] {listenerType});
+ if (setters.size() != 1) {
+ L.e("Could not find event '%s' on View type '%s'", bestDescription.event,
+ viewType.getCanonicalName());
+ bestViewType = null;
+ bestReturnType = null;
+ } else {
+ call = new ViewGetterCall(bestDescription, bestMethod, setters.get(0));
+ }
+ } else {
+ call = new ViewGetterCall(bestDescription, bestMethod, eventSetter);
+ }
+ }
+ return new InverseMethod(call, bestReturnType, bestViewType);
+ }
+
private static ModelClass eraseType(ModelClass type, List<ModelClass> typeParameters) {
List<ModelClass> typeArguments = type.getTypeArguments();
if (typeArguments == null || typeParameters == null) {
@@ -674,6 +961,9 @@
private MethodDescription getConversionMethod(ModelClass from, ModelClass to,
Map<String, String> imports) {
if (from != null && to != null) {
+ if (to.isObject()) {
+ return null;
+ }
for (String fromClassName : mStore.conversionMethods.keySet()) {
try {
ModelClass convertFrom = mClassAnalyzer.findClass(fromClassName, imports);
@@ -701,24 +991,30 @@
}
private boolean canUseForConversion(ModelClass from, ModelClass to) {
+ if (from.isIncomplete() || to.isIncomplete()) {
+ from = from.erasure();
+ to = to.erasure();
+ }
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.multiValueAdapters.putAll(intermediateV1.multiValueAdapters);
- store.untaggableTypes.putAll(intermediateV1.untaggableTypes);
+ private static void merge(IntermediateV2 store, Intermediate dumpStore) {
+ IntermediateV2 intermediateV2 = (IntermediateV2) dumpStore.upgrade();
+ merge(store.adapterMethods, intermediateV2.adapterMethods);
+ merge(store.renamedMethods, intermediateV2.renamedMethods);
+ merge(store.conversionMethods, intermediateV2.conversionMethods);
+ store.multiValueAdapters.putAll(intermediateV2.multiValueAdapters);
+ store.untaggableTypes.putAll(intermediateV2.untaggableTypes);
+ merge(store.inverseAdapters, intermediateV2.inverseAdapters);
+ merge(store.inverseMethods, intermediateV2.inverseMethods);
}
- private static <K, V> void merge(HashMap<K, HashMap<V, MethodDescription>> first,
- HashMap<K, HashMap<V, MethodDescription>> second) {
+ private static <K, V, D> void merge(HashMap<K, HashMap<V, D>> first,
+ HashMap<K, HashMap<V, D>> second) {
for (K key : second.keySet()) {
- HashMap<V, MethodDescription> firstVals = first.get(key);
- HashMap<V, MethodDescription> secondVals = second.get(key);
+ HashMap<V, D> firstVals = first.get(key);
+ HashMap<V, D> secondVals = second.get(key);
if (firstVals == null) {
first.put(key, secondVals);
} else {
@@ -731,14 +1027,16 @@
}
}
- private static String createAdapterCall(MethodDescription adapter, String bindingAdapterCall,
+ private static String createAdapterCall(MethodDescription adapter,
String componentExpression, String viewExpression, String... args) {
StringBuilder sb = new StringBuilder();
if (adapter.isStatic) {
sb.append(adapter.type);
} else {
- sb.append(componentExpression).append('.').append(bindingAdapterCall);
+ final SetterStore setterStore = SetterStore.get(ModelAnalyzer.getInstance());
+ final String binderCall = setterStore.getBindingAdapterCall(adapter.type);
+ sb.append(componentExpression).append('.').append(binderCall);
}
sb.append('.').append(adapter.method).append('(');
if (adapter.componentClass != null) {
@@ -764,11 +1062,15 @@
public final String[] parameterTypes;
+ public final boolean requireAll;
+
public final TreeMap<String, Integer> attributeIndices = new TreeMap<String, Integer>();
public MultiValueAdapterKey(ProcessingEnvironment processingEnv,
- ExecutableElement method, String[] attributes, boolean takesComponent) {
+ ExecutableElement method, String[] attributes, boolean takesComponent,
+ boolean requireAll) {
this.attributes = stripAttributes(attributes);
+ this.requireAll = requireAll;
List<? extends VariableElement> parameters = method.getParameters();
final int argStart = 1 + (takesComponent ? 1 : 0);
this.viewType = getQualifiedName(eraseType(processingEnv,
@@ -874,6 +1176,35 @@
}
}
+ private static class InverseDescription extends MethodDescription {
+ private static final long serialVersionUID = 1;
+
+ public final String event;
+
+ public InverseDescription(String type, String method, String event) {
+ super(type, method);
+ this.event = event;
+ }
+
+ public InverseDescription(ExecutableElement method, String event, boolean takesComponent) {
+ super(method, 1, takesComponent);
+ this.event = event;
+ }
+
+ @Override
+ public boolean equals(Object obj) {
+ if (!super.equals(obj) || !(obj instanceof InverseDescription)) {
+ return false;
+ }
+ return event.equals(((InverseDescription) obj).event);
+ }
+
+ @Override
+ public int hashCode() {
+ return mergedHashCode(type, method, event);
+ }
+ }
+
private static class AccessorKey implements Serializable {
private static final long serialVersionUID = 1;
@@ -929,6 +1260,24 @@
@Override
public Intermediate upgrade() {
+ IntermediateV2 v2 = new IntermediateV2();
+ v2.adapterMethods.putAll(adapterMethods);
+ v2.renamedMethods.putAll(renamedMethods);
+ v2.conversionMethods.putAll(conversionMethods);
+ v2.untaggableTypes.putAll(untaggableTypes);
+ v2.multiValueAdapters.putAll(multiValueAdapters);
+ return v2;
+ }
+ }
+
+ private static class IntermediateV2 extends IntermediateV1 {
+ public final HashMap<String, HashMap<AccessorKey, InverseDescription>> inverseAdapters =
+ new HashMap<String, HashMap<AccessorKey, InverseDescription>>();
+ public final HashMap<String, HashMap<String, InverseDescription>> inverseMethods =
+ new HashMap<String, HashMap<String, InverseDescription>>();
+
+ @Override
+ public Intermediate upgrade() {
return this;
}
}
@@ -973,16 +1322,11 @@
public String getBindingAdapterInstanceClass() {
return null;
}
-
- @Override
- public void setBindingAdapterCall(String method) {
- }
}
public static class AdapterSetter extends SetterCall {
final MethodDescription mAdapter;
final ModelClass mParameterType;
- String mBindingAdapterCall;
public AdapterSetter(MethodDescription adapter, ModelClass parameterType) {
mAdapter = adapter;
@@ -992,14 +1336,14 @@
@Override
public String toJavaInternal(String componentExpression, String viewExpression,
String valueExpression) {
- return createAdapterCall(mAdapter, mBindingAdapterCall, componentExpression,
+ return createAdapterCall(mAdapter, componentExpression,
viewExpression, mCastString + valueExpression);
}
@Override
protected String toJavaInternal(String componentExpression, String viewExpression,
String oldValue, String valueExpression) {
- return createAdapterCall(mAdapter, mBindingAdapterCall, componentExpression,
+ return createAdapterCall(mAdapter, componentExpression,
viewExpression, mCastString + oldValue, mCastString + valueExpression);
}
@@ -1022,11 +1366,6 @@
public String getBindingAdapterInstanceClass() {
return mAdapter.isStatic ? null : mAdapter.type;
}
-
- @Override
- public void setBindingAdapterCall(String method) {
- mBindingAdapterCall = method;
- }
}
public static class ModelMethodSetter extends SetterCall {
@@ -1069,10 +1408,6 @@
public String getBindingAdapterInstanceClass() {
return null;
}
-
- @Override
- public void setBindingAdapterCall(String method) {
- }
}
public interface BindingSetterCall {
@@ -1086,8 +1421,6 @@
ModelClass[] getParameterTypes();
String getBindingAdapterInstanceClass();
-
- void setBindingAdapterCall(String method);
}
public static abstract class SetterCall implements BindingSetterCall {
@@ -1138,19 +1471,41 @@
private final MethodDescription[] mConverters;
private final String[] mCasts;
private final MultiValueAdapterKey mKey;
- String mBindingAdapterCall;
+ private final boolean[] mSupplied;
- public MultiAttributeSetter(MultiValueAdapterKey key, String[] attributes,
+ public MultiAttributeSetter(MultiValueAdapterKey key, boolean[] supplied,
MethodDescription adapter, MethodDescription[] converters, String[] casts) {
Preconditions.check(converters != null &&
- converters.length == attributes.length &&
- casts != null && casts.length == attributes.length,
+ converters.length == key.attributes.length &&
+ casts != null && casts.length == key.attributes.length &&
+ supplied.length == key.attributes.length,
"invalid arguments to create multi attr setter");
- this.attributes = attributes;
this.mAdapter = adapter;
this.mConverters = converters;
this.mCasts = casts;
this.mKey = key;
+ this.mSupplied = supplied;
+ if (key.requireAll) {
+ this.attributes = key.attributes;
+ } else {
+ int numSupplied = 0;
+ for (int i = 0; i < mKey.attributes.length; i++) {
+ if (supplied[i]) {
+ numSupplied++;
+ }
+ }
+ if (numSupplied == key.attributes.length) {
+ this.attributes = key.attributes;
+ } else {
+ this.attributes = new String[numSupplied];
+ int attrIndex = 0;
+ for (int i = 0; i < key.attributes.length; i++) {
+ if (supplied[i]) {
+ attributes[attrIndex++] = key.attributes[i];
+ }
+ }
+ }
+ }
}
@Override
@@ -1159,33 +1514,45 @@
Preconditions.check(valueExpressions.length == attributes.length * 2,
"MultiAttributeSetter needs %s items, received %s",
Arrays.toString(attributes), Arrays.toString(valueExpressions));
- final int numAttrs = attributes.length;
+ final int numAttrs = mKey.attributes.length;
String[] args = new String[numAttrs + (requiresOldValue() ? numAttrs : 0)];
- final int startIndex = mAdapter.requiresOldValue ? 0 : attributes.length;
+ final int startIndex = mAdapter.requiresOldValue ? 0 : numAttrs;
+ int attrIndex = mAdapter.requiresOldValue ? 0 : attributes.length;
+ final ModelAnalyzer modelAnalyzer = ModelAnalyzer.getInstance();
StringBuilder argBuilder = new StringBuilder();
- for (int i = startIndex; i < valueExpressions.length; i++) {
+ final int endIndex = numAttrs * 2;
+ for (int i = startIndex; i < endIndex; i++) {
argBuilder.setLength(0);
- if (mConverters[i % attributes.length] != null) {
- final MethodDescription converter = mConverters[i % attributes.length];
- argBuilder.append(converter.type)
- .append('.')
- .append(converter.method)
- .append('(')
- .append(valueExpressions[i])
- .append(')');
+ if (!mSupplied[i % numAttrs]) {
+ final String paramType = mKey.parameterTypes[i % numAttrs];
+ final String defaultValue = modelAnalyzer.getDefaultValue(paramType);
+ argBuilder.append('(')
+ .append(paramType)
+ .append(')')
+ .append(defaultValue);
} else {
- if (mCasts[i % attributes.length] != null) {
- argBuilder.append('(')
- .append(mCasts[i % attributes.length])
+ if (mConverters[i % numAttrs] != null) {
+ final MethodDescription converter = mConverters[i % numAttrs];
+ argBuilder.append(converter.type)
+ .append('.')
+ .append(converter.method)
+ .append('(')
+ .append(valueExpressions[attrIndex])
.append(')');
+ } else {
+ if (mCasts[i % numAttrs] != null) {
+ argBuilder.append('(')
+ .append(mCasts[i % numAttrs])
+ .append(')');
+ }
+ argBuilder.append(valueExpressions[attrIndex]);
}
- argBuilder.append(valueExpressions[i]);
+ attrIndex++;
}
args[i - startIndex] = argBuilder.toString();
}
- return createAdapterCall(mAdapter, mBindingAdapterCall, componentExpression,
- viewExpression, args);
+ return createAdapterCall(mAdapter, componentExpression, viewExpression, args);
}
@Override
@@ -1203,8 +1570,11 @@
ModelClass[] parameters = new ModelClass[attributes.length];
String[] paramTypeStrings = mKey.parameterTypes;
ModelAnalyzer modelAnalyzer = ModelAnalyzer.getInstance();
- for (int i = 0; i < attributes.length; i++) {
- parameters[i] = modelAnalyzer.findClass(paramTypeStrings[i], null);
+ int attrIndex = 0;
+ for (int i = 0; i < mKey.attributes.length; i++) {
+ if (mSupplied[i]) {
+ parameters[attrIndex++] = modelAnalyzer.findClass(paramTypeStrings[i], null);
+ }
}
return parameters;
}
@@ -1215,11 +1585,6 @@
}
@Override
- public void setBindingAdapterCall(String method) {
- mBindingAdapterCall = method;
- }
-
- @Override
public String toString() {
return "MultiAttributeSetter{" +
"attributes=" + Arrays.toString(attributes) +
@@ -1230,4 +1595,206 @@
'}';
}
}
+
+ public static class ViewDataBindingEventSetter implements BindingSetterCall {
+
+ public ViewDataBindingEventSetter() {
+ }
+
+ @Override
+ public String toJava(String componentExpression, String viewExpression,
+ String... valueExpressions) {
+ return "setBindingInverseListener(" + viewExpression + ", " +
+ valueExpressions[0] + ", " + valueExpressions[1] + ")";
+ }
+
+ @Override
+ public int getMinApi() {
+ return 0;
+ }
+
+ @Override
+ public boolean requiresOldValue() {
+ return true;
+ }
+
+ @Override
+ public ModelClass[] getParameterTypes() {
+ ModelClass[] parameterTypes = new ModelClass[1];
+ parameterTypes[0] = ModelAnalyzer.getInstance().findClass(
+ "android.databinding.ViewDataBinder.PropertyChangedInverseListener", null);
+ return parameterTypes;
+ }
+
+ @Override
+ public String getBindingAdapterInstanceClass() {
+ return null;
+ }
+ }
+
+ public interface BindingGetterCall {
+ String toJava(String componentExpression, String viewExpression);
+
+ int getMinApi();
+
+ String getBindingAdapterInstanceClass();
+
+ void setBindingAdapterCall(String method);
+
+ BindingSetterCall getEvent();
+
+ String getEventAttribute();
+ }
+
+ public static class ViewDataBindingGetterCall implements BindingGetterCall {
+ private final String mGetter;
+ private final BindingSetterCall mEventSetter;
+ private final String mAttribute;
+
+ public ViewDataBindingGetterCall(String attribute) {
+ final int colonIndex = attribute.indexOf(':');
+ mAttribute = attribute.substring(colonIndex + 1);
+ mGetter = "get" + StringUtils.capitalize(mAttribute);
+ mEventSetter = new ViewDataBindingEventSetter();
+ }
+
+ @Override
+ public String toJava(String componentExpression, String viewExpression) {
+ return viewExpression + "." + mGetter + "()";
+ }
+
+ @Override
+ public int getMinApi() {
+ return 0;
+ }
+
+ @Override
+ public String getBindingAdapterInstanceClass() {
+ return null;
+ }
+
+ @Override
+ public void setBindingAdapterCall(String method) {
+ }
+
+ @Override
+ public BindingSetterCall getEvent() {
+ return mEventSetter;
+ }
+
+ @Override
+ public String getEventAttribute() {
+ return mAttribute;
+ }
+ }
+
+ public static class ViewGetterCall implements BindingGetterCall {
+ private final InverseDescription mInverseDescription;
+ private final BindingSetterCall mEventCall;
+ private final ModelMethod mMethod;
+
+ public ViewGetterCall(InverseDescription inverseDescription, ModelMethod method,
+ BindingSetterCall eventCall) {
+ mInverseDescription = inverseDescription;
+ mEventCall = eventCall;
+ mMethod = method;
+ }
+
+ @Override
+ public BindingSetterCall getEvent() {
+ return mEventCall;
+ }
+
+ @Override
+ public String getEventAttribute() {
+ return mInverseDescription.event;
+ }
+
+ @Override
+ public String toJava(String componentExpression, String viewExpression) {
+ return viewExpression + "." + mMethod.getName() + "()";
+ }
+
+ @Override
+ public int getMinApi() {
+ return mMethod.getMinApi();
+ }
+
+ @Override
+ public String getBindingAdapterInstanceClass() {
+ return null;
+ }
+
+ @Override
+ public void setBindingAdapterCall(String method) {
+ }
+ }
+
+ public static class AdapterGetter implements BindingGetterCall {
+ private final InverseDescription mInverseDescription;
+ private String mBindingAdapterCall;
+ private final BindingSetterCall mEventCall;
+
+ public AdapterGetter(InverseDescription description, BindingSetterCall eventCall) {
+ mInverseDescription = description;
+ mEventCall = eventCall;
+ }
+
+ @Override
+ public String toJava(String componentExpression, String viewExpression) {
+ StringBuilder sb = new StringBuilder();
+
+ if (mInverseDescription.isStatic) {
+ sb.append(mInverseDescription.type);
+ } else {
+ sb.append(componentExpression).append('.').append(mBindingAdapterCall);
+ }
+ sb.append('.').append(mInverseDescription.method).append('(');
+ if (mInverseDescription.componentClass != null) {
+ if (!"DataBindingComponent".equals(mInverseDescription.componentClass)) {
+ sb.append('(').append(mInverseDescription.componentClass).append(") ");
+ }
+ sb.append(componentExpression).append(", ");
+ }
+ sb.append(viewExpression).append(')');
+ return sb.toString();
+ }
+
+ @Override
+ public int getMinApi() {
+ return 1;
+ }
+
+ @Override
+ public String getBindingAdapterInstanceClass() {
+ return mInverseDescription.isStatic ? null : mInverseDescription.type;
+ }
+
+ @Override
+ public void setBindingAdapterCall(String method) {
+ mBindingAdapterCall = method;
+ }
+
+ @Override
+ public BindingSetterCall getEvent() {
+ return mEventCall;
+ }
+
+ @Override
+ public String getEventAttribute() {
+ return mInverseDescription.event;
+ }
+ }
+
+ private static class InverseMethod {
+ public BindingGetterCall call;
+ public ModelClass returnType;
+ public ModelClass viewType;
+
+ public InverseMethod(BindingGetterCall call, ModelClass returnType, ModelClass viewType) {
+ this.call = call;
+ this.returnType = returnType;
+ this.viewType = viewType;
+ }
+ }
}
diff --git a/compiler/src/main/java/android/databinding/tool/util/BrNameUtil.java b/compiler/src/main/java/android/databinding/tool/util/BrNameUtil.java
new file mode 100644
index 0000000..f79f37c
--- /dev/null
+++ b/compiler/src/main/java/android/databinding/tool/util/BrNameUtil.java
@@ -0,0 +1,97 @@
+/*
+ * Copyright (C) 2015 The Android Open Source Project
+ *
+ * Licensed under the Apache License, Version 2.0 (the "License");
+ * you may not use this file except in compliance with the License.
+ * You may obtain a copy of the License at
+ *
+ * http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing, software
+ * distributed under the License is distributed on an "AS IS" BASIS,
+ * WITHOUT 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 android.databinding.tool.reflection.Callable;
+
+/**
+ * Central place to convert method/field names to BR observable fields
+ */
+public class BrNameUtil {
+ private static String stripPrefixFromField(String name) {
+ 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;
+ }
+
+ public static String brKey(Callable callable) {
+ if (callable.type == Callable.Type.FIELD) {
+ return stripPrefixFromField(callable.name);
+ }
+ CharSequence propertyName;
+ final String name = callable.name;
+ if (isGetter(callable) || isSetter(callable)) {
+ propertyName = name.subSequence(3, name.length());
+ } else if (isBooleanGetter(callable)) {
+ propertyName = name.subSequence(2, name.length());
+ } else {
+ L.e("@Bindable associated with method must follow JavaBeans convention %s", callable);
+ return null;
+ }
+ char firstChar = propertyName.charAt(0);
+ return "" + Character.toLowerCase(firstChar) +
+ propertyName.subSequence(1, propertyName.length());
+ }
+
+ private static boolean isGetter(Callable callable) {
+ return prefixes(callable.name, "get") &&
+ Character.isJavaIdentifierStart(callable.name.charAt(3)) &&
+ callable.getParameterCount() == 0 &&
+ !callable.resolvedType.isVoid();
+ }
+
+ private static boolean isSetter(Callable callable) {
+ return prefixes(callable.name, "set") &&
+ Character.isJavaIdentifierStart(callable.name.charAt(3)) &&
+ callable.getParameterCount() == 1 &&
+ callable.resolvedType.isVoid();
+ }
+
+ private static boolean isBooleanGetter(Callable callable) {
+ return prefixes(callable.name, "is") &&
+ Character.isJavaIdentifierStart(callable.name.charAt(2)) &&
+ callable.getParameterCount() == 0 &&
+ callable.resolvedType.isBoolean();
+ }
+
+ 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;
+ }
+}
diff --git a/compiler/src/main/java/android/databinding/tool/util/GenerationalClassUtil.java b/compiler/src/main/java/android/databinding/tool/util/GenerationalClassUtil.java
index 6a06b75..18e4b96 100644
--- a/compiler/src/main/java/android/databinding/tool/util/GenerationalClassUtil.java
+++ b/compiler/src/main/java/android/databinding/tool/util/GenerationalClassUtil.java
@@ -80,7 +80,9 @@
// assume it is a zip file
loadFomZipFile(file);
}
- } catch (IOException | URISyntaxException e) {
+ } catch (IOException e) {
+ L.d("cannot open zip file from %s", url);
+ } catch (URISyntaxException e) {
L.d("cannot open zip file from %s", url);
}
}
diff --git a/compiler/src/main/java/android/databinding/tool/writer/ComponentWriter.java b/compiler/src/main/java/android/databinding/tool/writer/ComponentWriter.java
index e35cc2c..bc03893 100644
--- a/compiler/src/main/java/android/databinding/tool/writer/ComponentWriter.java
+++ b/compiler/src/main/java/android/databinding/tool/writer/ComponentWriter.java
@@ -15,81 +15,26 @@
*/
package android.databinding.tool.writer;
-import android.databinding.tool.Binding;
-import android.databinding.tool.BindingTarget;
-import android.databinding.tool.LayoutBinder;
-import android.databinding.tool.processing.ScopedException;
+import android.databinding.tool.reflection.ModelAnalyzer;
+import android.databinding.tool.store.SetterStore;
-import java.util.ArrayList;
-import java.util.HashMap;
import java.util.List;
-import java.util.Map.Entry;
+import java.util.Map;
public class ComponentWriter {
private static final String INDENT = " ";
- private final HashMap<String, ArrayList<String>> mBindingAdapters = new HashMap<>();
- public ComponentWriter(List<LayoutBinder> binders) {
- HashMap<Binding, ArrayList<String>> bindings = new HashMap<>();
-
- for (LayoutBinder layoutBinder : binders) {
- try {
- android.databinding.tool.processing.Scope.enter(layoutBinder);
- for (BindingTarget target : layoutBinder.getBindingTargets()) {
- try {
- android.databinding.tool.processing.Scope.enter(target);
- for (Binding binding : target.getBindings()) {
- try {
- android.databinding.tool.processing.Scope.enter(binding);
- final String bindingAdapter = binding
- .getBindingAdapterInstanceClass();
- if (bindingAdapter != null) {
- final String simpleName = simpleName(bindingAdapter);
- ArrayList<String> classes = mBindingAdapters.get(simpleName);
- if (classes == null) {
- classes = new ArrayList<>();
- mBindingAdapters.put(simpleName, classes);
- classes.add(bindingAdapter);
- } else if (!classes.contains(bindingAdapter)) {
- classes.add(bindingAdapter);
- }
- bindings.put(binding, classes);
- }
- } catch (ScopedException ex) {
- android.databinding.tool.processing.Scope.defer(ex);
- } finally{
- android.databinding.tool.processing.Scope.exit();
- }
- }
- } finally {
- android.databinding.tool.processing.Scope.exit();
- }
- }
- } finally {
- android.databinding.tool.processing.Scope.exit();
- }
- }
-
- for (Entry<Binding, ArrayList<String>> entry : bindings.entrySet()) {
- final Binding binding = entry.getKey();
- final ArrayList<String> classes = entry.getValue();
- final String call;
- if (classes.size() == 1) {
- call = "get" + simpleName(classes.get(0)) + "()";
- } else {
- int index = classes.indexOf(binding.getBindingAdapterInstanceClass());
- call = "get" + simpleName(classes.get(index)) + (index + 1) + "()";
- }
- binding.setBindingAdapterCall(call);
- }
+ public ComponentWriter() {
}
public String createComponent() {
final StringBuilder builder = new StringBuilder();
builder.append("package android.databinding;\n\n");
builder.append("public interface DataBindingComponent {\n");
- for (final String simpleName : mBindingAdapters.keySet()) {
- final ArrayList<String> classes = mBindingAdapters.get(simpleName);
+ final SetterStore setterStore = SetterStore.get(ModelAnalyzer.getInstance());
+ Map<String, List<String>> bindingAdapters = setterStore.getComponentBindingAdapters();
+ for (final String simpleName : bindingAdapters.keySet()) {
+ final List<String> classes = bindingAdapters.get(simpleName);
if (classes.size() > 1) {
int index = 1;
for (String className : classes) {
@@ -103,15 +48,6 @@
return builder.toString();
}
- private static String simpleName(String className) {
- int dotIndex = className.lastIndexOf('.');
- if (dotIndex < 0) {
- return className;
- } else {
- return className.substring(dotIndex + 1);
- }
- }
-
private static void addGetter(StringBuilder builder, String simpleName, String className,
int index) {
builder.append(INDENT)
diff --git a/compiler/src/main/java/android/databinding/tool/writer/FlagSet.java b/compiler/src/main/java/android/databinding/tool/writer/FlagSet.java
index a5e799e..de01e5f 100644
--- a/compiler/src/main/java/android/databinding/tool/writer/FlagSet.java
+++ b/compiler/src/main/java/android/databinding/tool/writer/FlagSet.java
@@ -16,7 +16,6 @@
package android.databinding.tool.writer;
-import java.util.Arrays;
import java.util.BitSet;
/**
@@ -34,7 +33,7 @@
buckets = new long[bucketCount];
for (int i = bitSet.nextSetBit(0);
i != -1; i = bitSet.nextSetBit(i + 1)) {
- buckets[i / sBucketSize] |= 1 << (i % sBucketSize);
+ buckets[i / sBucketSize] |= 1L << (i % sBucketSize);
}
type = "long";
}
@@ -59,7 +58,7 @@
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);
+ buckets[i / sBucketSize] |= 1L << (i % sBucketSize);
}
type = "long";
}
@@ -139,4 +138,32 @@
}
return true;
}
+
+ @Override
+ public int hashCode() {
+ int hash = 1;
+ for (long bucket : buckets) {
+ hash = (hash * 7) ^ (int)(bucket >>> 32);
+ hash = (hash * 13) ^ (int)(bucket & 0xFFFF);
+ }
+ return hash;
+ }
+
+ @Override
+ public boolean equals(Object obj) {
+ if (obj instanceof FlagSet) {
+ FlagSet other = (FlagSet) obj;
+ if (other.buckets.length != buckets.length) {
+ return false;
+ }
+ for (int i = 0; i < buckets.length; i++) {
+ if (buckets[i] != other.buckets[i]) {
+ return false;
+ }
+ }
+ return true;
+ } else {
+ return false;
+ }
+ }
}
diff --git a/compiler/src/main/kotlin/android/databinding/tool/ext/ext.kt b/compiler/src/main/kotlin/android/databinding/tool/ext/ext.kt
index c88f91e..a7f3a1b 100644
--- a/compiler/src/main/kotlin/android/databinding/tool/ext/ext.kt
+++ b/compiler/src/main/kotlin/android/databinding/tool/ext/ext.kt
@@ -15,17 +15,12 @@
import android.databinding.tool.expr.VersionProvider
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
+import kotlin.reflect.KProperty
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)
+ override fun getValue(thisRef: K, property: kotlin.reflect.KProperty<*>): T {
+ val t = mapping[thisRef]
if (t != null) {
return t
}
@@ -37,9 +32,10 @@
private class VersionedLazyExt<K, T>(private val initializer: (k : K) -> T) : ReadOnlyProperty<K, T> {
private val mapping = hashMapOf<K, VersionedResult<T>>()
- override fun get(thisRef: K, desc: PropertyMetadata): T {
- val t = mapping.get(thisRef)
- val version = if(thisRef is VersionProvider) thisRef.getVersion() else 1
+
+ override fun getValue(thisRef: K, property: KProperty<*>): T {
+ val t = mapping[thisRef]
+ val version = if(thisRef is VersionProvider) thisRef.version else 1
if (t != null && version == t.version) {
return t.result
}
@@ -51,19 +47,18 @@
data class VersionedResult<T>(val version : Int, val result : T)
-fun Delegates.lazy<K, T>(initializer: (k : K) -> T): ReadOnlyProperty<K, T> = LazyExt(initializer)
-fun Delegates.versionedLazy<K, T>(initializer: (k : K) -> T): ReadOnlyProperty<K, T> = VersionedLazyExt(initializer)
+fun <K, T> lazyProp(initializer: (k : K) -> T): ReadOnlyProperty<K, T> = LazyExt(initializer)
+fun <K, T> versionedLazy(initializer: (k : K) -> T): ReadOnlyProperty<K, T> = VersionedLazyExt(initializer)
public fun Class<*>.toJavaCode() : String {
- val name = getName();
if (name.startsWith('[')) {
val numArray = name.lastIndexOf('[') + 1;
val componentType : String;
- when (name.charAt(numArray)) {
+ when (name[numArray]) {
'Z' -> componentType = "boolean"
'B' -> componentType = "byte"
'C' -> componentType = "char"
- 'L' -> componentType = name.substring(numArray + 1, name.length() - 1).replace('$', '.');
+ 'L' -> componentType = name.substring(numArray + 1, name.length - 1).replace('$', '.');
'D' -> componentType = "double"
'F' -> componentType = "float"
'I' -> componentType = "int"
@@ -78,19 +73,19 @@
}
}
-public fun String.androidId() : String = this.splitBy("/")[1]
+public fun String.androidId() : String = this.split("/")[1]
public fun String.toCamelCase() : String {
- val split = this.splitBy("_")
- if (split.size() == 0) return ""
- if (split.size() == 1) return split[0].capitalize()
+ 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.splitBy("_")
- if (split.size() == 0) return ""
- if (split.size() == 1) return split[0]
+ val split = this.split("_")
+ if (split.size == 0) return ""
+ if (split.size == 1) return split[0]
return split.joinToCamelCaseAsVar()
}
diff --git a/compiler/src/main/kotlin/android/databinding/tool/ext/list_ext.kt b/compiler/src/main/kotlin/android/databinding/tool/ext/list_ext.kt
index 9126103..4d420dd 100644
--- a/compiler/src/main/kotlin/android/databinding/tool/ext/list_ext.kt
+++ b/compiler/src/main/kotlin/android/databinding/tool/ext/list_ext.kt
@@ -16,29 +16,14 @@
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()) {
+public fun List<String>.joinToCamelCase(): String = when(size) {
0 -> throw IllegalArgumentException("invalid section size, cannot be zero")
- 1 -> this.get(0).toCamelCase()
+ 1 -> this[0].toCamelCase()
else -> this.map {it.toCamelCase()}.joinToString("")
}
-public fun List<String>.joinToCamelCaseAsVar(): String = when(size()) {
+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()
+ 1 -> this[0].toCamelCaseAsVar()
else -> get(0).toCamelCaseAsVar() + drop(1).joinToCamelCase()
}
\ No newline at end of file
diff --git a/compiler/src/main/kotlin/android/databinding/tool/ext/node_ext.kt b/compiler/src/main/kotlin/android/databinding/tool/ext/node_ext.kt
deleted file mode 100644
index 83a34ba..0000000
--- a/compiler/src/main/kotlin/android/databinding/tool/ext/node_ext.kt
+++ /dev/null
@@ -1,56 +0,0 @@
-/*
- * 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/compiler/src/main/kotlin/android/databinding/tool/writer/BRWriter.kt b/compiler/src/main/kotlin/android/databinding/tool/writer/BRWriter.kt
index 9e41c2d..261025d 100644
--- a/compiler/src/main/kotlin/android/databinding/tool/writer/BRWriter.kt
+++ b/compiler/src/main/kotlin/android/databinding/tool/writer/BRWriter.kt
@@ -16,12 +16,12 @@
package android.databinding.tool.writer
-import kotlin.properties.Delegates
+import android.databinding.tool.util.StringUtils
class BRWriter(properties: Set<String>, val useFinal : Boolean) {
- val indexedProps = properties.sort().withIndex()
- public fun write(pkg : String): String = "package $pkg;${System.lineSeparator()}$klass"
- val klass: String by Delegates.lazy {
+ val indexedProps = properties.sorted().withIndex()
+ public fun write(pkg : String): String = "package $pkg;${StringUtils.LINE_SEPARATOR}$klass"
+ val klass: String by lazy {
kcode("") {
val prefix = if (useFinal) "final " else "";
nl("public class BR {") {
@@ -32,4 +32,4 @@
} nl ("}")
}.generate()
}
-}
\ No newline at end of file
+}
diff --git a/compiler/src/main/kotlin/android/databinding/tool/writer/CodeGenUtil.kt b/compiler/src/main/kotlin/android/databinding/tool/writer/CodeGenUtil.kt
deleted file mode 100644
index d48516f..0000000
--- a/compiler/src/main/kotlin/android/databinding/tool/writer/CodeGenUtil.kt
+++ /dev/null
@@ -1,125 +0,0 @@
-/*
- * Copyright (C) 2015 The Android Open Source Project
- * Licensed under the Apache License, Version 2.0 (the "License");
- * you may not use this file except in compliance with the License.
- * You may obtain a copy of the License at
- * http://www.apache.org/licenses/LICENSE-2.0
- * Unless required by applicable law or agreed to in writing, software
- * distributed under the License is distributed on an "AS IS" BASIS,
- * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
- * See the License for the specific language governing permissions and
- * limitations under the License.
- */
-
-package android.databinding.tool.writer
-
-import android.databinding.tool.expr.*
-import android.databinding.tool.reflection.Callable
-
-class CodeGenUtil {
- companion object {
- fun toCode(it : Expr, full : Boolean) : KCode {
- if (it.isDynamic() && !full) {
- return kcode(it.executePendingLocalName)
- }
- return when (it) {
- is ComparisonExpr -> kcode("") {
- app("", it.getLeft().toCode())
- app(" ", it.getOp()).app(" ")
- app("", it.getRight().toCode())
- }
- is InstanceOfExpr -> kcode("") {
- app("", it.getExpr().toCode())
- app(" instanceof ")
- app("", it.getType().toJavaCode())
- }
- is FieldAccessExpr -> kcode("") {
- if (it.isListener()) {
- app("(")
- if (it.getMinApi() > 1) {
- app("", "(getBuildSdkInt() < ${it.getMinApi()}) ? null : ")
- }
- if (it.getChild().isDynamic()) {
- val value = it.getChild().toCode().generate();
- app("", "((${it.fieldName} == null) ? (${it.fieldName} = (new ${it.listenerClassName}()).setValue(${value})) : ${it.fieldName}.setValue(${value}))")
- } else {
- app("", "((${it.fieldName} == null) ? (${it.fieldName} = new ${it.listenerClassName}()) : ${it.fieldName})")
- }
- app(")")
- } else {
- 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.executePendingLocalName)
- is MathExpr -> kcode("") {
- app("", it.getLeft().toCode())
- app(it.getOp())
- app("", it.getRight().toCode())
- }
- is UnaryExpr -> kcode("") {
- app(it.getOp(), it.getExpr().toCode())
- }
- is BitShiftExpr -> 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())
- }
- is ArgListExpr -> throw IllegalStateException("should never try to convert an argument expressions into code");
- else -> kcode("//NOT IMPLEMENTED YET")
- }
- }
- }
-}
\ No newline at end of file
diff --git a/compiler/src/main/kotlin/android/databinding/tool/writer/DataBinderWriter.kt b/compiler/src/main/kotlin/android/databinding/tool/writer/DataBinderWriter.kt
index 9b26c51..da953e4 100644
--- a/compiler/src/main/kotlin/android/databinding/tool/writer/DataBinderWriter.kt
+++ b/compiler/src/main/kotlin/android/databinding/tool/writer/DataBinderWriter.kt
@@ -21,7 +21,7 @@
nl("package $pkg;")
nl("import $projectPackage.BR;")
nl("class $className {") {
- tab("final static int TARGET_MIN_SDK = ${minSdk};")
+ tab("final static int TARGET_MIN_SDK = $minSdk;")
nl("")
tab("public $className() {") {
}
@@ -29,14 +29,14 @@
nl("")
tab("public android.databinding.ViewDataBinding getDataBinder(android.databinding.DataBindingComponent bindingComponent, android.view.View view, int layoutId) {") {
tab("switch(layoutId) {") {
- layoutBinders.groupBy{it.getLayoutname()}.forEach {
- val firstVal = it.value.get(0)
- tab("case ${firstVal.getModulePackage()}.R.layout.${firstVal.getLayoutname()}:") {
- if (it.value.size() == 1) {
- if (firstVal.isMerge()) {
- tab("return new ${firstVal.getPackage()}.${firstVal.getImplementationName()}(bindingComponent, new android.view.View[]{view});")
+ layoutBinders.groupBy{it.layoutname }.forEach {
+ val firstVal = it.value[0]
+ tab("case ${firstVal.modulePackage}.R.layout.${firstVal.layoutname}:") {
+ if (it.value.size == 1) {
+ if (firstVal.isMerge) {
+ tab("return new ${firstVal.`package`}.${firstVal.implementationName}(bindingComponent, new android.view.View[]{view});")
} else {
- tab("return ${firstVal.getPackage()}.${firstVal.getImplementationName()}.bind(view, bindingComponent);")
+ tab("return ${firstVal.`package`}.${firstVal.implementationName}.bind(view, bindingComponent);")
}
} else {
// we should check the tag to decide which layout we need to inflate
@@ -44,14 +44,15 @@
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()}_0\".equals(tag)) {") {
- if (it.isMerge()) {
- tab("return new ${it.getPackage()}.${it.getImplementationName()}(bindingComponent, new android.view.View[]{view});")
+ tab("if (\"${it.tag}_0\".equals(tag)) {") {
+ if (it.isMerge) {
+ tab("return new ${it.`package`}.${it.implementationName}(bindingComponent, new android.view.View[]{view});")
} else {
- tab("return new ${it.getPackage()}.${it.getImplementationName()}(bindingComponent, view);")
+ tab("return new ${it.`package`}.${it.implementationName}(bindingComponent, view);")
}
} tab("}")
}
+ tab("throw new java.lang.IllegalArgumentException(\"The tag for ${firstVal.layoutname} is invalid. Received: \" + tag);");
}tab("}")
}
@@ -65,19 +66,19 @@
tab("android.databinding.ViewDataBinding getDataBinder(android.databinding.DataBindingComponent bindingComponent, android.view.View[] views, int layoutId) {") {
tab("switch(layoutId) {") {
- layoutBinders.filter{it.isMerge()}.groupBy{it.getLayoutname()}.forEach {
- val firstVal = it.value.get(0)
- tab("case ${firstVal.getModulePackage()}.R.layout.${firstVal.getLayoutname()}:") {
- if (it.value.size() == 1) {
- tab("return new ${firstVal.getPackage()}.${firstVal.getImplementationName()}(bindingComponent, views);")
+ layoutBinders.filter{it.isMerge }.groupBy{it.layoutname }.forEach {
+ val firstVal = it.value[0]
+ tab("case ${firstVal.modulePackage}.R.layout.${firstVal.layoutname}:") {
+ if (it.value.size == 1) {
+ tab("return new ${firstVal.`package`}.${firstVal.implementationName}(bindingComponent, views);")
} else {
// we should check the tag to decide which layout we need to inflate
tab("{") {
tab("final Object tag = views[0].getTag();")
tab("if(tag == null) throw new java.lang.RuntimeException(\"view must have a tag\");")
it.value.forEach {
- tab("if (\"${it.getTag()}_0\".equals(tag)) {") {
- tab("return new ${it.getPackage()}.${it.getImplementationName()}(bindingComponent, views);")
+ tab("if (\"${it.tag}_0\".equals(tag)) {") {
+ tab("return new ${it.`package`}.${it.implementationName}(bindingComponent, views);")
} tab("}")
}
}tab("}")
@@ -98,11 +99,11 @@
// String.hashCode is well defined in the API so we can rely on it being the same on the device and the host machine
tab("final int code = tag.hashCode();");
tab("switch(code) {") {
- layoutBinders.groupBy {"${it.getTag()}_0".hashCode()}.forEach {
+ layoutBinders.groupBy {"${it.tag}_0".hashCode()}.forEach {
tab("case ${it.key}:") {
it.value.forEach {
- tab("if(tag.equals(\"${it.getTag()}_0\"))") {
- tab("return ${it.getModulePackage()}.R.layout.${it.getLayoutname()};")
+ tab("if(tag.equals(\"${it.tag}_0\"))") {
+ tab("return ${it.modulePackage}.R.layout.${it.layoutname};")
}
}
tab("break;")
diff --git a/compiler/src/main/kotlin/android/databinding/tool/writer/DynamicUtilWriter.kt b/compiler/src/main/kotlin/android/databinding/tool/writer/DynamicUtilWriter.kt
new file mode 100644
index 0000000..34ac043
--- /dev/null
+++ b/compiler/src/main/kotlin/android/databinding/tool/writer/DynamicUtilWriter.kt
@@ -0,0 +1,23 @@
+package android.databinding.tool.writer;
+
+class DynamicUtilWriter() {
+ public fun write(targetSdk : kotlin.Int) : KCode = kcode("package android.databinding;") {
+ nl("")
+ nl("import android.os.Build.VERSION;")
+ nl("import android.os.Build.VERSION_CODES;")
+ nl("")
+ nl("public class DynamicUtil {")
+ tab("@SuppressWarnings(\"deprecation\")")
+ tab("public static int getColorFromResource(final android.view.View root, final int resourceId) {") {
+ if (targetSdk >= 23) {
+ tab("if (VERSION.SDK_INT >= VERSION_CODES.M) {") {
+ tab("return root.getContext().getColor(resourceId);")
+ }
+ tab("}")
+ }
+ tab("return root.getResources().getColor(resourceId);")
+ }
+ tab("}")
+ nl("}")
+ }
+}
\ No newline at end of file
diff --git a/compiler/src/main/kotlin/android/databinding/tool/writer/KCode.kt b/compiler/src/main/kotlin/android/databinding/tool/writer/KCode.kt
index e1ceb3b..a9b282a 100644
--- a/compiler/src/main/kotlin/android/databinding/tool/writer/KCode.kt
+++ b/compiler/src/main/kotlin/android/databinding/tool/writer/KCode.kt
@@ -13,13 +13,14 @@
package android.databinding.tool.writer
+import android.databinding.tool.util.StringUtils
import java.util.BitSet
class KCode (private val s : String? = null){
private var sameLine = false
- private val lineSeparator = System.getProperty("line.separator")
+ private val lineSeparator = StringUtils.LINE_SEPARATOR
class Appendix(val glue : String, val code : KCode)
@@ -30,14 +31,14 @@
private val indentCache = arrayListOf<String>()
fun indent(n: Int): String {
if (cachedIndentations.get(n)) {
- return indentCache.get(n)
+ return indentCache[n]
}
- val s = (0..n-1).fold(""){prev, next -> "${prev} "}
+ val s = (0..n-1).fold(""){prev, next -> "$prev "}
cachedIndentations.set(n, true )
- while (indentCache.size() <= n) {
+ while (indentCache.size <= n) {
indentCache.add("");
}
- indentCache.set(n, s)
+ indentCache[n] = s
return s
}
}
@@ -54,7 +55,7 @@
return this
}
- fun tab(s : String?, init : (KCode.() -> Unit)? = null) : KCode {
+ infix fun tab(s : String?, init : (KCode.() -> Unit)? = null) : KCode {
val c = KCode(s)
if (init != null) {
c.init()
@@ -62,34 +63,24 @@
return tab(c)
}
- private fun tab(c : KCode?) : KCode {
- if (isNull(c)) {
+ fun tab(c : KCode?) : KCode {
+ if (c == null || 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)) {
+ infix fun nl(c : KCode?) : KCode {
+ if (c == null || isNull(c)) {
return this
}
nodes.add(c)
- c!!.sameLine = true
+ c.sameLine = true
return this
}
- fun nl(s : String?, init : (KCode.() -> Unit)? = null) : KCode {
+ infix fun nl(s : String?, init : (KCode.() -> Unit)? = null) : KCode {
val c = KCode(s)
if (init != null) {
c.init()
@@ -97,16 +88,6 @@
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
@@ -115,7 +96,7 @@
return this
}
- fun app(s : String) : KCode {
+ infix fun app(s : String) : KCode {
val c = KCode(s)
return app("", c)
}
@@ -166,4 +147,4 @@
c.init()
}
return c
-}
\ No newline at end of file
+}
diff --git a/compiler/src/main/kotlin/android/databinding/tool/writer/LayoutBinderWriter.kt b/compiler/src/main/kotlin/android/databinding/tool/writer/LayoutBinderWriter.kt
index d87d8d4..690a4d6 100644
--- a/compiler/src/main/kotlin/android/databinding/tool/writer/LayoutBinderWriter.kt
+++ b/compiler/src/main/kotlin/android/databinding/tool/writer/LayoutBinderWriter.kt
@@ -14,25 +14,27 @@
package android.databinding.tool.writer
import android.databinding.tool.BindingTarget
+import android.databinding.tool.InverseBinding
import android.databinding.tool.LayoutBinder
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.ListenerExpr
+import android.databinding.tool.expr.ResourceExpr
import android.databinding.tool.expr.TernaryExpr
import android.databinding.tool.ext.androidId
import android.databinding.tool.ext.br
import android.databinding.tool.ext.joinToCamelCaseAsVar
-import android.databinding.tool.ext.lazy
+import android.databinding.tool.ext.lazyProp
import android.databinding.tool.ext.versionedLazy
+import android.databinding.tool.processing.ErrorMessages
import android.databinding.tool.reflection.ModelAnalyzer
import android.databinding.tool.util.L
import java.util.ArrayList
import java.util.Arrays
import java.util.BitSet
import java.util.HashMap
-import java.util.HashSet
-import kotlin.properties.Delegates
fun String.stripNonJava() = this.split("[^a-zA-Z0-9]".toRegex()).map{ it.trim() }.joinToCamelCaseAsVar()
@@ -53,150 +55,167 @@
fun localizeFlag(set : FlagSet, name:String) : FlagSet {
localizedFlags.add(set)
- val result = getUniqueName(name, Scope.FLAG)
- set.setLocalName(result)
+ val result = getUniqueName(name, Scope.FLAG, false)
+ set.localName = result
return set
}
- fun getUniqueName(base : String, scope : Scope) : String {
- var candidate = base
- var i = 0
- while (usedFieldNames[scope].contains(candidate)) {
- i ++
- candidate = base + i
+ fun getUniqueName(base : String, scope : Scope, isPublic : kotlin.Boolean) : String {
+ var candidateBase = base
+ if (!isPublic && candidateBase.length > 20) {
+ candidateBase = candidateBase.substring(0, 20);
}
- usedFieldNames[scope].add(candidate)
+ var candidate = candidateBase
+ var i = 0
+ while (usedFieldNames[scope]!!.contains(candidate)) {
+ i ++
+ candidate = candidateBase + i
+ }
+ usedFieldNames[scope]!!.add(candidate)
return candidate
}
}
-val ExprModel.ext by Delegates.lazy { target : ExprModel ->
+val ExprModel.ext by lazyProp { target : ExprModel ->
ExprModelExt()
}
-fun ExprModel.getUniqueFieldName(base : String) : String = ext.getUniqueName(base, Scope.FIELD)
-fun ExprModel.getUniqueMethodName(base : String) : String = ext.getUniqueName(base, Scope.METHOD)
-fun ExprModel.getUniqueFlagName(base : String) : String = ext.getUniqueName(base, Scope.FLAG)
-fun ExprModel.getConstructorParamName(base : String) : String = ext.getUniqueName(base, Scope.CONSTRUCTOR_PARAM)
+fun ExprModel.getUniqueFieldName(base : String, isPublic : kotlin.Boolean) : String = ext.getUniqueName(base, Scope.FIELD, isPublic)
+fun ExprModel.getUniqueMethodName(base : String, isPublic : kotlin.Boolean) : String = ext.getUniqueName(base, Scope.METHOD, isPublic)
+fun ExprModel.getConstructorParamName(base : String) : String = ext.getUniqueName(base, Scope.CONSTRUCTOR_PARAM, false)
fun ExprModel.localizeFlag(set : FlagSet, base : String) : FlagSet = ext.localizeFlag(set, base)
+val Expr.needsLocalField by lazyProp { expr : Expr ->
+ expr.canBeEvaluatedToAVariable() && !(expr.isVariable() && !expr.isUsed) && (expr.isDynamic || expr is ResourceExpr)
+}
+
+
// not necessarily unique. Uniqueness is solved per scope
-val BindingTarget.readableName by Delegates.lazy { target: BindingTarget ->
- if (target.getId() == null) {
- "boundView" + indexFromTag(target.getTag())
+val BindingTarget.readableName by lazyProp { target: BindingTarget ->
+ if (target.id == null) {
+ "boundView" + indexFromTag(target.tag)
} else {
- target.getId().androidId().stripNonJava()
+ target.id.androidId().stripNonJava()
}
}
fun BindingTarget.superConversion(variable : String) : String {
- if (getResolvedType() != null && getResolvedType().extendsViewStub()) {
- return "new android.databinding.ViewStubProxy((android.view.ViewStub) ${variable})"
+ if (resolvedType != null && resolvedType.extendsViewStub()) {
+ return "new android.databinding.ViewStubProxy((android.view.ViewStub) $variable)"
} else {
- return "(${interfaceType}) ${variable}"
+ return "($interfaceClass) $variable"
}
}
-val BindingTarget.fieldName : String by Delegates.lazy { target : BindingTarget ->
+val BindingTarget.fieldName : String by lazyProp { target : BindingTarget ->
val name : String
- if (target.getId() == null) {
+ val isPublic : kotlin.Boolean
+ if (target.id == null) {
name = "m${target.readableName}"
+ isPublic = false
} else {
name = target.readableName
+ isPublic = true
}
- target.getModel().getUniqueFieldName(name)
+ target.model.getUniqueFieldName(name, isPublic)
}
-val BindingTarget.androidId by Delegates.lazy { target : BindingTarget ->
- if (target.getId().startsWith("@android:id/")) {
- "android.R.id.${target.getId().androidId()}"
+val BindingTarget.androidId by lazyProp { target : BindingTarget ->
+ if (target.id.startsWith("@android:id/")) {
+ "android.R.id.${target.id.androidId()}"
} else {
- "R.id.${target.getId().androidId()}"
+ "R.id.${target.id.androidId()}"
}
}
-val BindingTarget.interfaceType by Delegates.lazy { target : BindingTarget ->
- if (target.getResolvedType() != null && target.getResolvedType().extendsViewStub()) {
+val BindingTarget.interfaceClass by lazyProp { target : BindingTarget ->
+ if (target.resolvedType != null && target.resolvedType.extendsViewStub()) {
"android.databinding.ViewStubProxy"
} else {
- target.getInterfaceType()
+ target.interfaceType
}
}
-val BindingTarget.constructorParamName by Delegates.lazy { target : BindingTarget ->
- target.getModel().getConstructorParamName(target.readableName)
+val BindingTarget.constructorParamName by lazyProp { target : BindingTarget ->
+ target.model.getConstructorParamName(target.readableName)
}
// not necessarily unique. Uniqueness is decided per scope
-val Expr.readableName by Delegates.lazy { expr : Expr ->
- val stripped = "${expr.getUniqueKey().stripNonJava()}"
- L.d("readableUniqueName for [%s] %s is %s", System.identityHashCode(expr), expr.getUniqueKey(), stripped)
+val Expr.readableName by lazyProp { expr : Expr ->
+ val stripped = "${expr.uniqueKey.stripNonJava()}"
+ L.d("readableUniqueName for [%s] %s is %s", System.identityHashCode(expr), expr.uniqueKey, stripped)
stripped
}
-val Expr.fieldName by Delegates.lazy { expr : Expr ->
- expr.getModel().getUniqueFieldName("m${expr.readableName.capitalize()}")
+val Expr.fieldName by lazyProp { expr : Expr ->
+ expr.model.getUniqueFieldName("m${expr.readableName.capitalize()}", false)
}
-val Expr.listenerClassName by Delegates.lazy { expr : Expr ->
- expr.getModel().getUniqueFieldName("${expr.readableName.capitalize()}Impl")
+val InverseBinding.fieldName by lazyProp { inverseBinding : InverseBinding ->
+ val targetName = inverseBinding.target.fieldName;
+ val eventName = inverseBinding.eventAttribute.stripNonJava()
+ inverseBinding.model.getUniqueFieldName("$targetName$eventName", false)
}
-val Expr.oldValueName by Delegates.lazy { expr : Expr ->
- expr.getModel().getUniqueFieldName("mOld${expr.readableName.capitalize()}")
+val Expr.listenerClassName by lazyProp { expr : Expr ->
+ expr.model.getUniqueFieldName("${expr.resolvedType.simpleName}Impl", false)
}
-val Expr.executePendingLocalName by Delegates.lazy { expr : Expr ->
- "${expr.getModel().ext.getUniqueName(expr.readableName, Scope.EXECUTE_PENDING_METHOD)}"
+val Expr.oldValueName by lazyProp { expr : Expr ->
+ expr.model.getUniqueFieldName("mOld${expr.readableName.capitalize()}", false)
}
-val Expr.setterName by Delegates.lazy { expr : Expr ->
- expr.getModel().getUniqueMethodName("set${expr.readableName.capitalize()}")
+val Expr.executePendingLocalName by lazyProp { expr : Expr ->
+ if(expr.needsLocalField) "${expr.model.ext.getUniqueName(expr.readableName, Scope.EXECUTE_PENDING_METHOD, false)}"
+ else expr.toCode().generate()
}
-val Expr.onChangeName by Delegates.lazy { expr : Expr ->
- expr.getModel().getUniqueMethodName("onChange${expr.readableName.capitalize()}")
+val Expr.setterName by lazyProp { expr : Expr ->
+ expr.model.getUniqueMethodName("set${expr.readableName.capitalize()}", true)
}
-val Expr.getterName by Delegates.lazy { expr : Expr ->
- expr.getModel().getUniqueMethodName("get${expr.readableName.capitalize()}")
+val Expr.onChangeName by lazyProp { expr : Expr ->
+ expr.model.getUniqueMethodName("onChange${expr.readableName.capitalize()}", false)
}
-val Expr.dirtyFlagName by Delegates.lazy { expr : Expr ->
- expr.getModel().getUniqueFlagName("sFlag${expr.readableName.capitalize()}")
+val Expr.getterName by lazyProp { expr : Expr ->
+ expr.model.getUniqueMethodName("get${expr.readableName.capitalize()}", true)
}
+fun Expr.isVariable() = this is IdentifierExpr && this.isDynamic
-fun Expr.toCode(full : Boolean = false) : KCode = CodeGenUtil.toCode(this, full)
-
-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.dirtyFlagSet by lazyProp { expr : Expr ->
+ FlagSet(expr.invalidFlags, expr.model.flagBucketCount)
}
-val Expr.invalidateFlagSet by Delegates.lazy { expr : Expr ->
- FlagSet(expr.getId())
+val Expr.invalidateFlagSet by lazyProp { expr : Expr ->
+ FlagSet(expr.id)
}
-val Expr.shouldReadFlagSet by Delegates.versionedLazy { expr : Expr ->
- FlagSet(expr.getShouldReadFlags(), expr.getModel().getFlagBucketCount())
+val Expr.shouldReadFlagSet by versionedLazy { expr : Expr ->
+ FlagSet(expr.shouldReadFlags, expr.model.flagBucketCount)
}
-val Expr.conditionalFlags by Delegates.lazy { expr : Expr ->
+val Expr.shouldReadWithConditionalsFlagSet by versionedLazy { expr : Expr ->
+ FlagSet(expr.shouldReadFlagsWithConditionals, expr.model.flagBucketCount)
+}
+
+val Expr.conditionalFlags by lazyProp { expr : Expr ->
arrayListOf(FlagSet(expr.getRequirementFlagIndex(false)),
FlagSet(expr.getRequirementFlagIndex(true)))
}
-val LayoutBinder.requiredComponent by Delegates.lazy { layoutBinder: LayoutBinder ->
- val required = layoutBinder.
- getBindingTargets().
- flatMap { it.getBindings() }.
- firstOrNull { it.getBindingAdapterInstanceClass() != null }
- required?.getBindingAdapterInstanceClass()
+val LayoutBinder.requiredComponent by lazyProp { layoutBinder: LayoutBinder ->
+ val requiredFromBindings = layoutBinder.
+ bindingTargets.
+ flatMap { it.bindings }.
+ firstOrNull { it.bindingAdapterInstanceClass != null }?.bindingAdapterInstanceClass
+ val requiredFromInverse = layoutBinder.
+ bindingTargets.
+ flatMap { it.inverseBindings }.
+ firstOrNull { it.bindingAdapterInstanceClass != null }?.bindingAdapterInstanceClass
+ requiredFromBindings ?: requiredFromInverse
}
fun Expr.getRequirementFlagSet(expected : Boolean) : FlagSet = conditionalFlags[if(expected) 1 else 0]
@@ -209,22 +228,21 @@
}
}
-fun FlagSet.getWordSuffix(wordIndex : Int) : String {
- return if(wordIndex == 0) "" else "_${wordIndex}"
+fun getWordSuffix(wordIndex : Int) : String {
+ return if(wordIndex == 0) "" else "_$wordIndex"
}
fun FlagSet.localValue(bucketIndex : Int) =
- if (getLocalName() == null) binaryCode(bucketIndex)
- else "${getLocalName()}${getWordSuffix(bucketIndex)}"
+ if (localName == null) binaryCode(bucketIndex)
+ else "$localName${getWordSuffix(bucketIndex)}"
fun FlagSet.binaryCode(bucketIndex : Int) = longToBinary(buckets[bucketIndex])
-fun longToBinary(l : Long) =
- "0b${java.lang.Long.toBinaryString(l)}L"
+fun longToBinary(l : Long) = "0x${java.lang.Long.toHexString(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 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
@@ -238,7 +256,7 @@
fun indexFromTag(tag : String) : kotlin.Int {
val startIndex : kotlin.Int
if (tag.startsWith("binding_")) {
- startIndex = "binding_".length();
+ startIndex = "binding_".length;
} else {
startIndex = tag.lastIndexOf('_') + 1
}
@@ -246,56 +264,55 @@
}
class LayoutBinderWriter(val layoutBinder : LayoutBinder) {
- val model = layoutBinder.getModel()
+ val model = layoutBinder.model
val indices = HashMap<BindingTarget, kotlin.Int>()
- val mDirtyFlags by Delegates.lazy {
- val fs = FlagSet(BitSet(), model.getFlagBucketCount());
+ val mDirtyFlags by lazy {
+ val fs = FlagSet(BitSet(), model.flagBucketCount);
Arrays.fill(fs.buckets, -1)
- fs.setDynamic(true)
+ fs.isDynamic = true
model.localizeFlag(fs, "mDirtyFlags")
fs
}
- val dynamics by Delegates.lazy { model.getExprMap().values().filter { it.isDynamic() } }
- val className = layoutBinder.getImplementationName()
+ val className = layoutBinder.implementationName
- val baseClassName = "${layoutBinder.getClassName()}"
+ val baseClassName = "${layoutBinder.className}"
- val includedBinders by Delegates.lazy {
- layoutBinder.getBindingTargets().filter { it.isBinder() }
+ val includedBinders by lazy {
+ layoutBinder.bindingTargets.filter { it.isBinder }
}
- val variables by Delegates.lazy {
- model.getExprMap().values().filterIsInstance(javaClass<IdentifierExpr>()).filter { it.isVariable() }
+ val variables by lazy {
+ model.exprMap.values.filterIsInstance(IdentifierExpr::class.java).filter { it.isVariable() }
}
- val usedVariables by Delegates.lazy {
- variables.filter {it.isUsed()}
+ val usedVariables by lazy {
+ variables.filter {it.isUsed }
}
public fun write(minSdk : kotlin.Int) : String {
layoutBinder.resolveWhichExpressionsAreUsed()
calculateIndices();
- return kcode("package ${layoutBinder.getPackage()};") {
- nl("import ${layoutBinder.getModulePackage()}.R;")
- nl("import ${layoutBinder.getModulePackage()}.BR;")
+ return kcode("package ${layoutBinder.`package`};") {
+ nl("import ${layoutBinder.modulePackage}.R;")
+ nl("import ${layoutBinder.modulePackage}.BR;")
nl("import android.view.View;")
val classDeclaration : String
if (layoutBinder.hasVariations()) {
- classDeclaration = "${className} extends ${baseClassName}"
+ classDeclaration = "$className extends $baseClassName"
} else {
- classDeclaration = "${className} extends android.databinding.ViewDataBinding"
+ classDeclaration = "$className extends android.databinding.ViewDataBinding"
}
- nl("public class ${classDeclaration} {") {
+ nl("public class $classDeclaration {") {
tab(declareIncludeViews())
tab(declareViews())
tab(declareVariables())
tab(declareBoundValues())
tab(declareListeners())
+ tab(declareInverseBindingImpls());
tab(declareConstructor(minSdk))
tab(declareInvalidateAll())
tab(declareHasPendingBindings())
- tab(declareLog())
tab(declareSetVariable())
tab(variableSettersAndGetters())
tab(onFieldChange())
@@ -314,15 +331,15 @@
}.generate()
}
fun calculateIndices() : Unit {
- val taggedViews = layoutBinder.getBindingTargets().filter{
- it.isUsed() && it.getTag() != null && !it.isBinder()
+ val taggedViews = layoutBinder.bindingTargets.filter{
+ it.isUsed && it.tag != null && !it.isBinder
}
taggedViews.forEach {
- indices.put(it, indexFromTag(it.getTag()))
+ indices.put(it, indexFromTag(it.tag))
}
val indexStart = maxIndex() + 1
- layoutBinder.getBindingTargets().filter{
- it.isUsed() && !taggedViews.contains(it)
+ layoutBinder.bindingTargets.filter{
+ it.isUsed && !taggedViews.contains(it)
}.withIndex().forEach {
indices.put(it.value, it.index + indexStart)
}
@@ -331,22 +348,19 @@
nl("private static final android.databinding.ViewDataBinding.IncludedLayouts sIncludes;")
nl("private static final android.util.SparseIntArray sViewsWithIds;")
nl("static {") {
- val hasBinders = layoutBinder.getBindingTargets().firstOrNull{ it.isUsed() && it.isBinder()} != null
+ val hasBinders = layoutBinder.bindingTargets.firstOrNull{ it.isUsed && it.isBinder } != null
if (!hasBinders) {
tab("sIncludes = null;")
} else {
- val numBindings = layoutBinder.getBindingTargets().filter{ it.isUsed() }.count()
- tab("sIncludes = new android.databinding.ViewDataBinding.IncludedLayouts(${numBindings});")
+ val numBindings = layoutBinder.bindingTargets.filter{ it.isUsed }.count()
+ tab("sIncludes = new android.databinding.ViewDataBinding.IncludedLayouts($numBindings);")
val includeMap = HashMap<BindingTarget, ArrayList<BindingTarget>>()
- layoutBinder.getBindingTargets().filter{ it.isUsed() && it.isBinder() }.forEach {
- val includeTag = it.getTag();
- val parent = layoutBinder.getBindingTargets().firstOrNull {
- it.isUsed() && !it.isBinder() && includeTag.equals(it.getTag())
- }
- if (parent == null) {
- throw IllegalStateException("Could not find parent of include file")
- }
- var list = includeMap.get(parent)
+ layoutBinder.bindingTargets.filter{ it.isUsed && it.isBinder }.forEach {
+ val includeTag = it.tag;
+ val parent = layoutBinder.bindingTargets.firstOrNull {
+ it.isUsed && !it.isBinder && includeTag.equals(it.tag)
+ } ?: throw IllegalStateException("Could not find parent of include file")
+ var list = includeMap[parent]
if (list == null) {
list = ArrayList<BindingTarget>()
includeMap.put(parent, list)
@@ -354,36 +368,36 @@
list.add(it)
}
- includeMap.keySet().forEach {
- val index = indices.get(it)
- tab("sIncludes.setIncludes(${index}, ") {
+ includeMap.keys.forEach {
+ val index = indices[it]
+ tab("sIncludes.setIncludes($index, ") {
tab ("new String[] {${
- includeMap.get(it).map {
- "\"${it.getIncludedLayout()}\""
+ includeMap[it]!!.map {
+ "\"${it.includedLayout}\""
}.joinToString(", ")
}},")
tab("new int[] {${
- includeMap.get(it).map {
- "${indices.get(it)}"
+ includeMap[it]!!.map {
+ "${indices[it]}"
}.joinToString(", ")
}},")
tab("new int[] {${
- includeMap.get(it).map {
- "R.layout.${it.getIncludedLayout()}"
+ includeMap[it]!!.map {
+ "R.layout.${it.includedLayout}"
}.joinToString(", ")
}});")
}
}
}
- val viewsWithIds = layoutBinder.getBindingTargets().filter {
- it.isUsed() && !it.isBinder() && (!it.supportsTag() || (it.getId() != null && it.getTag() == null))
+ val viewsWithIds = layoutBinder.bindingTargets.filter {
+ it.isUsed && !it.isBinder && (!it.supportsTag() || (it.id != null && it.tag == null))
}
if (viewsWithIds.isEmpty()) {
tab("sViewsWithIds = null;")
} else {
tab("sViewsWithIds = new android.util.SparseIntArray();")
viewsWithIds.forEach {
- tab("sViewsWithIds.put(${it.androidId}, ${indices.get(it)});")
+ tab("sViewsWithIds.put(${it.androidId}, ${indices[it]});")
}
}
}
@@ -391,7 +405,7 @@
}
fun maxIndex() : kotlin.Int {
- val maxIndex = indices.values().max()
+ val maxIndex = indices.values.max()
if (maxIndex == null) {
return -1
} else {
@@ -403,7 +417,7 @@
val bindingCount = maxIndex() + 1
val parameterType : String
val superParam : String
- if (layoutBinder.isMerge()) {
+ if (layoutBinder.isMerge) {
parameterType = "View[]"
superParam = "root[0]"
} else {
@@ -413,53 +427,56 @@
val rootTagsSupported = minSdk >= 14
if (layoutBinder.hasVariations()) {
nl("")
- nl("public ${className}(android.databinding.DataBindingComponent bindingComponent, ${parameterType} root) {") {
- tab("this(bindingComponent, ${superParam}, mapBindings(bindingComponent, root, ${bindingCount}, sIncludes, sViewsWithIds));")
+ nl("public $className(android.databinding.DataBindingComponent bindingComponent, $parameterType root) {") {
+ tab("this(bindingComponent, $superParam, mapBindings(bindingComponent, root, $bindingCount, sIncludes, sViewsWithIds));")
}
nl("}")
- nl("private ${className}(android.databinding.DataBindingComponent bindingComponent, ${parameterType} root, Object[] bindings) {") {
- tab("super(bindingComponent, ${superParam}, ${model.getObservables().size()}") {
- layoutBinder.getSortedTargets().filter { it.getId() != null }.forEach {
+ nl("private $className(android.databinding.DataBindingComponent bindingComponent, $parameterType root, Object[] bindings) {") {
+ tab("super(bindingComponent, $superParam, ${model.observables.size}") {
+ layoutBinder.sortedTargets.filter { it.id != null }.forEach {
tab(", ${fieldConversion(it)}")
}
tab(");")
}
}
} else {
- nl("public ${baseClassName}(android.databinding.DataBindingComponent bindingComponent, ${parameterType} root) {") {
- tab("super(bindingComponent, ${superParam}, ${model.getObservables().size()});")
- tab("final Object[] bindings = mapBindings(bindingComponent, root, ${bindingCount}, sIncludes, sViewsWithIds);")
+ nl("public $baseClassName(android.databinding.DataBindingComponent bindingComponent, $parameterType root) {") {
+ tab("super(bindingComponent, $superParam, ${model.observables.size});")
+ tab("final Object[] bindings = mapBindings(bindingComponent, root, $bindingCount, sIncludes, sViewsWithIds);")
}
}
if (layoutBinder.requiredComponent != null) {
tab("ensureBindingComponentIsNotNull(${layoutBinder.requiredComponent}.class);")
}
- val taggedViews = layoutBinder.getSortedTargets().filter{it.isUsed()}
+ val taggedViews = layoutBinder.sortedTargets.filter{it.isUsed }
taggedViews.forEach {
- if (!layoutBinder.hasVariations() || it.getId() == null) {
+ if (!layoutBinder.hasVariations() || it.id == null) {
tab("this.${it.fieldName} = ${fieldConversion(it)};")
}
- if (!it.isBinder()) {
- if (it.getResolvedType() != null && it.getResolvedType().extendsViewStub()) {
+ if (!it.isBinder) {
+ if (it.resolvedType != null && it.resolvedType.extendsViewStub()) {
tab("this.${it.fieldName}.setContainingBinding(this);")
}
- if (it.supportsTag() && it.getTag() != null &&
- (rootTagsSupported || it.getTag().startsWith("binding_"))) {
- val originalTag = it.getOriginalTag();
+ if (it.supportsTag() && it.tag != null &&
+ (rootTagsSupported || it.tag.startsWith("binding_"))) {
+ val originalTag = it.originalTag;
var tagValue = "null"
- if (originalTag != null) {
- tagValue = "\"${originalTag}\""
+ if (originalTag != null && !originalTag.startsWith("@{")) {
+ tagValue = "\"$originalTag\""
if (originalTag.startsWith("@")) {
- var packageName = layoutBinder.getModulePackage()
+ var packageName = layoutBinder.modulePackage
if (originalTag.startsWith("@android:")) {
packageName = "android"
}
val slashIndex = originalTag.indexOf('/')
val resourceId = originalTag.substring(slashIndex + 1)
- tagValue = "root.getResources().getString(${packageName}.R.string.${resourceId})"
+ tagValue = "root.getResources().getString($packageName.R.string.$resourceId)"
}
}
- tab("this.${it.fieldName}.setTag(${tagValue});")
+ tab("this.${it.fieldName}.setTag($tagValue);")
+ } else if (it.tag != null && !it.tag.startsWith("binding_") &&
+ it.originalTag != null) {
+ L.e(ErrorMessages.ROOT_TAG_NOT_SUPPORTED, it.originalTag)
}
}
}
@@ -469,14 +486,11 @@
}
fun fieldConversion(target : BindingTarget) : String {
- if (!target.isUsed()) {
+ if (!target.isUsed) {
return "null"
} else {
- val index = indices.get(target)
- if (index == null) {
- throw IllegalStateException("Unknown binding target")
- }
- val variableName = "bindings[${index}]"
+ val index = indices[target] ?: throw IllegalStateException("Unknown binding target")
+ val variableName = "bindings[$index]"
return target.superConversion(variableName)
}
}
@@ -484,14 +498,14 @@
fun declareInvalidateAll() = kcode("") {
nl("@Override")
nl("public void invalidateAll() {") {
- val fs = FlagSet(layoutBinder.getModel().getInvalidateAnyBitSet(),
- layoutBinder.getModel().getFlagBucketCount());
+ val fs = FlagSet(layoutBinder.model.invalidateAnyBitSet,
+ layoutBinder.model.flagBucketCount);
tab("synchronized(this) {") {
- for (i in (0..(mDirtyFlags.buckets.size() - 1))) {
+ for (i in (0..(mDirtyFlags.buckets.size - 1))) {
tab("${mDirtyFlags.localValue(i)} = ${fs.localValue(i)};")
}
} tab("}")
- includedBinders.filter{it.isUsed()}.forEach { binder ->
+ includedBinders.filter{it.isUsed }.forEach { binder ->
tab("${binder.fieldName}.invalidateAll();")
}
tab("requestRebind();");
@@ -502,19 +516,19 @@
fun declareHasPendingBindings() = kcode("") {
nl("@Override")
nl("public boolean hasPendingBindings() {") {
- if (mDirtyFlags.buckets.size() > 0) {
+ if (mDirtyFlags.buckets.size > 0) {
tab("synchronized(this) {") {
- val flagCheck = 0.rangeTo(mDirtyFlags.buckets.size() - 1).map {
+ val flagCheck = 0.rangeTo(mDirtyFlags.buckets.size - 1).map {
"${mDirtyFlags.localValue(it)} != 0"
}.joinToString(" || ")
- tab("if (${flagCheck}) {") {
+ tab("if ($flagCheck) {") {
tab("return true;")
}
tab("}")
}
tab("}")
}
- includedBinders.filter{it.isUsed()}.forEach { binder ->
+ includedBinders.filter{it.isUsed }.forEach { binder ->
tab("if (${binder.fieldName}.hasPendingBindings()) {") {
tab("return true;")
}
@@ -529,11 +543,19 @@
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 ("case ${it.name.br()} :") {
+ tab("${it.setterName}((${it.resolvedType.toJavaCode()}) variable);")
tab("return true;")
}
}
+ val declaredOnly = variables.filter { !it.isUsed && it.isDeclared };
+ declaredOnly.forEachIndexed { i, identifierExpr ->
+ tab ("case ${identifierExpr.name.br()} :") {
+ if (i == declaredOnly.size - 1) {
+ tab("return true;")
+ }
+ }
+ }
}
tab("}")
tab("return false;")
@@ -541,44 +563,41 @@
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.readableName}) {") {
+ variables.filterNot{it.isUsed }.forEach {
+ nl("public void ${it.setterName}(${it.resolvedType.toJavaCode()} ${it.readableName}) {") {
tab("// not used, ignore")
}
nl("}")
nl("")
- nl("public ${it.getResolvedType().toJavaCode()} ${it.getterName}() {") {
- tab("return ${it.getDefaultValue()};")
+ nl("public ${it.resolvedType.toJavaCode()} ${it.getterName}() {") {
+ tab("return ${it.defaultValue};")
}
nl("}")
}
usedVariables.forEach {
- if (it.getUserDefinedType() != null) {
- nl("public void ${it.setterName}(${it.getResolvedType().toJavaCode()} ${it.readableName}) {") {
- if (it.isObservable()) {
- tab("updateRegistration(${it.getId()}, ${it.readableName});");
+ if (it.userDefinedType != null) {
+ nl("public void ${it.setterName}(${it.resolvedType.toJavaCode()} ${it.readableName}) {") {
+ if (it.isObservable) {
+ tab("updateRegistration(${it.id}, ${it.readableName});");
}
tab("this.${it.fieldName} = ${it.readableName};")
// set dirty flags!
val flagSet = it.invalidateFlagSet
tab("synchronized(this) {") {
mDirtyFlags.mapOr(flagSet) { suffix, index ->
- tab("${mDirtyFlags.getLocalName()}$suffix |= ${flagSet.localValue(index)};")
+ tab("${mDirtyFlags.localName}$suffix |= ${flagSet.localValue(index)};")
}
} tab ("}")
+ // TODO: Remove this condition after releasing version 1.1 of SDK
+ if (ModelAnalyzer.getInstance().findClass("android.databinding.ViewDataBinding", null).isObservable) {
+ tab("notifyPropertyChanged(${it.name.br()});")
+ }
tab("super.requestRebind();")
}
nl("}")
nl("")
- nl("public ${it.getResolvedType().toJavaCode()} ${it.getterName}() {") {
+ nl("public ${it.resolvedType.toJavaCode()} ${it.getterName}() {") {
tab("return ${it.fieldName};")
}
nl("}")
@@ -590,9 +609,9 @@
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);")
+ model.observables.forEach {
+ tab("case ${it.id} :") {
+ tab("return ${it.onChangeName}((${it.resolvedType.toJavaCode()}) object, fieldId);")
}
}
}
@@ -602,18 +621,22 @@
nl("}")
nl("")
- model.getObservables().forEach {
- nl("private boolean ${it.onChangeName}(${it.getResolvedType().toJavaCode()} ${it.readableName}, int fieldId) {") {
+ model.observables.forEach {
+ nl("private boolean ${it.onChangeName}(${it.resolvedType.toJavaCode()} ${it.readableName}, int fieldId) {") {
tab("switch (fieldId) {", {
- val accessedFields: List<FieldAccessExpr> = it.getParents().filterIsInstance(javaClass<FieldAccessExpr>())
+ val accessedFields: List<FieldAccessExpr> = it.parents.filterIsInstance(FieldAccessExpr::class.java)
accessedFields.filter { it.hasBindableAnnotations() }
- .groupBy { it.getName() }
+ .groupBy { it.brName }
.forEach {
- tab("case ${it.key.br()}:") {
- val field = it.value.first()
+ // If two expressions look different but resolve to the same method,
+ // we are not yet able to merge them. This is why we merge their
+ // flags below.
+ tab("case ${it.key}:") {
tab("synchronized(this) {") {
- mDirtyFlags.mapOr(field.invalidateFlagSet) { suffix, index ->
- tab("${mDirtyFlags.localValue(index)} |= ${field.invalidateFlagSet.localValue(index)};")
+ val flagSet = it.value.foldRight(FlagSet()) { l, r -> l.invalidateFlagSet.or(r) }
+
+ mDirtyFlags.mapOr(flagSet) { suffix, index ->
+ tab("${mDirtyFlags.localValue(index)} |= ${flagSet.localValue(index)};")
}
} tab("}")
tab("return true;")
@@ -624,7 +647,7 @@
val flagSet = it.invalidateFlagSet
tab("synchronized(this) {") {
mDirtyFlags.mapOr(flagSet) { suffix, index ->
- tab("${mDirtyFlags.getLocalName()}$suffix |= ${flagSet.localValue(index)};")
+ tab("${mDirtyFlags.localName}$suffix |= ${flagSet.localValue(index)};")
}
} tab("}")
tab("return true;")
@@ -641,57 +664,80 @@
fun declareViews() = kcode("// views") {
val oneLayout = !layoutBinder.hasVariations();
- layoutBinder.getSortedTargets().filter {it.isUsed() && (oneLayout || it.getId() == null)}.forEach {
+ layoutBinder.sortedTargets.filter {it.isUsed && (oneLayout || it.id == null)}.forEach {
val access : String
- if (oneLayout && it.getId() != null) {
+ if (oneLayout && it.id != null) {
access = "public"
} else {
access = "private"
}
- nl("${access} final ${it.interfaceType} ${it.fieldName};")
+ nl("$access final ${it.interfaceClass} ${it.fieldName};")
}
}
fun declareVariables() = kcode("// variables") {
usedVariables.forEach {
- nl("private ${it.getResolvedType().toJavaCode()} ${it.fieldName};")
+ nl("private ${it.resolvedType.toJavaCode()} ${it.fieldName};")
}
}
fun declareBoundValues() = kcode("// values") {
- layoutBinder.getSortedTargets().filter { it.isUsed() }
- .flatMap { it.getBindings() }
+ layoutBinder.sortedTargets.filter { it.isUsed }
+ .flatMap { it.bindings }
.filter { it.requiresOldValue() }
- .flatMap{ it.getComponentExpressions().toArrayList() }
+ .flatMap{ it.componentExpressions.toArrayList() }
.groupBy { it }
.forEach {
- val expr = it.getKey()
- nl("private ${expr.getResolvedType().toJavaCode()} ${expr.oldValueName};")
+ val expr = it.key
+ nl("private ${expr.resolvedType.toJavaCode()} ${expr.oldValueName};")
}
}
fun declareListeners() = kcode("// listeners") {
- model.getExprMap().values().filter {
- it is FieldAccessExpr && it.isListener()
+ model.exprMap.values.filter {
+ it is ListenerExpr
}.groupBy { it }.forEach {
- val expr = it.key as FieldAccessExpr
+ val expr = it.key as ListenerExpr
nl("private ${expr.listenerClassName} ${expr.fieldName};")
}
}
+ fun declareInverseBindingImpls() = kcode("// Inverse Binding Event Handlers") {
+ layoutBinder.sortedTargets.filter { it.isUsed }.forEach { target ->
+ target.inverseBindings.forEach { inverseBinding ->
+ val className : String
+ val param : String
+ if (inverseBinding.isOnBinder) {
+ className = "android.databinding.ViewDataBinding.PropertyChangedInverseListener"
+ param = "BR.${inverseBinding.eventAttribute}"
+ } else {
+ className = "android.databinding.InverseBindingListener"
+ param = ""
+ }
+ nl("private $className ${inverseBinding.fieldName} = new $className($param) {") {
+ tab("@Override")
+ tab("public void onChange() {") {
+ tab(inverseBinding.toJavaCode("mBindingComponent", mDirtyFlags)).app(";");
+ }
+ tab("}")
+ }
+ nl("};")
+ }
+ }
+ }
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)};")
+ app(" ", if(flag.isDynamic) null else "static final");
+ app(" ", " ${flag.type} ${flag.localName}$suffix = ${longToBinary(value)};")
}
}
}
fun flagMapping() = kcode("/* flag mapping") {
- if (model.getFlagMapping() != null) {
- val mapping = model.getFlagMapping()
+ if (model.flagMapping != null) {
+ val mapping = model.flagMapping
for (i in mapping.indices) {
tab("flag $i: ${mapping[i]}")
}
@@ -703,98 +749,100 @@
nl("@Override")
nl("protected void executeBindings() {") {
val tmpDirtyFlags = FlagSet(mDirtyFlags.buckets)
- tmpDirtyFlags.setLocalName("dirtyFlags");
- for (i in (0..mDirtyFlags.buckets.size() - 1)) {
+ tmpDirtyFlags.localName = "dirtyFlags";
+ for (i in (0..mDirtyFlags.buckets.size - 1)) {
tab("${tmpDirtyFlags.type} ${tmpDirtyFlags.localValue(i)} = 0;")
}
tab("synchronized(this) {") {
- for (i in (0..mDirtyFlags.buckets.size() - 1)) {
+ for (i in (0..mDirtyFlags.buckets.size - 1)) {
tab("${tmpDirtyFlags.localValue(i)} = ${mDirtyFlags.localValue(i)};")
tab("${mDirtyFlags.localValue(i)} = 0;")
}
} tab("}")
- model.getPendingExpressions().filterNot {!it.canBeEvaluatedToAVariable() || (it.isVariable() && !it.isUsed())}.forEach {
- tab("${it.getResolvedType().toJavaCode()} ${it.executePendingLocalName} = ${if(it.isVariable()) it.fieldName else it.getDefaultValue()};")
+ model.pendingExpressions.filter { it.needsLocalField }.forEach {
+ tab("${it.resolvedType.toJavaCode()} ${it.executePendingLocalName} = ${if (it.isVariable()) it.fieldName else it.defaultValue};")
}
L.d("writing executePendingBindings for %s", className)
do {
- val batch = ExprModel.filterShouldRead(model.getPendingExpressions()).toArrayList()
+ val batch = ExprModel.filterShouldRead(model.pendingExpressions).toArrayList()
+ val justRead = arrayListOf<Expr>()
L.d("batch: %s", batch)
- val mJustRead = arrayListOf<Expr>()
while (!batch.none()) {
- val readNow = batch.filter { it.shouldReadNow(mJustRead) }
+ val readNow = batch.filter { it.shouldReadNow(justRead) }
if (readNow.isEmpty()) {
throw IllegalStateException("do not know what I can read. bailing out ${batch.joinToString("\n")}")
}
- L.d("new read now. batch size: %d, readNow size: %d", batch.size(), readNow.size())
-
- readNow.forEach {
- nl(readWithDependants(it, mJustRead, batch, tmpDirtyFlags))
- }
- batch.removeAll(mJustRead)
+ L.d("new read now. batch size: %d, readNow size: %d", batch.size, readNow.size)
+ nl(readWithDependants(readNow, justRead, batch, tmpDirtyFlags))
+ batch.removeAll(justRead)
}
tab("// batch finished")
- } while(model.markBitsRead())
+ } while (model.markBitsRead())
// verify everything is read.
- val batch = ExprModel.filterShouldRead(model.getPendingExpressions()).toArrayList()
+ val batch = ExprModel.filterShouldRead(model.pendingExpressions).toArrayList()
if (batch.isNotEmpty()) {
L.e("could not generate code for %s. This might be caused by circular dependencies."
- + "Please report on b.android.com", layoutBinder.getLayoutname())
+ + "Please report on b.android.com. %d %s %s", layoutBinder.layoutname,
+ batch.size, batch[0], batch[0].toCode().generate())
}
//
- layoutBinder.getSortedTargets().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(" || ")
- }) {") {
+ layoutBinder.sortedTargets.filter { it.isUsed }
+ .flatMap { it.bindings }
+ .groupBy {
+ "${tmpDirtyFlags.mapOr(it.expr.dirtyFlagSet) { suffix, index ->
+ "(${tmpDirtyFlags.localValue(index)} & ${it.expr.dirtyFlagSet.localValue(index)}) != 0"
+ }.joinToString(" || ") }"
+ }.forEach {
+ tab("if (${it.key}) {") {
+ it.value.groupBy { Math.max(1, it.minApi) }.forEach {
+ val setterValues = kcode("") {
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}"
+ val fieldName: String
+ if (binding.target.viewClass.
+ equals(binding.target.interfaceType)) {
+ fieldName = "this.${binding.target.fieldName}"
} else {
- fieldName = "((${binding.getTarget().getViewClass()}) this.${binding.getTarget().fieldName})"
+ fieldName = "((${binding.target.viewClass}) this.${binding.target.fieldName})"
}
- val bindingCode = binding.toJavaCode(fieldName, "this.mBindingComponent")
- if (binding.getMinApi() > 1) {
- tab("if(getBuildSdkInt() >= ${binding.getMinApi()}) {") {
- tab("$bindingCode;")
- }
- tab("}")
- } else {
- tab("$bindingCode;")
- }
+ tab(binding.toJavaCode(fieldName, "this.mBindingComponent")).app(";")
}
}
- tab("}")
+ tab("// api target ${it.key}")
+ if (it.key > 1) {
+ tab("if(getBuildSdkInt() >= ${it.key}) {") {
+ app("", setterValues)
+ }
+ tab("}")
+ } else {
+ app("", setterValues)
+ }
}
+ }
+ tab("}")
+ }
- layoutBinder.getSortedTargets().filter { it.isUsed() }
- .flatMap { it.getBindings() }
+
+ layoutBinder.sortedTargets.filter { it.isUsed }
+ .flatMap { it.bindings }
.filter { it.requiresOldValue() }
- .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.first().getComponentExpressions().forEach { expr ->
- tab("this.${expr.oldValueName} = ${expr.toCode(false).generate()};")
- }
+ .groupBy {"${tmpDirtyFlags.mapOr(it.expr.dirtyFlagSet) { suffix, index ->
+ "(${tmpDirtyFlags.localValue(index)} & ${it.expr.dirtyFlagSet.localValue(index)}) != 0"
+ }.joinToString(" || ")
+ }"}.forEach {
+ tab("if (${it.key}) {") {
+ it.value.groupBy { it.expr }.map { it.value.first() }.forEach {
+ it.componentExpressions.forEach { expr ->
+ tab("this.${expr.oldValueName} = ${expr.toCode().generate()};")
}
- tab("}")
}
- includedBinders.filter{it.isUsed()}.forEach { binder ->
+ }
+ tab("}")
+ }
+ includedBinders.filter{it.isUsed }.forEach { binder ->
tab("${binder.fieldName}.executePendingBindings();")
}
- layoutBinder.getSortedTargets().filter{
- it.isUsed() && it.getResolvedType() != null && it.getResolvedType().extendsViewStub()
+ layoutBinder.sortedTargets.filter{
+ it.isUsed && it.resolvedType != null && it.resolvedType.extendsViewStub()
}.forEach {
tab("if (${it.fieldName}.getBinding() != null) {") {
tab("${it.fieldName}.getBinding().executePendingBindings();")
@@ -805,182 +853,234 @@
nl("}")
}
- fun readWithDependants(expr : Expr, mJustRead : MutableList<Expr>, batch : MutableList<Expr>,
- tmpDirtyFlags : FlagSet, inheritedFlags : FlagSet? = null) : KCode = kcode("") {
- mJustRead.add(expr)
- L.d("%s / readWithDependants %s", className, expr.getUniqueKey());
- val flagSet = expr.shouldReadFlagSet
- val needsIfWrapper = inheritedFlags == null || !flagSet.bitsEqual(inheritedFlags)
- L.d("flag set:%s . inherited flags: %s. need another if: %s", flagSet, inheritedFlags, needsIfWrapper);
- val ifClause = "if (${tmpDirtyFlags.mapOr(flagSet){ suffix, index ->
- "(${tmpDirtyFlags.localValue(index)} & ${flagSet.localValue(index)}) != 0"
- }.joinToString(" || ")
- })"
-
- val readCode = kcode("") {
- if (expr.canBeEvaluatedToAVariable() && !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.executePendingLocalName} != null" }.joinToString(" && ")}) {") {
- tab("${expr.executePendingLocalName}").app(" = ", expr.toCode(true)).app(";")
+ fun readWithDependants(expressionList: List<Expr>, justRead: MutableList<Expr>,
+ batch: MutableList<Expr>, tmpDirtyFlags: FlagSet,
+ inheritedFlags: FlagSet? = null) : KCode = kcode("") {
+ expressionList.groupBy { it.shouldReadFlagSet }.forEach {
+ val flagSet = it.key
+ val needsIfWrapper = inheritedFlags == null || !flagSet.bitsEqual(inheritedFlags)
+ val expressions = it.value
+ val ifClause = "if (${tmpDirtyFlags.mapOr(flagSet){ suffix, index ->
+ "(${tmpDirtyFlags.localValue(index)} & ${flagSet.localValue(index)}) != 0"
+ }.joinToString(" || ")
+ })"
+ val readCode = kcode("") {
+ val dependants = ArrayList<Expr>()
+ expressions.groupBy { condition(it) }.forEach {
+ val condition = it.key
+ val assignedValues = it.value.filter { it.needsLocalField && !it.isVariable() }
+ if (!assignedValues.isEmpty()) {
+ val assignment = kcode("") {
+ assignedValues.forEach { expr: Expr ->
+ tab("// read ${expr.uniqueKey}")
+ tab("${expr.executePendingLocalName}").app(" = ", expr.toFullCode()).app(";")
+ }
+ }
+ if (condition != null) {
+ tab("if ($condition) {") {
+ app("", assignment)
+ }
+ tab ("}")
+ } else {
+ app("", assignment)
+ }
+ it.value.filter { it.isObservable }.forEach { expr: Expr ->
+ tab("updateRegistration(${expr.id}, ${expr.executePendingLocalName});")
+ }
}
- tab("}")
- } else {
- tab("${expr.executePendingLocalName}").app(" = ", expr.toCode(true)).app(";")
- }
- if (expr.isObservable()) {
- tab("updateRegistration(${expr.getId()}, ${expr.executePendingLocalName});")
- }
- }
- // 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.executePendingLocalName}) {") {
- conditionals.forEach {
- val set = it.getRequirementFlagSet(true)
- mDirtyFlags.mapOr(set) { suffix , index ->
- tab("${tmpDirtyFlags.localValue(index)} |= ${set.localValue(index)};")
+ it.value.forEach { expr: Expr ->
+ justRead.add(expr)
+ L.d("%s / readWithDependants %s", className, expr.uniqueKey);
+ L.d("flag set:%s . inherited flags: %s. need another if: %s", flagSet, inheritedFlags, needsIfWrapper);
+
+ // if I am the condition for an expression, set its flag
+ expr.dependants.filter {
+ !it.isConditional && it.dependant is TernaryExpr &&
+ (it.dependant as TernaryExpr).pred == expr
+ }.map { it.dependant }.groupBy {
+ // group by when those ternaries will be evaluated (e.g. don't set conditional flags for no reason)
+ val ternaryBitSet = it.shouldReadFlagsWithConditionals
+ val isBehindTernary = ternaryBitSet.nextSetBit(model.invalidateAnyFlagIndex) == -1
+ if (!isBehindTernary) {
+ val ternaryFlags = it.shouldReadWithConditionalsFlagSet
+ "if(${tmpDirtyFlags.mapOr(ternaryFlags){ suffix, index ->
+ "(${tmpDirtyFlags.localValue(index)} & ${ternaryFlags.localValue(index)}) != 0"
+ }.joinToString(" || ")}) {"
+ } else {
+ // TODO if it is behind a ternary, we should set it when its predicate is elevated
+ // Normally, this would mean that there is another code path to re-read our current expression.
+ // Unfortunately, this may not be true due to the coverage detection in `expr#markAsReadIfDone`, this may never happen.
+ // for v1.0, we'll go with always setting it and suffering an unnecessary calculation for this edge case.
+ // we can solve this by listening to elevation events from the model.
+ ""
+ }
+ }.forEach {
+ val hasAnotherIf = it.key != ""
+ if (hasAnotherIf) {
+ tab(it.key) {
+ tab("if (${expr.executePendingLocalName}) {") {
+ it.value.forEach {
+ val set = it.getRequirementFlagSet(true)
+ mDirtyFlags.mapOr(set) { suffix, index ->
+ tab("${tmpDirtyFlags.localValue(index)} |= ${set.localValue(index)};")
+ }
+ }
+ }
+ tab("} else {") {
+ it.value.forEach {
+ val set = it.getRequirementFlagSet(false)
+ mDirtyFlags.mapOr(set) { suffix, index ->
+ tab("${tmpDirtyFlags.localValue(index)} |= ${set.localValue(index)};")
+ }
+ }
+ }.tab("}")
+ }.app("}")
+ } else {
+ tab("if (${expr.executePendingLocalName}) {") {
+ it.value.forEach {
+ val set = it.getRequirementFlagSet(true)
+ mDirtyFlags.mapOr(set) { suffix, index ->
+ tab("${tmpDirtyFlags.localValue(index)} |= ${set.localValue(index)};")
+ }
+ }
+ }
+ tab("} else {") {
+ it.value.forEach {
+ val set = it.getRequirementFlagSet(false)
+ mDirtyFlags.mapOr(set) { suffix, index ->
+ tab("${tmpDirtyFlags.localValue(index)} |= ${set.localValue(index)};")
+ }
+ }
+ } app("}")
+ }
+ }
+ val chosen = expr.dependants.filter {
+ val dependant = it.dependant
+ batch.contains(dependant) &&
+ dependant.shouldReadFlagSet.andNot(flagSet).isEmpty &&
+ dependant.shouldReadNow(justRead)
+ }
+ if (chosen.isNotEmpty()) {
+ dependants.addAll(chosen.map { it.dependant })
}
}
}
- tab("} else {") {
- conditionals.forEach {
- val set = it.getRequirementFlagSet(false)
- mDirtyFlags.mapOr(set) { suffix , index ->
- tab("${tmpDirtyFlags.localValue(index)} |= ${set.localValue(index)};")
- }
- }
- } tab("}")
+ if (dependants.isNotEmpty()) {
+ val nextInheritedFlags = if (needsIfWrapper) flagSet else inheritedFlags
+ nl(readWithDependants(dependants, justRead, batch, tmpDirtyFlags, nextInheritedFlags))
+ }
}
- 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(" {")
+ app("", readCode)
}
+ tab("}")
+ } else {
+ app("", readCode)
}
}
- if (needsIfWrapper) {
- tab(ifClause) {
- app(" {")
- nl(readCode)
+ }
+
+ fun condition(expr : Expr) : String? {
+ if (expr.canBeEvaluatedToAVariable() && !expr.isVariable()) {
+ // create an if case for all dependencies that might be null
+ val nullables = expr.dependencies.filter {
+ it.isMandatory && it.other.resolvedType.isNullable
+ }.map { it.other }
+ if (!expr.isEqualityCheck && nullables.isNotEmpty()) {
+ return "${nullables.map { "${it.executePendingLocalName} != null" }.joinToString(" && ")}"
+ } else {
+ return null
}
- tab("}")
} else {
- nl(readCode)
+ return null
}
}
fun declareListenerImpls() = kcode("// Listener Stub Implementations") {
- model.getExprMap().values().filter {
- it.isUsed() && it is FieldAccessExpr && it.isListener()
+ model.exprMap.values.filter {
+ it.isUsed && it is ListenerExpr
}.groupBy { it }.forEach {
- val expr = it.key as FieldAccessExpr
- val listeners = expr.getListenerTypes()
- val extends = listeners.firstOrNull{ !it.isInterface() }
- val extendsImplements = StringBuilder()
- if (extends != null) {
- extendsImplements.append("extends ${extends.toJavaCode()} ");
+ val expr = it.key as ListenerExpr
+ val listenerType = expr.resolvedType;
+ val extendsImplements : String
+ if (listenerType.isInterface) {
+ extendsImplements = "implements"
+ } else {
+ extendsImplements = "extends"
}
- val implements = expr.getListenerTypes().filter{ it.isInterface() }.map {
- it.toJavaCode()
- }.joinToString(", ")
- if (!implements.isEmpty()) {
- extendsImplements.append("implements ${implements}")
- }
- nl("public static class ${expr.listenerClassName} ${extendsImplements} {") {
- tab("public ${expr.listenerClassName}() {}")
- if (expr.getChild().isDynamic()) {
- tab("private ${expr.getChild().getResolvedType().toJavaCode()} value;")
- tab("public ${expr.listenerClassName} setValue(${expr.getChild().getResolvedType().toJavaCode()} value) {") {
+ nl("public static class ${expr.listenerClassName} $extendsImplements ${listenerType.canonicalName}{") {
+ if (expr.child.isDynamic) {
+ tab("private ${expr.child.resolvedType.toJavaCode()} value;")
+ tab("public ${expr.listenerClassName} setValue(${expr.child.resolvedType.toJavaCode()} value) {") {
tab("this.value = value;")
tab("return value == null ? null : this;")
}
tab("}")
}
- val signatures = HashSet<String>()
- expr.getListenerMethods().withIndex().forEach {
- val listener = it.value
- val calledMethod = expr.getCalledMethods().get(it.index)
- val parameterTypes = listener.getParameterTypes()
- val returnType = listener.getReturnType(parameterTypes.toArrayList())
- val signature = "public ${returnType} ${listener.getName()}(${
+ val listenerMethod = expr.method
+ val parameterTypes = listenerMethod.parameterTypes
+ val returnType = listenerMethod.getReturnType(parameterTypes.toArrayList())
+ tab("@Override")
+ tab("public $returnType ${listenerMethod.name}(${
parameterTypes.withIndex().map {
"${it.value.toJavaCode()} arg${it.index}"
}.joinToString(", ")
- }) {"
- if (!signatures.contains(signature)) {
- signatures.add(signature)
- tab("@Override")
- tab(signature) {
- val obj : String
- if (expr.getChild().isDynamic()) {
- obj = "this.value"
- } else {
- obj = expr.getChild().toCode(false).generate();
- }
- val returnStr : String
- if (!returnType.isVoid()) {
- returnStr = "return "
- } else {
- returnStr = ""
- }
- val args = parameterTypes.withIndex().map {
- "arg${it.index}"
- }.joinToString(", ")
- tab("${returnStr}${obj}.${calledMethod.getName()}(${args});")
- }
- tab("}")
+ }) {") {
+ val obj : String
+ if (expr.child.isDynamic) {
+ obj = "this.value"
+ } else {
+ obj = expr.child.toCode().generate();
}
+ val returnStr : String
+ if (!returnType.isVoid) {
+ returnStr = "return "
+ } else {
+ returnStr = ""
+ }
+ val args = parameterTypes.withIndex().map {
+ "arg${it.index}"
+ }.joinToString(", ")
+ tab("$returnStr$obj.${expr.name}($args);")
}
+ tab("}")
}
nl("}")
}
}
fun declareFactories() = kcode("") {
- nl("public static ${baseClassName} inflate(android.view.LayoutInflater inflater, android.view.ViewGroup root, boolean attachToRoot) {") {
+ nl("public static $baseClassName inflate(android.view.LayoutInflater inflater, android.view.ViewGroup root, boolean attachToRoot) {") {
tab("return inflate(inflater, root, attachToRoot, android.databinding.DataBindingUtil.getDefaultComponent());")
}
nl("}")
- nl("public static ${baseClassName} inflate(android.view.LayoutInflater inflater, android.view.ViewGroup root, boolean attachToRoot, android.databinding.DataBindingComponent bindingComponent) {") {
- tab("return android.databinding.DataBindingUtil.<${baseClassName}>inflate(inflater, ${layoutBinder.getModulePackage()}.R.layout.${layoutBinder.getLayoutname()}, root, attachToRoot, bindingComponent);")
+ nl("public static $baseClassName inflate(android.view.LayoutInflater inflater, android.view.ViewGroup root, boolean attachToRoot, android.databinding.DataBindingComponent bindingComponent) {") {
+ tab("return android.databinding.DataBindingUtil.<$baseClassName>inflate(inflater, ${layoutBinder.modulePackage}.R.layout.${layoutBinder.layoutname}, root, attachToRoot, bindingComponent);")
}
nl("}")
- if (!layoutBinder.isMerge()) {
- nl("public static ${baseClassName} inflate(android.view.LayoutInflater inflater) {") {
+ if (!layoutBinder.isMerge) {
+ nl("public static $baseClassName inflate(android.view.LayoutInflater inflater) {") {
tab("return inflate(inflater, android.databinding.DataBindingUtil.getDefaultComponent());")
}
nl("}")
- nl("public static ${baseClassName} inflate(android.view.LayoutInflater inflater, android.databinding.DataBindingComponent bindingComponent) {") {
- tab("return bind(inflater.inflate(${layoutBinder.getModulePackage()}.R.layout.${layoutBinder.getLayoutname()}, null, false), bindingComponent);")
+ nl("public static $baseClassName inflate(android.view.LayoutInflater inflater, android.databinding.DataBindingComponent bindingComponent) {") {
+ tab("return bind(inflater.inflate(${layoutBinder.modulePackage}.R.layout.${layoutBinder.layoutname}, null, false), bindingComponent);")
}
nl("}")
- nl("public static ${baseClassName} bind(android.view.View view) {") {
+ nl("public static $baseClassName bind(android.view.View view) {") {
tab("return bind(view, android.databinding.DataBindingUtil.getDefaultComponent());")
}
nl("}")
- nl("public static ${baseClassName} bind(android.view.View view, android.databinding.DataBindingComponent bindingComponent) {") {
- tab("if (!\"${layoutBinder.getTag()}_0\".equals(view.getTag())) {") {
+ nl("public static $baseClassName bind(android.view.View view, android.databinding.DataBindingComponent bindingComponent) {") {
+ tab("if (!\"${layoutBinder.tag}_0\".equals(view.getTag())) {") {
tab("throw new RuntimeException(\"view tag isn't correct on view:\" + view.getTag());")
}
tab("}")
- tab("return new ${baseClassName}(bindingComponent, view);")
+ tab("return new $baseClassName(bindingComponent, view);")
}
nl("}")
}
@@ -990,43 +1090,43 @@
* When called for a library compilation, we do not generate real implementations
*/
public fun writeBaseClass(forLibrary : Boolean) : String =
- kcode("package ${layoutBinder.getPackage()};") {
+ kcode("package ${layoutBinder.`package`};") {
nl("import android.databinding.Bindable;")
nl("import android.databinding.DataBindingUtil;")
nl("import android.databinding.ViewDataBinding;")
- nl("public abstract class ${baseClassName} extends ViewDataBinding {")
- layoutBinder.getSortedTargets().filter{it.getId() != null}.forEach {
- tab("public final ${it.interfaceType} ${it.fieldName};")
+ nl("public abstract class $baseClassName extends ViewDataBinding {")
+ layoutBinder.sortedTargets.filter{it.id != null}.forEach {
+ tab("public final ${it.interfaceClass} ${it.fieldName};")
}
nl("")
- tab("protected ${baseClassName}(android.databinding.DataBindingComponent bindingComponent, android.view.View root_, int localFieldCount") {
- layoutBinder.getSortedTargets().filter{it.getId() != null}.forEach {
- tab(", ${it.interfaceType} ${it.constructorParamName}")
+ tab("protected $baseClassName(android.databinding.DataBindingComponent bindingComponent, android.view.View root_, int localFieldCount") {
+ layoutBinder.sortedTargets.filter{it.id != null}.forEach {
+ tab(", ${it.interfaceClass} ${it.constructorParamName}")
}
}
tab(") {") {
tab("super(bindingComponent, root_, localFieldCount);")
- layoutBinder.getSortedTargets().filter{it.getId() != null}.forEach {
+ layoutBinder.sortedTargets.filter{it.id != null}.forEach {
tab("this.${it.fieldName} = ${it.constructorParamName};")
}
}
tab("}")
nl("")
variables.forEach {
- if (it.getUserDefinedType() != null) {
- val type = ModelAnalyzer.getInstance().applyImports(it.getUserDefinedType(), model.getImports())
- tab("public abstract void ${it.setterName}(${type} ${it.readableName});")
+ if (it.userDefinedType != null) {
+ val type = ModelAnalyzer.getInstance().applyImports(it.userDefinedType, model.imports)
+ tab("public abstract void ${it.setterName}($type ${it.readableName});")
}
}
- tab("public static ${baseClassName} inflate(android.view.LayoutInflater inflater, android.view.ViewGroup root, boolean attachToRoot) {") {
+ tab("public static $baseClassName inflate(android.view.LayoutInflater inflater, android.view.ViewGroup root, boolean attachToRoot) {") {
tab("return inflate(inflater, root, attachToRoot, android.databinding.DataBindingUtil.getDefaultComponent());")
}
tab("}")
- tab("public static ${baseClassName} inflate(android.view.LayoutInflater inflater) {") {
+ tab("public static $baseClassName inflate(android.view.LayoutInflater inflater) {") {
tab("return inflate(inflater, android.databinding.DataBindingUtil.getDefaultComponent());")
}
tab("}")
- tab("public static ${baseClassName} bind(android.view.View view) {") {
+ tab("public static $baseClassName bind(android.view.View view) {") {
if (forLibrary) {
tab("return null;")
} else {
@@ -1034,27 +1134,27 @@
}
}
tab("}")
- tab("public static ${baseClassName} inflate(android.view.LayoutInflater inflater, android.view.ViewGroup root, boolean attachToRoot, android.databinding.DataBindingComponent bindingComponent) {") {
+ tab("public static $baseClassName inflate(android.view.LayoutInflater inflater, android.view.ViewGroup root, boolean attachToRoot, android.databinding.DataBindingComponent bindingComponent) {") {
if (forLibrary) {
tab("return null;")
} else {
- tab("return DataBindingUtil.<${baseClassName}>inflate(inflater, ${layoutBinder.getModulePackage()}.R.layout.${layoutBinder.getLayoutname()}, root, attachToRoot, bindingComponent);")
+ tab("return DataBindingUtil.<$baseClassName>inflate(inflater, ${layoutBinder.modulePackage}.R.layout.${layoutBinder.layoutname}, root, attachToRoot, bindingComponent);")
}
}
tab("}")
- tab("public static ${baseClassName} inflate(android.view.LayoutInflater inflater, android.databinding.DataBindingComponent bindingComponent) {") {
+ tab("public static $baseClassName inflate(android.view.LayoutInflater inflater, android.databinding.DataBindingComponent bindingComponent) {") {
if (forLibrary) {
tab("return null;")
} else {
- tab("return DataBindingUtil.<${baseClassName}>inflate(inflater, ${layoutBinder.getModulePackage()}.R.layout.${layoutBinder.getLayoutname()}, null, false, bindingComponent);")
+ tab("return DataBindingUtil.<$baseClassName>inflate(inflater, ${layoutBinder.modulePackage}.R.layout.${layoutBinder.layoutname}, null, false, bindingComponent);")
}
}
tab("}")
- tab("public static ${baseClassName} bind(android.view.View view, android.databinding.DataBindingComponent bindingComponent) {") {
+ tab("public static $baseClassName bind(android.view.View view, android.databinding.DataBindingComponent bindingComponent) {") {
if (forLibrary) {
tab("return null;")
} else {
- tab("return (${baseClassName})bind(bindingComponent, view, ${layoutBinder.getModulePackage()}.R.layout.${layoutBinder.getLayoutname()});")
+ tab("return ($baseClassName)bind(bindingComponent, view, ${layoutBinder.modulePackage}.R.layout.${layoutBinder.layoutname});")
}
}
tab("}")
diff --git a/compiler/src/main/resources/NOTICE.txt b/compiler/src/main/resources/NOTICE.txt
new file mode 100644
index 0000000..36ed1c9
--- /dev/null
+++ b/compiler/src/main/resources/NOTICE.txt
@@ -0,0 +1,1823 @@
+List of 3rd party licenses:
+-----------------------------------------------------------------------------
+* [ResolvedArtifact dependency:org.gradle.api.internal.artifacts.ivyservice.dynamicversions.DefaultResolvedModuleVersion@51d6b1e name:antlr4 classifier:null extension:jar type:jar]
+
+ ****** LICENSE:
+[The "BSD license"]
+Copyright (c) 2015 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.
+
+
+
+
+-----------------------------------------------------------------------------
+* [ResolvedArtifact dependency:org.gradle.api.internal.artifacts.ivyservice.dynamicversions.DefaultResolvedModuleVersion@651cb3d9 name:kotlin-stdlib classifier:null extension:jar type:jar]
+
+ ****** NOTICE:
+ =========================================================================
+ == NOTICE file corresponding to the section 4 d of ==
+ == the Apache License, Version 2.0, ==
+ == in this case for the Kotlin Compiler distribution. ==
+ =========================================================================
+
+ Kotlin Compiler
+ Copyright 2010-2015 JetBrains s.r.o and respective authors and developers
+
+ ****** LICENSE:
+/*
+ * Copyright 2010-2015 JetBrains s.r.o.
+ *
+ * Licensed under the Apache License, Version 2.0 (the "License");
+ * you may not use this file except in compliance with the License.
+ * You may obtain a copy of the License at
+ *
+ * http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing, software
+ * distributed under the License is distributed on an "AS IS" BASIS,
+ * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+ * See the License for the specific language governing permissions and
+ * limitations under the License.
+ */
+
+ ****** LICENSE:
+
+ Apache License
+ Version 2.0, January 2004
+ http://www.apache.org/licenses/
+
+ TERMS AND CONDITIONS FOR USE, REPRODUCTION, AND DISTRIBUTION
+
+ 1. Definitions.
+
+ "License" shall mean the terms and conditions for use, reproduction,
+ and distribution as defined by Sections 1 through 9 of this document.
+
+ "Licensor" shall mean the copyright owner or entity authorized by
+ the copyright owner that is granting the License.
+
+ "Legal Entity" shall mean the union of the acting entity and all
+ other entities that control, are controlled by, or are under common
+ control with that entity. For the purposes of this definition,
+ "control" means (i) the power, direct or indirect, to cause the
+ direction or management of such entity, whether by contract or
+ otherwise, or (ii) ownership of fifty percent (50%) or more of the
+ outstanding shares, or (iii) beneficial ownership of such entity.
+
+ "You" (or "Your") shall mean an individual or Legal Entity
+ exercising permissions granted by this License.
+
+ "Source" form shall mean the preferred form for making modifications,
+ including but not limited to software source code, documentation
+ source, and configuration files.
+
+ "Object" form shall mean any form resulting from mechanical
+ transformation or translation of a Source form, including but
+ not limited to compiled object code, generated documentation,
+ and conversions to other media types.
+
+ "Work" shall mean the work of authorship, whether in Source or
+ Object form, made available under the License, as indicated by a
+ copyright notice that is included in or attached to the work
+ (an example is provided in the Appendix below).
+
+ "Derivative Works" shall mean any work, whether in Source or Object
+ form, that is based on (or derived from) the Work and for which the
+ editorial revisions, annotations, elaborations, or other modifications
+ represent, as a whole, an original work of authorship. For the purposes
+ of this License, Derivative Works shall not include works that remain
+ separable from, or merely link (or bind by name) to the interfaces of,
+ the Work and Derivative Works thereof.
+
+ "Contribution" shall mean any work of authorship, including
+ the original version of the Work and any modifications or additions
+ to that Work or Derivative Works thereof, that is intentionally
+ submitted to Licensor for inclusion in the Work by the copyright owner
+ or by an individual or Legal Entity authorized to submit on behalf of
+ the copyright owner. For the purposes of this definition, "submitted"
+ means any form of electronic, verbal, or written communication sent
+ to the Licensor or its representatives, including but not limited to
+ communication on electronic mailing lists, source code control systems,
+ and issue tracking systems that are managed by, or on behalf of, the
+ Licensor for the purpose of discussing and improving the Work, but
+ excluding communication that is conspicuously marked or otherwise
+ designated in writing by the copyright owner as "Not a Contribution."
+
+ "Contributor" shall mean Licensor and any individual or Legal Entity
+ on behalf of whom a Contribution has been received by Licensor and
+ subsequently incorporated within the Work.
+
+ 2. Grant of Copyright License. Subject to the terms and conditions of
+ this License, each Contributor hereby grants to You a perpetual,
+ worldwide, non-exclusive, no-charge, royalty-free, irrevocable
+ copyright license to reproduce, prepare Derivative Works of,
+ publicly display, publicly perform, sublicense, and distribute the
+ Work and such Derivative Works in Source or Object form.
+
+ 3. Grant of Patent License. Subject to the terms and conditions of
+ this License, each Contributor hereby grants to You a perpetual,
+ worldwide, non-exclusive, no-charge, royalty-free, irrevocable
+ (except as stated in this section) patent license to make, have made,
+ use, offer to sell, sell, import, and otherwise transfer the Work,
+ where such license applies only to those patent claims licensable
+ by such Contributor that are necessarily infringed by their
+ Contribution(s) alone or by combination of their Contribution(s)
+ with the Work to which such Contribution(s) was submitted. If You
+ institute patent litigation against any entity (including a
+ cross-claim or counterclaim in a lawsuit) alleging that the Work
+ or a Contribution incorporated within the Work constitutes direct
+ or contributory patent infringement, then any patent licenses
+ granted to You under this License for that Work shall terminate
+ as of the date such litigation is filed.
+
+ 4. Redistribution. You may reproduce and distribute copies of the
+ Work or Derivative Works thereof in any medium, with or without
+ modifications, and in Source or Object form, provided that You
+ meet the following conditions:
+
+ (a) You must give any other recipients of the Work or
+ Derivative Works a copy of this License; and
+
+ (b) You must cause any modified files to carry prominent notices
+ stating that You changed the files; and
+
+ (c) You must retain, in the Source form of any Derivative Works
+ that You distribute, all copyright, patent, trademark, and
+ attribution notices from the Source form of the Work,
+ excluding those notices that do not pertain to any part of
+ the Derivative Works; and
+
+ (d) If the Work includes a "NOTICE" text file as part of its
+ distribution, then any Derivative Works that You distribute must
+ include a readable copy of the attribution notices contained
+ within such NOTICE file, excluding those notices that do not
+ pertain to any part of the Derivative Works, in at least one
+ of the following places: within a NOTICE text file distributed
+ as part of the Derivative Works; within the Source form or
+ documentation, if provided along with the Derivative Works; or,
+ within a display generated by the Derivative Works, if and
+ wherever such third-party notices normally appear. The contents
+ of the NOTICE file are for informational purposes only and
+ do not modify the License. You may add Your own attribution
+ notices within Derivative Works that You distribute, alongside
+ or as an addendum to the NOTICE text from the Work, provided
+ that such additional attribution notices cannot be construed
+ as modifying the License.
+
+ You may add Your own copyright statement to Your modifications and
+ may provide additional or different license terms and conditions
+ for use, reproduction, or distribution of Your modifications, or
+ for any such Derivative Works as a whole, provided Your use,
+ reproduction, and distribution of the Work otherwise complies with
+ the conditions stated in this License.
+
+ 5. Submission of Contributions. Unless You explicitly state otherwise,
+ any Contribution intentionally submitted for inclusion in the Work
+ by You to the Licensor shall be under the terms and conditions of
+ this License, without any additional terms or conditions.
+ Notwithstanding the above, nothing herein shall supersede or modify
+ the terms of any separate license agreement you may have executed
+ with Licensor regarding such Contributions.
+
+ 6. Trademarks. This License does not grant permission to use the trade
+ names, trademarks, service marks, or product names of the Licensor,
+ except as required for reasonable and customary use in describing the
+ origin of the Work and reproducing the content of the NOTICE file.
+
+ 7. Disclaimer of Warranty. Unless required by applicable law or
+ agreed to in writing, Licensor provides the Work (and each
+ Contributor provides its Contributions) on an "AS IS" BASIS,
+ WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or
+ implied, including, without limitation, any warranties or conditions
+ of TITLE, NON-INFRINGEMENT, MERCHANTABILITY, or FITNESS FOR A
+ PARTICULAR PURPOSE. You are solely responsible for determining the
+ appropriateness of using or redistributing the Work and assume any
+ risks associated with Your exercise of permissions under this License.
+
+ 8. Limitation of Liability. In no event and under no legal theory,
+ whether in tort (including negligence), contract, or otherwise,
+ unless required by applicable law (such as deliberate and grossly
+ negligent acts) or agreed to in writing, shall any Contributor be
+ liable to You for damages, including any direct, indirect, special,
+ incidental, or consequential damages of any character arising as a
+ result of this License or out of the use or inability to use the
+ Work (including but not limited to damages for loss of goodwill,
+ work stoppage, computer failure or malfunction, or any and all
+ other commercial damages or losses), even if such Contributor
+ has been advised of the possibility of such damages.
+
+ 9. Accepting Warranty or Additional Liability. While redistributing
+ the Work or Derivative Works thereof, You may choose to offer,
+ and charge a fee for, acceptance of support, warranty, indemnity,
+ or other liability obligations and/or rights consistent with this
+ License. However, in accepting such obligations, You may act only
+ on Your own behalf and on Your sole responsibility, not on behalf
+ of any other Contributor, and only if You agree to indemnify,
+ defend, and hold each Contributor harmless for any liability
+ incurred by, or claims asserted against, such Contributor by reason
+ of your accepting any such warranty or additional liability.
+
+ END OF TERMS AND CONDITIONS
+
+ APPENDIX: How to apply the Apache License to your work.
+
+ To apply the Apache License to your work, attach the following
+ boilerplate notice, with the fields enclosed by brackets "[]"
+ replaced with your own identifying information. (Don't include
+ the brackets!) The text should be enclosed in the appropriate
+ comment syntax for the file format. We also recommend that a
+ file or class name and description of purpose be included on the
+ same "printed page" as the copyright notice for easier
+ identification within third-party archives.
+
+ Copyright [yyyy] [name of copyright owner]
+
+ Licensed under the Apache License, Version 2.0 (the "License");
+ you may not use this file except in compliance with the License.
+ You may obtain a copy of the License at
+
+ http://www.apache.org/licenses/LICENSE-2.0
+
+ Unless required by applicable law or agreed to in writing, software
+ distributed under the License is distributed on an "AS IS" BASIS,
+ WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+ See the License for the specific language governing permissions and
+ limitations under the License.
+
+
+
+
+-----------------------------------------------------------------------------
+* [ResolvedArtifact dependency:org.gradle.api.internal.artifacts.ivyservice.dynamicversions.DefaultResolvedModuleVersion@2fc42657 name:commons-io classifier:null extension:jar type:jar]
+
+ ****** NOTICE:
+Apache Commons IO
+Copyright 2002-2014 The Apache Software Foundation
+
+This product includes software developed at
+The Apache Software Foundation (http://www.apache.org/).
+
+ ****** LICENSE:
+
+ Apache License
+ Version 2.0, January 2004
+ http://www.apache.org/licenses/
+
+ TERMS AND CONDITIONS FOR USE, REPRODUCTION, AND DISTRIBUTION
+
+ 1. Definitions.
+
+ "License" shall mean the terms and conditions for use, reproduction,
+ and distribution as defined by Sections 1 through 9 of this document.
+
+ "Licensor" shall mean the copyright owner or entity authorized by
+ the copyright owner that is granting the License.
+
+ "Legal Entity" shall mean the union of the acting entity and all
+ other entities that control, are controlled by, or are under common
+ control with that entity. For the purposes of this definition,
+ "control" means (i) the power, direct or indirect, to cause the
+ direction or management of such entity, whether by contract or
+ otherwise, or (ii) ownership of fifty percent (50%) or more of the
+ outstanding shares, or (iii) beneficial ownership of such entity.
+
+ "You" (or "Your") shall mean an individual or Legal Entity
+ exercising permissions granted by this License.
+
+ "Source" form shall mean the preferred form for making modifications,
+ including but not limited to software source code, documentation
+ source, and configuration files.
+
+ "Object" form shall mean any form resulting from mechanical
+ transformation or translation of a Source form, including but
+ not limited to compiled object code, generated documentation,
+ and conversions to other media types.
+
+ "Work" shall mean the work of authorship, whether in Source or
+ Object form, made available under the License, as indicated by a
+ copyright notice that is included in or attached to the work
+ (an example is provided in the Appendix below).
+
+ "Derivative Works" shall mean any work, whether in Source or Object
+ form, that is based on (or derived from) the Work and for which the
+ editorial revisions, annotations, elaborations, or other modifications
+ represent, as a whole, an original work of authorship. For the purposes
+ of this License, Derivative Works shall not include works that remain
+ separable from, or merely link (or bind by name) to the interfaces of,
+ the Work and Derivative Works thereof.
+
+ "Contribution" shall mean any work of authorship, including
+ the original version of the Work and any modifications or additions
+ to that Work or Derivative Works thereof, that is intentionally
+ submitted to Licensor for inclusion in the Work by the copyright owner
+ or by an individual or Legal Entity authorized to submit on behalf of
+ the copyright owner. For the purposes of this definition, "submitted"
+ means any form of electronic, verbal, or written communication sent
+ to the Licensor or its representatives, including but not limited to
+ communication on electronic mailing lists, source code control systems,
+ and issue tracking systems that are managed by, or on behalf of, the
+ Licensor for the purpose of discussing and improving the Work, but
+ excluding communication that is conspicuously marked or otherwise
+ designated in writing by the copyright owner as "Not a Contribution."
+
+ "Contributor" shall mean Licensor and any individual or Legal Entity
+ on behalf of whom a Contribution has been received by Licensor and
+ subsequently incorporated within the Work.
+
+ 2. Grant of Copyright License. Subject to the terms and conditions of
+ this License, each Contributor hereby grants to You a perpetual,
+ worldwide, non-exclusive, no-charge, royalty-free, irrevocable
+ copyright license to reproduce, prepare Derivative Works of,
+ publicly display, publicly perform, sublicense, and distribute the
+ Work and such Derivative Works in Source or Object form.
+
+ 3. Grant of Patent License. Subject to the terms and conditions of
+ this License, each Contributor hereby grants to You a perpetual,
+ worldwide, non-exclusive, no-charge, royalty-free, irrevocable
+ (except as stated in this section) patent license to make, have made,
+ use, offer to sell, sell, import, and otherwise transfer the Work,
+ where such license applies only to those patent claims licensable
+ by such Contributor that are necessarily infringed by their
+ Contribution(s) alone or by combination of their Contribution(s)
+ with the Work to which such Contribution(s) was submitted. If You
+ institute patent litigation against any entity (including a
+ cross-claim or counterclaim in a lawsuit) alleging that the Work
+ or a Contribution incorporated within the Work constitutes direct
+ or contributory patent infringement, then any patent licenses
+ granted to You under this License for that Work shall terminate
+ as of the date such litigation is filed.
+
+ 4. Redistribution. You may reproduce and distribute copies of the
+ Work or Derivative Works thereof in any medium, with or without
+ modifications, and in Source or Object form, provided that You
+ meet the following conditions:
+
+ (a) You must give any other recipients of the Work or
+ Derivative Works a copy of this License; and
+
+ (b) You must cause any modified files to carry prominent notices
+ stating that You changed the files; and
+
+ (c) You must retain, in the Source form of any Derivative Works
+ that You distribute, all copyright, patent, trademark, and
+ attribution notices from the Source form of the Work,
+ excluding those notices that do not pertain to any part of
+ the Derivative Works; and
+
+ (d) If the Work includes a "NOTICE" text file as part of its
+ distribution, then any Derivative Works that You distribute must
+ include a readable copy of the attribution notices contained
+ within such NOTICE file, excluding those notices that do not
+ pertain to any part of the Derivative Works, in at least one
+ of the following places: within a NOTICE text file distributed
+ as part of the Derivative Works; within the Source form or
+ documentation, if provided along with the Derivative Works; or,
+ within a display generated by the Derivative Works, if and
+ wherever such third-party notices normally appear. The contents
+ of the NOTICE file are for informational purposes only and
+ do not modify the License. You may add Your own attribution
+ notices within Derivative Works that You distribute, alongside
+ or as an addendum to the NOTICE text from the Work, provided
+ that such additional attribution notices cannot be construed
+ as modifying the License.
+
+ You may add Your own copyright statement to Your modifications and
+ may provide additional or different license terms and conditions
+ for use, reproduction, or distribution of Your modifications, or
+ for any such Derivative Works as a whole, provided Your use,
+ reproduction, and distribution of the Work otherwise complies with
+ the conditions stated in this License.
+
+ 5. Submission of Contributions. Unless You explicitly state otherwise,
+ any Contribution intentionally submitted for inclusion in the Work
+ by You to the Licensor shall be under the terms and conditions of
+ this License, without any additional terms or conditions.
+ Notwithstanding the above, nothing herein shall supersede or modify
+ the terms of any separate license agreement you may have executed
+ with Licensor regarding such Contributions.
+
+ 6. Trademarks. This License does not grant permission to use the trade
+ names, trademarks, service marks, or product names of the Licensor,
+ except as required for reasonable and customary use in describing the
+ origin of the Work and reproducing the content of the NOTICE file.
+
+ 7. Disclaimer of Warranty. Unless required by applicable law or
+ agreed to in writing, Licensor provides the Work (and each
+ Contributor provides its Contributions) on an "AS IS" BASIS,
+ WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or
+ implied, including, without limitation, any warranties or conditions
+ of TITLE, NON-INFRINGEMENT, MERCHANTABILITY, or FITNESS FOR A
+ PARTICULAR PURPOSE. You are solely responsible for determining the
+ appropriateness of using or redistributing the Work and assume any
+ risks associated with Your exercise of permissions under this License.
+
+ 8. Limitation of Liability. In no event and under no legal theory,
+ whether in tort (including negligence), contract, or otherwise,
+ unless required by applicable law (such as deliberate and grossly
+ negligent acts) or agreed to in writing, shall any Contributor be
+ liable to You for damages, including any direct, indirect, special,
+ incidental, or consequential damages of any character arising as a
+ result of this License or out of the use or inability to use the
+ Work (including but not limited to damages for loss of goodwill,
+ work stoppage, computer failure or malfunction, or any and all
+ other commercial damages or losses), even if such Contributor
+ has been advised of the possibility of such damages.
+
+ 9. Accepting Warranty or Additional Liability. While redistributing
+ the Work or Derivative Works thereof, You may choose to offer,
+ and charge a fee for, acceptance of support, warranty, indemnity,
+ or other liability obligations and/or rights consistent with this
+ License. However, in accepting such obligations, You may act only
+ on Your own behalf and on Your sole responsibility, not on behalf
+ of any other Contributor, and only if You agree to indemnify,
+ defend, and hold each Contributor harmless for any liability
+ incurred by, or claims asserted against, such Contributor by reason
+ of your accepting any such warranty or additional liability.
+
+ END OF TERMS AND CONDITIONS
+
+ APPENDIX: How to apply the Apache License to your work.
+
+ To apply the Apache License to your work, attach the following
+ boilerplate notice, with the fields enclosed by brackets "[]"
+ replaced with your own identifying information. (Don't include
+ the brackets!) The text should be enclosed in the appropriate
+ comment syntax for the file format. We also recommend that a
+ file or class name and description of purpose be included on the
+ same "printed page" as the copyright notice for easier
+ identification within third-party archives.
+
+ Copyright [yyyy] [name of copyright owner]
+
+ Licensed under the Apache License, Version 2.0 (the "License");
+ you may not use this file except in compliance with the License.
+ You may obtain a copy of the License at
+
+ http://www.apache.org/licenses/LICENSE-2.0
+
+ Unless required by applicable law or agreed to in writing, software
+ distributed under the License is distributed on an "AS IS" BASIS,
+ WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+ See the License for the specific language governing permissions and
+ limitations under the License.
+
+
+
+
+
+-----------------------------------------------------------------------------
+* [ResolvedArtifact dependency:org.gradle.api.internal.artifacts.ivyservice.dynamicversions.DefaultResolvedModuleVersion@15859779 name:antlr4-annotations classifier:null extension:jar type:jar]
+
+ ****** LICENSE:
+[The "BSD license"]
+Copyright (c) 2015 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.
+
+
+
+
+-----------------------------------------------------------------------------
+* [ResolvedArtifact dependency:org.gradle.api.internal.artifacts.ivyservice.dynamicversions.DefaultResolvedModuleVersion@a364cb2 name:commons-codec classifier:null extension:jar type:jar]
+
+ ****** NOTICE:
+Apache Commons Codec
+Copyright 2002-2015 The Apache Software Foundation
+
+This product includes software developed at
+The Apache Software Foundation (http://www.apache.org/).
+
+src/test/org/apache/commons/codec/language/DoubleMetaphoneTest.java
+contains test data from http://aspell.net/test/orig/batch0.tab.
+Copyright (C) 2002 Kevin Atkinson (kevina@gnu.org)
+
+===============================================================================
+
+The content of package org.apache.commons.codec.language.bm has been translated
+from the original php source code available at http://stevemorse.org/phoneticinfo.htm
+with permission from the original authors.
+Original source copyright:
+Copyright (c) 2008 Alexander Beider & Stephen P. Morse.
+
+ ****** LICENSE:
+
+ Apache License
+ Version 2.0, January 2004
+ http://www.apache.org/licenses/
+
+ TERMS AND CONDITIONS FOR USE, REPRODUCTION, AND DISTRIBUTION
+
+ 1. Definitions.
+
+ "License" shall mean the terms and conditions for use, reproduction,
+ and distribution as defined by Sections 1 through 9 of this document.
+
+ "Licensor" shall mean the copyright owner or entity authorized by
+ the copyright owner that is granting the License.
+
+ "Legal Entity" shall mean the union of the acting entity and all
+ other entities that control, are controlled by, or are under common
+ control with that entity. For the purposes of this definition,
+ "control" means (i) the power, direct or indirect, to cause the
+ direction or management of such entity, whether by contract or
+ otherwise, or (ii) ownership of fifty percent (50%) or more of the
+ outstanding shares, or (iii) beneficial ownership of such entity.
+
+ "You" (or "Your") shall mean an individual or Legal Entity
+ exercising permissions granted by this License.
+
+ "Source" form shall mean the preferred form for making modifications,
+ including but not limited to software source code, documentation
+ source, and configuration files.
+
+ "Object" form shall mean any form resulting from mechanical
+ transformation or translation of a Source form, including but
+ not limited to compiled object code, generated documentation,
+ and conversions to other media types.
+
+ "Work" shall mean the work of authorship, whether in Source or
+ Object form, made available under the License, as indicated by a
+ copyright notice that is included in or attached to the work
+ (an example is provided in the Appendix below).
+
+ "Derivative Works" shall mean any work, whether in Source or Object
+ form, that is based on (or derived from) the Work and for which the
+ editorial revisions, annotations, elaborations, or other modifications
+ represent, as a whole, an original work of authorship. For the purposes
+ of this License, Derivative Works shall not include works that remain
+ separable from, or merely link (or bind by name) to the interfaces of,
+ the Work and Derivative Works thereof.
+
+ "Contribution" shall mean any work of authorship, including
+ the original version of the Work and any modifications or additions
+ to that Work or Derivative Works thereof, that is intentionally
+ submitted to Licensor for inclusion in the Work by the copyright owner
+ or by an individual or Legal Entity authorized to submit on behalf of
+ the copyright owner. For the purposes of this definition, "submitted"
+ means any form of electronic, verbal, or written communication sent
+ to the Licensor or its representatives, including but not limited to
+ communication on electronic mailing lists, source code control systems,
+ and issue tracking systems that are managed by, or on behalf of, the
+ Licensor for the purpose of discussing and improving the Work, but
+ excluding communication that is conspicuously marked or otherwise
+ designated in writing by the copyright owner as "Not a Contribution."
+
+ "Contributor" shall mean Licensor and any individual or Legal Entity
+ on behalf of whom a Contribution has been received by Licensor and
+ subsequently incorporated within the Work.
+
+ 2. Grant of Copyright License. Subject to the terms and conditions of
+ this License, each Contributor hereby grants to You a perpetual,
+ worldwide, non-exclusive, no-charge, royalty-free, irrevocable
+ copyright license to reproduce, prepare Derivative Works of,
+ publicly display, publicly perform, sublicense, and distribute the
+ Work and such Derivative Works in Source or Object form.
+
+ 3. Grant of Patent License. Subject to the terms and conditions of
+ this License, each Contributor hereby grants to You a perpetual,
+ worldwide, non-exclusive, no-charge, royalty-free, irrevocable
+ (except as stated in this section) patent license to make, have made,
+ use, offer to sell, sell, import, and otherwise transfer the Work,
+ where such license applies only to those patent claims licensable
+ by such Contributor that are necessarily infringed by their
+ Contribution(s) alone or by combination of their Contribution(s)
+ with the Work to which such Contribution(s) was submitted. If You
+ institute patent litigation against any entity (including a
+ cross-claim or counterclaim in a lawsuit) alleging that the Work
+ or a Contribution incorporated within the Work constitutes direct
+ or contributory patent infringement, then any patent licenses
+ granted to You under this License for that Work shall terminate
+ as of the date such litigation is filed.
+
+ 4. Redistribution. You may reproduce and distribute copies of the
+ Work or Derivative Works thereof in any medium, with or without
+ modifications, and in Source or Object form, provided that You
+ meet the following conditions:
+
+ (a) You must give any other recipients of the Work or
+ Derivative Works a copy of this License; and
+
+ (b) You must cause any modified files to carry prominent notices
+ stating that You changed the files; and
+
+ (c) You must retain, in the Source form of any Derivative Works
+ that You distribute, all copyright, patent, trademark, and
+ attribution notices from the Source form of the Work,
+ excluding those notices that do not pertain to any part of
+ the Derivative Works; and
+
+ (d) If the Work includes a "NOTICE" text file as part of its
+ distribution, then any Derivative Works that You distribute must
+ include a readable copy of the attribution notices contained
+ within such NOTICE file, excluding those notices that do not
+ pertain to any part of the Derivative Works, in at least one
+ of the following places: within a NOTICE text file distributed
+ as part of the Derivative Works; within the Source form or
+ documentation, if provided along with the Derivative Works; or,
+ within a display generated by the Derivative Works, if and
+ wherever such third-party notices normally appear. The contents
+ of the NOTICE file are for informational purposes only and
+ do not modify the License. You may add Your own attribution
+ notices within Derivative Works that You distribute, alongside
+ or as an addendum to the NOTICE text from the Work, provided
+ that such additional attribution notices cannot be construed
+ as modifying the License.
+
+ You may add Your own copyright statement to Your modifications and
+ may provide additional or different license terms and conditions
+ for use, reproduction, or distribution of Your modifications, or
+ for any such Derivative Works as a whole, provided Your use,
+ reproduction, and distribution of the Work otherwise complies with
+ the conditions stated in this License.
+
+ 5. Submission of Contributions. Unless You explicitly state otherwise,
+ any Contribution intentionally submitted for inclusion in the Work
+ by You to the Licensor shall be under the terms and conditions of
+ this License, without any additional terms or conditions.
+ Notwithstanding the above, nothing herein shall supersede or modify
+ the terms of any separate license agreement you may have executed
+ with Licensor regarding such Contributions.
+
+ 6. Trademarks. This License does not grant permission to use the trade
+ names, trademarks, service marks, or product names of the Licensor,
+ except as required for reasonable and customary use in describing the
+ origin of the Work and reproducing the content of the NOTICE file.
+
+ 7. Disclaimer of Warranty. Unless required by applicable law or
+ agreed to in writing, Licensor provides the Work (and each
+ Contributor provides its Contributions) on an "AS IS" BASIS,
+ WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or
+ implied, including, without limitation, any warranties or conditions
+ of TITLE, NON-INFRINGEMENT, MERCHANTABILITY, or FITNESS FOR A
+ PARTICULAR PURPOSE. You are solely responsible for determining the
+ appropriateness of using or redistributing the Work and assume any
+ risks associated with Your exercise of permissions under this License.
+
+ 8. Limitation of Liability. In no event and under no legal theory,
+ whether in tort (including negligence), contract, or otherwise,
+ unless required by applicable law (such as deliberate and grossly
+ negligent acts) or agreed to in writing, shall any Contributor be
+ liable to You for damages, including any direct, indirect, special,
+ incidental, or consequential damages of any character arising as a
+ result of this License or out of the use or inability to use the
+ Work (including but not limited to damages for loss of goodwill,
+ work stoppage, computer failure or malfunction, or any and all
+ other commercial damages or losses), even if such Contributor
+ has been advised of the possibility of such damages.
+
+ 9. Accepting Warranty or Additional Liability. While redistributing
+ the Work or Derivative Works thereof, You may choose to offer,
+ and charge a fee for, acceptance of support, warranty, indemnity,
+ or other liability obligations and/or rights consistent with this
+ License. However, in accepting such obligations, You may act only
+ on Your own behalf and on Your sole responsibility, not on behalf
+ of any other Contributor, and only if You agree to indemnify,
+ defend, and hold each Contributor harmless for any liability
+ incurred by, or claims asserted against, such Contributor by reason
+ of your accepting any such warranty or additional liability.
+
+ END OF TERMS AND CONDITIONS
+
+ APPENDIX: How to apply the Apache License to your work.
+
+ To apply the Apache License to your work, attach the following
+ boilerplate notice, with the fields enclosed by brackets "[]"
+ replaced with your own identifying information. (Don't include
+ the brackets!) The text should be enclosed in the appropriate
+ comment syntax for the file format. We also recommend that a
+ file or class name and description of purpose be included on the
+ same "printed page" as the copyright notice for easier
+ identification within third-party archives.
+
+ Copyright [yyyy] [name of copyright owner]
+
+ Licensed under the Apache License, Version 2.0 (the "License");
+ you may not use this file except in compliance with the License.
+ You may obtain a copy of the License at
+
+ http://www.apache.org/licenses/LICENSE-2.0
+
+ Unless required by applicable law or agreed to in writing, software
+ distributed under the License is distributed on an "AS IS" BASIS,
+ WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+ See the License for the specific language governing permissions and
+ limitations under the License.
+
+
+
+
+-----------------------------------------------------------------------------
+* [ResolvedArtifact dependency:org.gradle.api.internal.artifacts.ivyservice.dynamicversions.DefaultResolvedModuleVersion@6c2e3a1a name:antlr-runtime classifier:null extension:jar type:jar]
+
+ ****** LICENSE:
+[The "BSD license"]
+Copyright (c) 2015 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.
+
+
+
+
+-----------------------------------------------------------------------------
+* [ResolvedArtifact dependency:org.gradle.api.internal.artifacts.ivyservice.dynamicversions.DefaultResolvedModuleVersion@76957ab3 name:juniversalchardet classifier:null extension:jar type:jar]
+
+ ****** LICENSE:
+ MOZILLA PUBLIC LICENSE
+ Version 1.1
+
+ ---------------
+
+1. Definitions.
+
+ 1.0.1. "Commercial Use" means distribution or otherwise making the
+ Covered Code available to a third party.
+
+ 1.1. "Contributor" means each entity that creates or contributes to
+ the creation of Modifications.
+
+ 1.2. "Contributor Version" means the combination of the Original
+ Code, prior Modifications used by a Contributor, and the Modifications
+ made by that particular Contributor.
+
+ 1.3. "Covered Code" means the Original Code or Modifications or the
+ combination of the Original Code and Modifications, in each case
+ including portions thereof.
+
+ 1.4. "Electronic Distribution Mechanism" means a mechanism generally
+ accepted in the software development community for the electronic
+ transfer of data.
+
+ 1.5. "Executable" means Covered Code in any form other than Source
+ Code.
+
+ 1.6. "Initial Developer" means the individual or entity identified
+ as the Initial Developer in the Source Code notice required by Exhibit
+ A.
+
+ 1.7. "Larger Work" means a work which combines Covered Code or
+ portions thereof with code not governed by the terms of this License.
+
+ 1.8. "License" means this document.
+
+ 1.8.1. "Licensable" means having the right to grant, to the maximum
+ extent possible, whether at the time of the initial grant or
+ subsequently acquired, any and all of the rights conveyed herein.
+
+ 1.9. "Modifications" means any addition to or deletion from the
+ substance or structure of either the Original Code or any previous
+ Modifications. When Covered Code is released as a series of files, a
+ Modification is:
+ A. Any addition to or deletion from the contents of a file
+ containing Original Code or previous Modifications.
+
+ B. Any new file that contains any part of the Original Code or
+ previous Modifications.
+
+ 1.10. "Original Code" means Source Code of computer software code
+ which is described in the Source Code notice required by Exhibit A as
+ Original Code, and which, at the time of its release under this
+ License is not already Covered Code governed by this License.
+
+ 1.10.1. "Patent Claims" means any patent claim(s), now owned or
+ hereafter acquired, including without limitation, method, process,
+ and apparatus claims, in any patent Licensable by grantor.
+
+ 1.11. "Source Code" means the preferred form of the Covered Code for
+ making modifications to it, including all modules it contains, plus
+ any associated interface definition files, scripts used to control
+ compilation and installation of an Executable, or source code
+ differential comparisons against either the Original Code or another
+ well known, available Covered Code of the Contributor's choice. The
+ Source Code can be in a compressed or archival form, provided the
+ appropriate decompression or de-archiving software is widely available
+ for no charge.
+
+ 1.12. "You" (or "Your") means an individual or a legal entity
+ exercising rights under, and complying with all of the terms of, this
+ License or a future version of this License issued under Section 6.1.
+ For legal entities, "You" includes any entity which controls, is
+ controlled by, or is under common control with You. For purposes of
+ this definition, "control" means (a) the power, direct or indirect,
+ to cause the direction or management of such entity, whether by
+ contract or otherwise, or (b) ownership of more than fifty percent
+ (50%) of the outstanding shares or beneficial ownership of such
+ entity.
+
+2. Source Code License.
+
+ 2.1. The Initial Developer Grant.
+ The Initial Developer hereby grants You a world-wide, royalty-free,
+ non-exclusive license, subject to third party intellectual property
+ claims:
+ (a) under intellectual property rights (other than patent or
+ trademark) Licensable by Initial Developer to use, reproduce,
+ modify, display, perform, sublicense and distribute the Original
+ Code (or portions thereof) with or without Modifications, and/or
+ as part of a Larger Work; and
+
+ (b) under Patents Claims infringed by the making, using or
+ selling of Original Code, to make, have made, use, practice,
+ sell, and offer for sale, and/or otherwise dispose of the
+ Original Code (or portions thereof).
+
+ (c) the licenses granted in this Section 2.1(a) and (b) are
+ effective on the date Initial Developer first distributes
+ Original Code under the terms of this License.
+
+ (d) Notwithstanding Section 2.1(b) above, no patent license is
+ granted: 1) for code that You delete from the Original Code; 2)
+ separate from the Original Code; or 3) for infringements caused
+ by: i) the modification of the Original Code or ii) the
+ combination of the Original Code with other software or devices.
+
+ 2.2. Contributor Grant.
+ Subject to third party intellectual property claims, each Contributor
+ hereby grants You a world-wide, royalty-free, non-exclusive license
+
+ (a) under intellectual property rights (other than patent or
+ trademark) Licensable by Contributor, to use, reproduce, modify,
+ display, perform, sublicense and distribute the Modifications
+ created by such Contributor (or portions thereof) either on an
+ unmodified basis, with other Modifications, as Covered Code
+ and/or as part of a Larger Work; and
+
+ (b) under Patent Claims infringed by the making, using, or
+ selling of Modifications made by that Contributor either alone
+ and/or in combination with its Contributor Version (or portions
+ of such combination), to make, use, sell, offer for sale, have
+ made, and/or otherwise dispose of: 1) Modifications made by that
+ Contributor (or portions thereof); and 2) the combination of
+ Modifications made by that Contributor with its Contributor
+ Version (or portions of such combination).
+
+ (c) the licenses granted in Sections 2.2(a) and 2.2(b) are
+ effective on the date Contributor first makes Commercial Use of
+ the Covered Code.
+
+ (d) Notwithstanding Section 2.2(b) above, no patent license is
+ granted: 1) for any code that Contributor has deleted from the
+ Contributor Version; 2) separate from the Contributor Version;
+ 3) for infringements caused by: i) third party modifications of
+ Contributor Version or ii) the combination of Modifications made
+ by that Contributor with other software (except as part of the
+ Contributor Version) or other devices; or 4) under Patent Claims
+ infringed by Covered Code in the absence of Modifications made by
+ that Contributor.
+
+3. Distribution Obligations.
+
+ 3.1. Application of License.
+ The Modifications which You create or to which You contribute are
+ governed by the terms of this License, including without limitation
+ Section 2.2. The Source Code version of Covered Code may be
+ distributed only under the terms of this License or a future version
+ of this License released under Section 6.1, and You must include a
+ copy of this License with every copy of the Source Code You
+ distribute. You may not offer or impose any terms on any Source Code
+ version that alters or restricts the applicable version of this
+ License or the recipients' rights hereunder. However, You may include
+ an additional document offering the additional rights described in
+ Section 3.5.
+
+ 3.2. Availability of Source Code.
+ Any Modification which You create or to which You contribute must be
+ made available in Source Code form under the terms of this License
+ either on the same media as an Executable version or via an accepted
+ Electronic Distribution Mechanism to anyone to whom you made an
+ Executable version available; and if made available via Electronic
+ Distribution Mechanism, must remain available for at least twelve (12)
+ months after the date it initially became available, or at least six
+ (6) months after a subsequent version of that particular Modification
+ has been made available to such recipients. You are responsible for
+ ensuring that the Source Code version remains available even if the
+ Electronic Distribution Mechanism is maintained by a third party.
+
+ 3.3. Description of Modifications.
+ You must cause all Covered Code to which You contribute to contain a
+ file documenting the changes You made to create that Covered Code and
+ the date of any change. You must include a prominent statement that
+ the Modification is derived, directly or indirectly, from Original
+ Code provided by the Initial Developer and including the name of the
+ Initial Developer in (a) the Source Code, and (b) in any notice in an
+ Executable version or related documentation in which You describe the
+ origin or ownership of the Covered Code.
+
+ 3.4. Intellectual Property Matters
+ (a) Third Party Claims.
+ If Contributor has knowledge that a license under a third party's
+ intellectual property rights is required to exercise the rights
+ granted by such Contributor under Sections 2.1 or 2.2,
+ Contributor must include a text file with the Source Code
+ distribution titled "LEGAL" which describes the claim and the
+ party making the claim in sufficient detail that a recipient will
+ know whom to contact. If Contributor obtains such knowledge after
+ the Modification is made available as described in Section 3.2,
+ Contributor shall promptly modify the LEGAL file in all copies
+ Contributor makes available thereafter and shall take other steps
+ (such as notifying appropriate mailing lists or newsgroups)
+ reasonably calculated to inform those who received the Covered
+ Code that new knowledge has been obtained.
+
+ (b) Contributor APIs.
+ If Contributor's Modifications include an application programming
+ interface and Contributor has knowledge of patent licenses which
+ are reasonably necessary to implement that API, Contributor must
+ also include this information in the LEGAL file.
+
+ (c) Representations.
+ Contributor represents that, except as disclosed pursuant to
+ Section 3.4(a) above, Contributor believes that Contributor's
+ Modifications are Contributor's original creation(s) and/or
+ Contributor has sufficient rights to grant the rights conveyed by
+ this License.
+
+ 3.5. Required Notices.
+ You must duplicate the notice in Exhibit A in each file of the Source
+ Code. If it is not possible to put such notice in a particular Source
+ Code file due to its structure, then You must include such notice in a
+ location (such as a relevant directory) where a user would be likely
+ to look for such a notice. If You created one or more Modification(s)
+ You may add your name as a Contributor to the notice described in
+ Exhibit A. You must also duplicate this License in any documentation
+ for the Source Code where You describe recipients' rights or ownership
+ rights relating to Covered Code. You may choose to offer, and to
+ charge a fee for, warranty, support, indemnity or liability
+ obligations to one or more recipients of Covered Code. However, You
+ may do so only on Your own behalf, and not on behalf of the Initial
+ Developer or any Contributor. You must make it absolutely clear than
+ any such warranty, support, indemnity or liability obligation is
+ offered by You alone, and You hereby agree to indemnify the Initial
+ Developer and every Contributor for any liability incurred by the
+ Initial Developer or such Contributor as a result of warranty,
+ support, indemnity or liability terms You offer.
+
+ 3.6. Distribution of Executable Versions.
+ You may distribute Covered Code in Executable form only if the
+ requirements of Section 3.1-3.5 have been met for that Covered Code,
+ and if You include a notice stating that the Source Code version of
+ the Covered Code is available under the terms of this License,
+ including a description of how and where You have fulfilled the
+ obligations of Section 3.2. The notice must be conspicuously included
+ in any notice in an Executable version, related documentation or
+ collateral in which You describe recipients' rights relating to the
+ Covered Code. You may distribute the Executable version of Covered
+ Code or ownership rights under a license of Your choice, which may
+ contain terms different from this License, provided that You are in
+ compliance with the terms of this License and that the license for the
+ Executable version does not attempt to limit or alter the recipient's
+ rights in the Source Code version from the rights set forth in this
+ License. If You distribute the Executable version under a different
+ license You must make it absolutely clear that any terms which differ
+ from this License are offered by You alone, not by the Initial
+ Developer or any Contributor. You hereby agree to indemnify the
+ Initial Developer and every Contributor for any liability incurred by
+ the Initial Developer or such Contributor as a result of any such
+ terms You offer.
+
+ 3.7. Larger Works.
+ You may create a Larger Work by combining Covered Code with other code
+ not governed by the terms of this License and distribute the Larger
+ Work as a single product. In such a case, You must make sure the
+ requirements of this License are fulfilled for the Covered Code.
+
+4. Inability to Comply Due to Statute or Regulation.
+
+ If it is impossible for You to comply with any of the terms of this
+ License with respect to some or all of the Covered Code due to
+ statute, judicial order, or regulation then You must: (a) comply with
+ the terms of this License to the maximum extent possible; and (b)
+ describe the limitations and the code they affect. Such description
+ must be included in the LEGAL file described in Section 3.4 and must
+ be included with all distributions of the Source Code. Except to the
+ extent prohibited by statute or regulation, such description must be
+ sufficiently detailed for a recipient of ordinary skill to be able to
+ understand it.
+
+5. Application of this License.
+
+ This License applies to code to which the Initial Developer has
+ attached the notice in Exhibit A and to related Covered Code.
+
+6. Versions of the License.
+
+ 6.1. New Versions.
+ Netscape Communications Corporation ("Netscape") may publish revised
+ and/or new versions of the License from time to time. Each version
+ will be given a distinguishing version number.
+
+ 6.2. Effect of New Versions.
+ Once Covered Code has been published under a particular version of the
+ License, You may always continue to use it under the terms of that
+ version. You may also choose to use such Covered Code under the terms
+ of any subsequent version of the License published by Netscape. No one
+ other than Netscape has the right to modify the terms applicable to
+ Covered Code created under this License.
+
+ 6.3. Derivative Works.
+ If You create or use a modified version of this License (which you may
+ only do in order to apply it to code which is not already Covered Code
+ governed by this License), You must (a) rename Your license so that
+ the phrases "Mozilla", "MOZILLAPL", "MOZPL", "Netscape",
+ "MPL", "NPL" or any confusingly similar phrase do not appear in your
+ license (except to note that your license differs from this License)
+ and (b) otherwise make it clear that Your version of the license
+ contains terms which differ from the Mozilla Public License and
+ Netscape Public License. (Filling in the name of the Initial
+ Developer, Original Code or Contributor in the notice described in
+ Exhibit A shall not of themselves be deemed to be modifications of
+ this License.)
+
+7. DISCLAIMER OF WARRANTY.
+
+ COVERED CODE IS PROVIDED UNDER THIS LICENSE ON AN "AS IS" BASIS,
+ WITHOUT WARRANTY OF ANY KIND, EITHER EXPRESSED OR IMPLIED, INCLUDING,
+ WITHOUT LIMITATION, WARRANTIES THAT THE COVERED CODE IS FREE OF
+ DEFECTS, MERCHANTABLE, FIT FOR A PARTICULAR PURPOSE OR NON-INFRINGING.
+ THE ENTIRE RISK AS TO THE QUALITY AND PERFORMANCE OF THE COVERED CODE
+ IS WITH YOU. SHOULD ANY COVERED CODE PROVE DEFECTIVE IN ANY RESPECT,
+ YOU (NOT THE INITIAL DEVELOPER OR ANY OTHER CONTRIBUTOR) ASSUME THE
+ COST OF ANY NECESSARY SERVICING, REPAIR OR CORRECTION. THIS DISCLAIMER
+ OF WARRANTY CONSTITUTES AN ESSENTIAL PART OF THIS LICENSE. NO USE OF
+ ANY COVERED CODE IS AUTHORIZED HEREUNDER EXCEPT UNDER THIS DISCLAIMER.
+
+8. TERMINATION.
+
+ 8.1. This License and the rights granted hereunder will terminate
+ automatically if You fail to comply with terms herein and fail to cure
+ such breach within 30 days of becoming aware of the breach. All
+ sublicenses to the Covered Code which are properly granted shall
+ survive any termination of this License. Provisions which, by their
+ nature, must remain in effect beyond the termination of this License
+ shall survive.
+
+ 8.2. If You initiate litigation by asserting a patent infringement
+ claim (excluding declatory judgment actions) against Initial Developer
+ or a Contributor (the Initial Developer or Contributor against whom
+ You file such action is referred to as "Participant") alleging that:
+
+ (a) such Participant's Contributor Version directly or indirectly
+ infringes any patent, then any and all rights granted by such
+ Participant to You under Sections 2.1 and/or 2.2 of this License
+ shall, upon 60 days notice from Participant terminate prospectively,
+ unless if within 60 days after receipt of notice You either: (i)
+ agree in writing to pay Participant a mutually agreeable reasonable
+ royalty for Your past and future use of Modifications made by such
+ Participant, or (ii) withdraw Your litigation claim with respect to
+ the Contributor Version against such Participant. If within 60 days
+ of notice, a reasonable royalty and payment arrangement are not
+ mutually agreed upon in writing by the parties or the litigation claim
+ is not withdrawn, the rights granted by Participant to You under
+ Sections 2.1 and/or 2.2 automatically terminate at the expiration of
+ the 60 day notice period specified above.
+
+ (b) any software, hardware, or device, other than such Participant's
+ Contributor Version, directly or indirectly infringes any patent, then
+ any rights granted to You by such Participant under Sections 2.1(b)
+ and 2.2(b) are revoked effective as of the date You first made, used,
+ sold, distributed, or had made, Modifications made by that
+ Participant.
+
+ 8.3. If You assert a patent infringement claim against Participant
+ alleging that such Participant's Contributor Version directly or
+ indirectly infringes any patent where such claim is resolved (such as
+ by license or settlement) prior to the initiation of patent
+ infringement litigation, then the reasonable value of the licenses
+ granted by such Participant under Sections 2.1 or 2.2 shall be taken
+ into account in determining the amount or value of any payment or
+ license.
+
+ 8.4. In the event of termination under Sections 8.1 or 8.2 above,
+ all end user license agreements (excluding distributors and resellers)
+ which have been validly granted by You or any distributor hereunder
+ prior to termination shall survive termination.
+
+9. LIMITATION OF LIABILITY.
+
+ UNDER NO CIRCUMSTANCES AND UNDER NO LEGAL THEORY, WHETHER TORT
+ (INCLUDING NEGLIGENCE), CONTRACT, OR OTHERWISE, SHALL YOU, THE INITIAL
+ DEVELOPER, ANY OTHER CONTRIBUTOR, OR ANY DISTRIBUTOR OF COVERED CODE,
+ OR ANY SUPPLIER OF ANY OF SUCH PARTIES, BE LIABLE TO ANY PERSON FOR
+ ANY INDIRECT, SPECIAL, INCIDENTAL, OR CONSEQUENTIAL DAMAGES OF ANY
+ CHARACTER INCLUDING, WITHOUT LIMITATION, DAMAGES FOR LOSS OF GOODWILL,
+ WORK STOPPAGE, COMPUTER FAILURE OR MALFUNCTION, OR ANY AND ALL OTHER
+ COMMERCIAL DAMAGES OR LOSSES, EVEN IF SUCH PARTY SHALL HAVE BEEN
+ INFORMED OF THE POSSIBILITY OF SUCH DAMAGES. THIS LIMITATION OF
+ LIABILITY SHALL NOT APPLY TO LIABILITY FOR DEATH OR PERSONAL INJURY
+ RESULTING FROM SUCH PARTY'S NEGLIGENCE TO THE EXTENT APPLICABLE LAW
+ PROHIBITS SUCH LIMITATION. SOME JURISDICTIONS DO NOT ALLOW THE
+ EXCLUSION OR LIMITATION OF INCIDENTAL OR CONSEQUENTIAL DAMAGES, SO
+ THIS EXCLUSION AND LIMITATION MAY NOT APPLY TO YOU.
+
+10. U.S. GOVERNMENT END USERS.
+
+ The Covered Code is a "commercial item," as that term is defined in
+ 48 C.F.R. 2.101 (Oct. 1995), consisting of "commercial computer
+ software" and "commercial computer software documentation," as such
+ terms are used in 48 C.F.R. 12.212 (Sept. 1995). Consistent with 48
+ C.F.R. 12.212 and 48 C.F.R. 227.7202-1 through 227.7202-4 (June 1995),
+ all U.S. Government End Users acquire Covered Code with only those
+ rights set forth herein.
+
+11. MISCELLANEOUS.
+
+ This License represents the complete agreement concerning subject
+ matter hereof. If any provision of this License is held to be
+ unenforceable, such provision shall be reformed only to the extent
+ necessary to make it enforceable. This License shall be governed by
+ California law provisions (except to the extent applicable law, if
+ any, provides otherwise), excluding its conflict-of-law provisions.
+ With respect to disputes in which at least one party is a citizen of,
+ or an entity chartered or registered to do business in the United
+ States of America, any litigation relating to this License shall be
+ subject to the jurisdiction of the Federal Courts of the Northern
+ District of California, with venue lying in Santa Clara County,
+ California, with the losing party responsible for costs, including
+ without limitation, court costs and reasonable attorneys' fees and
+ expenses. The application of the United Nations Convention on
+ Contracts for the International Sale of Goods is expressly excluded.
+ Any law or regulation which provides that the language of a contract
+ shall be construed against the drafter shall not apply to this
+ License.
+
+12. RESPONSIBILITY FOR CLAIMS.
+
+ As between Initial Developer and the Contributors, each party is
+ responsible for claims and damages arising, directly or indirectly,
+ out of its utilization of rights under this License and You agree to
+ work with Initial Developer and Contributors to distribute such
+ responsibility on an equitable basis. Nothing herein is intended or
+ shall be deemed to constitute any admission of liability.
+
+13. MULTIPLE-LICENSED CODE.
+
+ Initial Developer may designate portions of the Covered Code as
+ "Multiple-Licensed". "Multiple-Licensed" means that the Initial
+ Developer permits you to utilize portions of the Covered Code under
+ Your choice of the MPL or the alternative licenses, if any, specified
+ by the Initial Developer in the file described in Exhibit A.
+
+EXHIBIT A -Mozilla Public License.
+
+ ``The contents of this file are subject to the Mozilla Public License
+ Version 1.1 (the "License"); you may not use this file except in
+ compliance with the License. You may obtain a copy of the License at
+ https://www.mozilla.org/MPL/
+
+ Software distributed under the License is distributed on an "AS IS"
+ basis, WITHOUT WARRANTY OF ANY KIND, either express or implied. See the
+ License for the specific language governing rights and limitations
+ under the License.
+
+ The Original Code is ______________________________________.
+
+ The Initial Developer of the Original Code is ________________________.
+ Portions created by ______________________ are Copyright (C) ______
+ _______________________. All Rights Reserved.
+
+ Contributor(s): ______________________________________.
+
+ Alternatively, the contents of this file may be used under the terms
+ of the _____ license (the "[___] License"), in which case the
+ provisions of [______] License are applicable instead of those
+ above. If you wish to allow use of your version of this file only
+ under the terms of the [____] License and not to allow others to use
+ your version of this file under the MPL, indicate your decision by
+ deleting the provisions above and replace them with the notice and
+ other provisions required by the [___] License. If you do not delete
+ the provisions above, a recipient may use your version of this file
+ under either the MPL or the [___] License."
+
+ [NOTE: The text of this Exhibit A may differ slightly from the text of
+ the notices in the Source Code files of the Original Code. You should
+ use the text of this Exhibit A rather than the text found in the
+ Original Code Source Code for Your Modifications.]
+
+
+
+
+-----------------------------------------------------------------------------
+* [ResolvedArtifact dependency:org.gradle.api.internal.artifacts.ivyservice.dynamicversions.DefaultResolvedModuleVersion@1abebee9 name:org.abego.treelayout.core classifier:null extension:jar type:jar]
+
+ ****** LICENSE:
+[The "BSD license"]
+Copyright (c) 2011, abego Software GmbH, Germany (http://www.abego.org)
+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. Neither the name of the abego Software GmbH nor the names of its
+ contributors may be used to endorse or promote products derived from this
+ software without specific prior written permission.
+
+THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS "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 COPYRIGHT HOLDER OR CONTRIBUTORS 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.
+
+
+
+
+-----------------------------------------------------------------------------
+* [ResolvedArtifact dependency:org.gradle.api.internal.artifacts.ivyservice.dynamicversions.DefaultResolvedModuleVersion@1bfa74bc name:antlr4-runtime classifier:null extension:jar type:jar]
+
+ ****** LICENSE:
+[The "BSD license"]
+Copyright (c) 2015 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.
+
+
+
+
+-----------------------------------------------------------------------------
+* [ResolvedArtifact dependency:org.gradle.api.internal.artifacts.ivyservice.dynamicversions.DefaultResolvedModuleVersion@3b9eaadc name:ST4 classifier:null extension:jar type:jar]
+
+ ****** LICENSE:
+[The "BSD license"]
+Copyright (c) 2011-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.
+
+
+
+
+-----------------------------------------------------------------------------
+* [ResolvedArtifact dependency:org.gradle.api.internal.artifacts.ivyservice.dynamicversions.DefaultResolvedModuleVersion@4770644e name:commons-lang3 classifier:null extension:jar type:jar]
+
+ ****** NOTICE:
+Apache Commons Lang
+Copyright 2001-2015 The Apache Software Foundation
+
+This product includes software developed at
+The Apache Software Foundation (http://www.apache.org/).
+
+This product includes software from the Spring Framework,
+under the Apache License 2.0 (see: StringUtils.containsWhitespace())
+
+ ****** LICENSE:
+
+ Apache License
+ Version 2.0, January 2004
+ http://www.apache.org/licenses/
+
+ TERMS AND CONDITIONS FOR USE, REPRODUCTION, AND DISTRIBUTION
+
+ 1. Definitions.
+
+ "License" shall mean the terms and conditions for use, reproduction,
+ and distribution as defined by Sections 1 through 9 of this document.
+
+ "Licensor" shall mean the copyright owner or entity authorized by
+ the copyright owner that is granting the License.
+
+ "Legal Entity" shall mean the union of the acting entity and all
+ other entities that control, are controlled by, or are under common
+ control with that entity. For the purposes of this definition,
+ "control" means (i) the power, direct or indirect, to cause the
+ direction or management of such entity, whether by contract or
+ otherwise, or (ii) ownership of fifty percent (50%) or more of the
+ outstanding shares, or (iii) beneficial ownership of such entity.
+
+ "You" (or "Your") shall mean an individual or Legal Entity
+ exercising permissions granted by this License.
+
+ "Source" form shall mean the preferred form for making modifications,
+ including but not limited to software source code, documentation
+ source, and configuration files.
+
+ "Object" form shall mean any form resulting from mechanical
+ transformation or translation of a Source form, including but
+ not limited to compiled object code, generated documentation,
+ and conversions to other media types.
+
+ "Work" shall mean the work of authorship, whether in Source or
+ Object form, made available under the License, as indicated by a
+ copyright notice that is included in or attached to the work
+ (an example is provided in the Appendix below).
+
+ "Derivative Works" shall mean any work, whether in Source or Object
+ form, that is based on (or derived from) the Work and for which the
+ editorial revisions, annotations, elaborations, or other modifications
+ represent, as a whole, an original work of authorship. For the purposes
+ of this License, Derivative Works shall not include works that remain
+ separable from, or merely link (or bind by name) to the interfaces of,
+ the Work and Derivative Works thereof.
+
+ "Contribution" shall mean any work of authorship, including
+ the original version of the Work and any modifications or additions
+ to that Work or Derivative Works thereof, that is intentionally
+ submitted to Licensor for inclusion in the Work by the copyright owner
+ or by an individual or Legal Entity authorized to submit on behalf of
+ the copyright owner. For the purposes of this definition, "submitted"
+ means any form of electronic, verbal, or written communication sent
+ to the Licensor or its representatives, including but not limited to
+ communication on electronic mailing lists, source code control systems,
+ and issue tracking systems that are managed by, or on behalf of, the
+ Licensor for the purpose of discussing and improving the Work, but
+ excluding communication that is conspicuously marked or otherwise
+ designated in writing by the copyright owner as "Not a Contribution."
+
+ "Contributor" shall mean Licensor and any individual or Legal Entity
+ on behalf of whom a Contribution has been received by Licensor and
+ subsequently incorporated within the Work.
+
+ 2. Grant of Copyright License. Subject to the terms and conditions of
+ this License, each Contributor hereby grants to You a perpetual,
+ worldwide, non-exclusive, no-charge, royalty-free, irrevocable
+ copyright license to reproduce, prepare Derivative Works of,
+ publicly display, publicly perform, sublicense, and distribute the
+ Work and such Derivative Works in Source or Object form.
+
+ 3. Grant of Patent License. Subject to the terms and conditions of
+ this License, each Contributor hereby grants to You a perpetual,
+ worldwide, non-exclusive, no-charge, royalty-free, irrevocable
+ (except as stated in this section) patent license to make, have made,
+ use, offer to sell, sell, import, and otherwise transfer the Work,
+ where such license applies only to those patent claims licensable
+ by such Contributor that are necessarily infringed by their
+ Contribution(s) alone or by combination of their Contribution(s)
+ with the Work to which such Contribution(s) was submitted. If You
+ institute patent litigation against any entity (including a
+ cross-claim or counterclaim in a lawsuit) alleging that the Work
+ or a Contribution incorporated within the Work constitutes direct
+ or contributory patent infringement, then any patent licenses
+ granted to You under this License for that Work shall terminate
+ as of the date such litigation is filed.
+
+ 4. Redistribution. You may reproduce and distribute copies of the
+ Work or Derivative Works thereof in any medium, with or without
+ modifications, and in Source or Object form, provided that You
+ meet the following conditions:
+
+ (a) You must give any other recipients of the Work or
+ Derivative Works a copy of this License; and
+
+ (b) You must cause any modified files to carry prominent notices
+ stating that You changed the files; and
+
+ (c) You must retain, in the Source form of any Derivative Works
+ that You distribute, all copyright, patent, trademark, and
+ attribution notices from the Source form of the Work,
+ excluding those notices that do not pertain to any part of
+ the Derivative Works; and
+
+ (d) If the Work includes a "NOTICE" text file as part of its
+ distribution, then any Derivative Works that You distribute must
+ include a readable copy of the attribution notices contained
+ within such NOTICE file, excluding those notices that do not
+ pertain to any part of the Derivative Works, in at least one
+ of the following places: within a NOTICE text file distributed
+ as part of the Derivative Works; within the Source form or
+ documentation, if provided along with the Derivative Works; or,
+ within a display generated by the Derivative Works, if and
+ wherever such third-party notices normally appear. The contents
+ of the NOTICE file are for informational purposes only and
+ do not modify the License. You may add Your own attribution
+ notices within Derivative Works that You distribute, alongside
+ or as an addendum to the NOTICE text from the Work, provided
+ that such additional attribution notices cannot be construed
+ as modifying the License.
+
+ You may add Your own copyright statement to Your modifications and
+ may provide additional or different license terms and conditions
+ for use, reproduction, or distribution of Your modifications, or
+ for any such Derivative Works as a whole, provided Your use,
+ reproduction, and distribution of the Work otherwise complies with
+ the conditions stated in this License.
+
+ 5. Submission of Contributions. Unless You explicitly state otherwise,
+ any Contribution intentionally submitted for inclusion in the Work
+ by You to the Licensor shall be under the terms and conditions of
+ this License, without any additional terms or conditions.
+ Notwithstanding the above, nothing herein shall supersede or modify
+ the terms of any separate license agreement you may have executed
+ with Licensor regarding such Contributions.
+
+ 6. Trademarks. This License does not grant permission to use the trade
+ names, trademarks, service marks, or product names of the Licensor,
+ except as required for reasonable and customary use in describing the
+ origin of the Work and reproducing the content of the NOTICE file.
+
+ 7. Disclaimer of Warranty. Unless required by applicable law or
+ agreed to in writing, Licensor provides the Work (and each
+ Contributor provides its Contributions) on an "AS IS" BASIS,
+ WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or
+ implied, including, without limitation, any warranties or conditions
+ of TITLE, NON-INFRINGEMENT, MERCHANTABILITY, or FITNESS FOR A
+ PARTICULAR PURPOSE. You are solely responsible for determining the
+ appropriateness of using or redistributing the Work and assume any
+ risks associated with Your exercise of permissions under this License.
+
+ 8. Limitation of Liability. In no event and under no legal theory,
+ whether in tort (including negligence), contract, or otherwise,
+ unless required by applicable law (such as deliberate and grossly
+ negligent acts) or agreed to in writing, shall any Contributor be
+ liable to You for damages, including any direct, indirect, special,
+ incidental, or consequential damages of any character arising as a
+ result of this License or out of the use or inability to use the
+ Work (including but not limited to damages for loss of goodwill,
+ work stoppage, computer failure or malfunction, or any and all
+ other commercial damages or losses), even if such Contributor
+ has been advised of the possibility of such damages.
+
+ 9. Accepting Warranty or Additional Liability. While redistributing
+ the Work or Derivative Works thereof, You may choose to offer,
+ and charge a fee for, acceptance of support, warranty, indemnity,
+ or other liability obligations and/or rights consistent with this
+ License. However, in accepting such obligations, You may act only
+ on Your own behalf and on Your sole responsibility, not on behalf
+ of any other Contributor, and only if You agree to indemnify,
+ defend, and hold each Contributor harmless for any liability
+ incurred by, or claims asserted against, such Contributor by reason
+ of your accepting any such warranty or additional liability.
+
+ END OF TERMS AND CONDITIONS
+
+ APPENDIX: How to apply the Apache License to your work.
+
+ To apply the Apache License to your work, attach the following
+ boilerplate notice, with the fields enclosed by brackets "[]"
+ replaced with your own identifying information. (Don't include
+ the brackets!) The text should be enclosed in the appropriate
+ comment syntax for the file format. We also recommend that a
+ file or class name and description of purpose be included on the
+ same "printed page" as the copyright notice for easier
+ identification within third-party archives.
+
+ Copyright [yyyy] [name of copyright owner]
+
+ Licensed under the Apache License, Version 2.0 (the "License");
+ you may not use this file except in compliance with the License.
+ You may obtain a copy of the License at
+
+ http://www.apache.org/licenses/LICENSE-2.0
+
+ Unless required by applicable law or agreed to in writing, software
+ distributed under the License is distributed on an "AS IS" BASIS,
+ WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+ See the License for the specific language governing permissions and
+ limitations under the License.
+
+
+
+
+-----------------------------------------------------------------------------
+* [ResolvedArtifact dependency:org.gradle.api.internal.artifacts.ivyservice.dynamicversions.DefaultResolvedModuleVersion@238af8b0 name:kotlin-runtime classifier:null extension:jar type:jar]
+
+ ****** NOTICE:
+ =========================================================================
+ == NOTICE file corresponding to the section 4 d of ==
+ == the Apache License, Version 2.0, ==
+ == in this case for the Kotlin Compiler distribution. ==
+ =========================================================================
+
+ Kotlin Compiler
+ Copyright 2010-2015 JetBrains s.r.o and respective authors and developers
+
+ ****** LICENSE:
+/*
+ * Copyright 2010-2015 JetBrains s.r.o.
+ *
+ * Licensed under the Apache License, Version 2.0 (the "License");
+ * you may not use this file except in compliance with the License.
+ * You may obtain a copy of the License at
+ *
+ * http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing, software
+ * distributed under the License is distributed on an "AS IS" BASIS,
+ * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+ * See the License for the specific language governing permissions and
+ * limitations under the License.
+ */
+
+ ****** LICENSE:
+
+ Apache License
+ Version 2.0, January 2004
+ http://www.apache.org/licenses/
+
+ TERMS AND CONDITIONS FOR USE, REPRODUCTION, AND DISTRIBUTION
+
+ 1. Definitions.
+
+ "License" shall mean the terms and conditions for use, reproduction,
+ and distribution as defined by Sections 1 through 9 of this document.
+
+ "Licensor" shall mean the copyright owner or entity authorized by
+ the copyright owner that is granting the License.
+
+ "Legal Entity" shall mean the union of the acting entity and all
+ other entities that control, are controlled by, or are under common
+ control with that entity. For the purposes of this definition,
+ "control" means (i) the power, direct or indirect, to cause the
+ direction or management of such entity, whether by contract or
+ otherwise, or (ii) ownership of fifty percent (50%) or more of the
+ outstanding shares, or (iii) beneficial ownership of such entity.
+
+ "You" (or "Your") shall mean an individual or Legal Entity
+ exercising permissions granted by this License.
+
+ "Source" form shall mean the preferred form for making modifications,
+ including but not limited to software source code, documentation
+ source, and configuration files.
+
+ "Object" form shall mean any form resulting from mechanical
+ transformation or translation of a Source form, including but
+ not limited to compiled object code, generated documentation,
+ and conversions to other media types.
+
+ "Work" shall mean the work of authorship, whether in Source or
+ Object form, made available under the License, as indicated by a
+ copyright notice that is included in or attached to the work
+ (an example is provided in the Appendix below).
+
+ "Derivative Works" shall mean any work, whether in Source or Object
+ form, that is based on (or derived from) the Work and for which the
+ editorial revisions, annotations, elaborations, or other modifications
+ represent, as a whole, an original work of authorship. For the purposes
+ of this License, Derivative Works shall not include works that remain
+ separable from, or merely link (or bind by name) to the interfaces of,
+ the Work and Derivative Works thereof.
+
+ "Contribution" shall mean any work of authorship, including
+ the original version of the Work and any modifications or additions
+ to that Work or Derivative Works thereof, that is intentionally
+ submitted to Licensor for inclusion in the Work by the copyright owner
+ or by an individual or Legal Entity authorized to submit on behalf of
+ the copyright owner. For the purposes of this definition, "submitted"
+ means any form of electronic, verbal, or written communication sent
+ to the Licensor or its representatives, including but not limited to
+ communication on electronic mailing lists, source code control systems,
+ and issue tracking systems that are managed by, or on behalf of, the
+ Licensor for the purpose of discussing and improving the Work, but
+ excluding communication that is conspicuously marked or otherwise
+ designated in writing by the copyright owner as "Not a Contribution."
+
+ "Contributor" shall mean Licensor and any individual or Legal Entity
+ on behalf of whom a Contribution has been received by Licensor and
+ subsequently incorporated within the Work.
+
+ 2. Grant of Copyright License. Subject to the terms and conditions of
+ this License, each Contributor hereby grants to You a perpetual,
+ worldwide, non-exclusive, no-charge, royalty-free, irrevocable
+ copyright license to reproduce, prepare Derivative Works of,
+ publicly display, publicly perform, sublicense, and distribute the
+ Work and such Derivative Works in Source or Object form.
+
+ 3. Grant of Patent License. Subject to the terms and conditions of
+ this License, each Contributor hereby grants to You a perpetual,
+ worldwide, non-exclusive, no-charge, royalty-free, irrevocable
+ (except as stated in this section) patent license to make, have made,
+ use, offer to sell, sell, import, and otherwise transfer the Work,
+ where such license applies only to those patent claims licensable
+ by such Contributor that are necessarily infringed by their
+ Contribution(s) alone or by combination of their Contribution(s)
+ with the Work to which such Contribution(s) was submitted. If You
+ institute patent litigation against any entity (including a
+ cross-claim or counterclaim in a lawsuit) alleging that the Work
+ or a Contribution incorporated within the Work constitutes direct
+ or contributory patent infringement, then any patent licenses
+ granted to You under this License for that Work shall terminate
+ as of the date such litigation is filed.
+
+ 4. Redistribution. You may reproduce and distribute copies of the
+ Work or Derivative Works thereof in any medium, with or without
+ modifications, and in Source or Object form, provided that You
+ meet the following conditions:
+
+ (a) You must give any other recipients of the Work or
+ Derivative Works a copy of this License; and
+
+ (b) You must cause any modified files to carry prominent notices
+ stating that You changed the files; and
+
+ (c) You must retain, in the Source form of any Derivative Works
+ that You distribute, all copyright, patent, trademark, and
+ attribution notices from the Source form of the Work,
+ excluding those notices that do not pertain to any part of
+ the Derivative Works; and
+
+ (d) If the Work includes a "NOTICE" text file as part of its
+ distribution, then any Derivative Works that You distribute must
+ include a readable copy of the attribution notices contained
+ within such NOTICE file, excluding those notices that do not
+ pertain to any part of the Derivative Works, in at least one
+ of the following places: within a NOTICE text file distributed
+ as part of the Derivative Works; within the Source form or
+ documentation, if provided along with the Derivative Works; or,
+ within a display generated by the Derivative Works, if and
+ wherever such third-party notices normally appear. The contents
+ of the NOTICE file are for informational purposes only and
+ do not modify the License. You may add Your own attribution
+ notices within Derivative Works that You distribute, alongside
+ or as an addendum to the NOTICE text from the Work, provided
+ that such additional attribution notices cannot be construed
+ as modifying the License.
+
+ You may add Your own copyright statement to Your modifications and
+ may provide additional or different license terms and conditions
+ for use, reproduction, or distribution of Your modifications, or
+ for any such Derivative Works as a whole, provided Your use,
+ reproduction, and distribution of the Work otherwise complies with
+ the conditions stated in this License.
+
+ 5. Submission of Contributions. Unless You explicitly state otherwise,
+ any Contribution intentionally submitted for inclusion in the Work
+ by You to the Licensor shall be under the terms and conditions of
+ this License, without any additional terms or conditions.
+ Notwithstanding the above, nothing herein shall supersede or modify
+ the terms of any separate license agreement you may have executed
+ with Licensor regarding such Contributions.
+
+ 6. Trademarks. This License does not grant permission to use the trade
+ names, trademarks, service marks, or product names of the Licensor,
+ except as required for reasonable and customary use in describing the
+ origin of the Work and reproducing the content of the NOTICE file.
+
+ 7. Disclaimer of Warranty. Unless required by applicable law or
+ agreed to in writing, Licensor provides the Work (and each
+ Contributor provides its Contributions) on an "AS IS" BASIS,
+ WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or
+ implied, including, without limitation, any warranties or conditions
+ of TITLE, NON-INFRINGEMENT, MERCHANTABILITY, or FITNESS FOR A
+ PARTICULAR PURPOSE. You are solely responsible for determining the
+ appropriateness of using or redistributing the Work and assume any
+ risks associated with Your exercise of permissions under this License.
+
+ 8. Limitation of Liability. In no event and under no legal theory,
+ whether in tort (including negligence), contract, or otherwise,
+ unless required by applicable law (such as deliberate and grossly
+ negligent acts) or agreed to in writing, shall any Contributor be
+ liable to You for damages, including any direct, indirect, special,
+ incidental, or consequential damages of any character arising as a
+ result of this License or out of the use or inability to use the
+ Work (including but not limited to damages for loss of goodwill,
+ work stoppage, computer failure or malfunction, or any and all
+ other commercial damages or losses), even if such Contributor
+ has been advised of the possibility of such damages.
+
+ 9. Accepting Warranty or Additional Liability. While redistributing
+ the Work or Derivative Works thereof, You may choose to offer,
+ and charge a fee for, acceptance of support, warranty, indemnity,
+ or other liability obligations and/or rights consistent with this
+ License. However, in accepting such obligations, You may act only
+ on Your own behalf and on Your sole responsibility, not on behalf
+ of any other Contributor, and only if You agree to indemnify,
+ defend, and hold each Contributor harmless for any liability
+ incurred by, or claims asserted against, such Contributor by reason
+ of your accepting any such warranty or additional liability.
+
+ END OF TERMS AND CONDITIONS
+
+ APPENDIX: How to apply the Apache License to your work.
+
+ To apply the Apache License to your work, attach the following
+ boilerplate notice, with the fields enclosed by brackets "[]"
+ replaced with your own identifying information. (Don't include
+ the brackets!) The text should be enclosed in the appropriate
+ comment syntax for the file format. We also recommend that a
+ file or class name and description of purpose be included on the
+ same "printed page" as the copyright notice for easier
+ identification within third-party archives.
+
+ Copyright [yyyy] [name of copyright owner]
+
+ Licensed under the Apache License, Version 2.0 (the "License");
+ you may not use this file except in compliance with the License.
+ You may obtain a copy of the License at
+
+ http://www.apache.org/licenses/LICENSE-2.0
+
+ Unless required by applicable law or agreed to in writing, software
+ distributed under the License is distributed on an "AS IS" BASIS,
+ WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+ See the License for the specific language governing permissions and
+ limitations under the License.
+
+
+
diff --git a/compiler/src/test/java/android/databinding/tool/LayoutBinderTest.java b/compiler/src/test/java/android/databinding/tool/LayoutBinderTest.java
index 042b563..8b1f820 100644
--- a/compiler/src/test/java/android/databinding/tool/LayoutBinderTest.java
+++ b/compiler/src/test/java/android/databinding/tool/LayoutBinderTest.java
@@ -14,17 +14,18 @@
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.JavaAnalyzer;
import android.databinding.tool.reflection.java.JavaClass;
+import org.junit.Before;
+import org.junit.Test;
+
import java.util.List;
import java.util.Map;
@@ -34,12 +35,13 @@
import static org.junit.Assert.assertTrue;
public class LayoutBinderTest {
- LayoutBinder mLayoutBinder;
+ MockLayoutBinder mLayoutBinder;
ExprModel mExprModel;
@Before
public void setUp() throws Exception {
mLayoutBinder = new MockLayoutBinder();
mExprModel = mLayoutBinder.getModel();
+ JavaAnalyzer.initForTests();
}
@Test
@@ -75,8 +77,8 @@
int originalSize = mExprModel.size();
mLayoutBinder.addVariable("user", "android.databinding.tool2.LayoutBinderTest.TestUser",
null);
- mLayoutBinder.parse("user.name", null);
- mLayoutBinder.parse("user.lastName", null);
+ mLayoutBinder.parse("user.name", false, null);
+ mLayoutBinder.parse("user.lastName", false, null);
assertEquals(originalSize + 3, mExprModel.size());
final List<Expr> bindingExprs = mExprModel.getBindingExpressions();
assertEquals(2, bindingExprs.size());
@@ -92,7 +94,7 @@
public void testParseWithMethods() {
mLayoutBinder.addVariable("user", "android.databinding.tool.LayoutBinderTest.TestUser",
null);
- mLayoutBinder.parse("user.fullName", null);
+ mLayoutBinder.parse("user.fullName", false, null);
Expr item = mExprModel.getBindingExpressions().get(0);
assertTrue(item instanceof FieldAccessExpr);
IdentifierExpr id = mExprModel.identifier("user");
diff --git a/compiler/src/test/java/android/databinding/tool/MockLayoutBinder.java b/compiler/src/test/java/android/databinding/tool/MockLayoutBinder.java
index 3380ffa..0807e7c 100644
--- a/compiler/src/test/java/android/databinding/tool/MockLayoutBinder.java
+++ b/compiler/src/test/java/android/databinding/tool/MockLayoutBinder.java
@@ -13,6 +13,8 @@
package android.databinding.tool;
+import android.databinding.tool.expr.IdentifierExpr;
+import android.databinding.tool.store.Location;
import android.databinding.tool.store.ResourceBundle;
import java.io.File;
@@ -24,4 +26,8 @@
"com.test.submodule",
false));
}
+
+ public IdentifierExpr addVariable(String name, String type, Location location) {
+ return super.addVariable(name, type, location, true);
+ }
}
diff --git a/compiler/src/test/java/android/databinding/tool/expr/ExprModelTest.java b/compiler/src/test/java/android/databinding/tool/expr/ExprModelTest.java
index bcd6794..04813e1 100644
--- a/compiler/src/test/java/android/databinding/tool/expr/ExprModelTest.java
+++ b/compiler/src/test/java/android/databinding/tool/expr/ExprModelTest.java
@@ -16,14 +16,8 @@
package android.databinding.tool.expr;
-import org.apache.commons.lang3.ArrayUtils;
-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.BaseObservable;
+import android.databinding.Bindable;
+import android.databinding.Observable;
import android.databinding.tool.LayoutBinder;
import android.databinding.tool.MockLayoutBinder;
import android.databinding.tool.reflection.ModelAnalyzer;
@@ -31,6 +25,13 @@
import android.databinding.tool.reflection.java.JavaAnalyzer;
import android.databinding.tool.store.Location;
import android.databinding.tool.util.L;
+import android.databinding.tool.writer.KCode;
+
+import org.junit.Before;
+import org.junit.Rule;
+import org.junit.Test;
+import org.junit.rules.TestWatcher;
+import org.junit.runner.Description;
import java.util.ArrayList;
import java.util.Arrays;
@@ -70,6 +71,16 @@
protected String computeUniqueKey() {
return mKey + super.computeUniqueKey();
}
+
+ @Override
+ protected KCode generateCode(boolean full) {
+ return new KCode();
+ }
+
+ @Override
+ protected String getInvertibleError() {
+ return "DummyExpr cannot be 2-way.";
+ }
}
ExprModel mExprModel;
@@ -131,12 +142,12 @@
@Test
public void testShouldRead() {
- LayoutBinder lb = new MockLayoutBinder();
+ MockLayoutBinder lb = new MockLayoutBinder();
mExprModel = lb.getModel();
IdentifierExpr a = lb.addVariable("a", "java.lang.String", null);
IdentifierExpr b = lb.addVariable("b", "java.lang.String", null);
IdentifierExpr c = lb.addVariable("c", "java.lang.String", null);
- lb.parse("a == null ? b : c", null);
+ lb.parse("a == null ? b : c", false, null);
mExprModel.comparison("==", a, mExprModel.symbol("null", Object.class));
lb.getModel().seal();
List<Expr> shouldRead = getShouldRead();
@@ -152,8 +163,33 @@
}
@Test
+ public void testReadConstantTernary() {
+ MockLayoutBinder lb = new MockLayoutBinder();
+ mExprModel = lb.getModel();
+ IdentifierExpr a = lb.addVariable("a", "java.lang.String", null);
+ IdentifierExpr b = lb.addVariable("b", "java.lang.String", null);
+ TernaryExpr ternaryExpr = parse(lb, "true ? a : b", TernaryExpr.class);
+ mExprModel.seal();
+ List<Expr> shouldRead = getShouldRead();
+ assertExactMatch(shouldRead, ternaryExpr.getPred());
+ List<Expr> first = getReadFirst(shouldRead);
+ assertExactMatch(first, ternaryExpr.getPred());
+ mExprModel.markBitsRead();
+ shouldRead = getShouldRead();
+ assertExactMatch(shouldRead, a, b, ternaryExpr);
+ first = getReadFirst(shouldRead);
+ assertExactMatch(first, a, b);
+ List<Expr> justRead = new ArrayList<Expr>();
+ justRead.add(a);
+ justRead.add(b);
+ first = filterOut(getReadFirst(shouldRead, justRead), justRead);
+ assertExactMatch(first, ternaryExpr);
+ assertFalse(mExprModel.markBitsRead());
+ }
+
+ @Test
public void testTernaryWithPlus() {
- LayoutBinder lb = new MockLayoutBinder();
+ MockLayoutBinder lb = new MockLayoutBinder();
mExprModel = lb.getModel();
IdentifierExpr user = lb
.addVariable("user", "android.databinding.tool.expr.ExprModelTest.User",
@@ -208,7 +244,7 @@
@Test
public void testTernaryInsideTernary() {
- LayoutBinder lb = new MockLayoutBinder();
+ MockLayoutBinder lb = new MockLayoutBinder();
mExprModel = lb.getModel();
IdentifierExpr cond1 = lb.addVariable("cond1", "boolean", null);
IdentifierExpr cond2 = lb.addVariable("cond2", "boolean", null);
@@ -253,14 +289,14 @@
@Test
public void testRequirementFlags() {
- LayoutBinder lb = new MockLayoutBinder();
+ MockLayoutBinder lb = new MockLayoutBinder();
mExprModel = lb.getModel();
IdentifierExpr a = lb.addVariable("a", "java.lang.String", null);
IdentifierExpr b = lb.addVariable("b", "java.lang.String", null);
IdentifierExpr c = lb.addVariable("c", "java.lang.String", null);
IdentifierExpr d = lb.addVariable("d", "java.lang.String", null);
IdentifierExpr e = lb.addVariable("e", "java.lang.String", null);
- final Expr aTernary = lb.parse("a == null ? b == null ? c : d : e", null);
+ final Expr aTernary = lb.parse("a == null ? b == null ? c : d : e", false, null);
assertTrue(aTernary instanceof TernaryExpr);
final Expr bTernary = ((TernaryExpr) aTernary).getIfTrue();
assertTrue(bTernary instanceof TernaryExpr);
@@ -325,7 +361,7 @@
@Test
public void testPostConditionalDependencies() {
- LayoutBinder lb = new MockLayoutBinder();
+ MockLayoutBinder lb = new MockLayoutBinder();
mExprModel = lb.getModel();
IdentifierExpr u1 = lb.addVariable("u1", User.class.getCanonicalName(), null);
@@ -389,24 +425,26 @@
assertTrue(mExprModel.markBitsRead());
shouldRead = getShouldRead();
- // actually, there is no real case to read u1 anymore because if b>c was not true,
+ // FIXME: 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);
+ assertExactMatch(shouldRead, u1, u2, u1LastName, u2LastName, bcTernary.getIfTrue(), bcTernary);
firstRead = getReadFirst(shouldRead);
- assertExactMatch(firstRead, u1LastName, u2);
-
+ assertExactMatch(firstRead, u1, u2);
+ assertFlags(u1, bcTernary.getIfTrue().getRequirementFlagIndex(true));
+ assertFlags(u2, bcTernary.getIfTrue().getRequirementFlagIndex(false));
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);
+
+ assertFalse(mExprModel.markBitsRead());
}
@Test
public void testCircularDependency() {
- LayoutBinder lb = new MockLayoutBinder();
+ MockLayoutBinder lb = new MockLayoutBinder();
mExprModel = lb.getModel();
IdentifierExpr a = lb.addVariable("a", int.class.getCanonicalName(),
null);
@@ -424,7 +462,7 @@
@Test
public void testNestedCircularDependency() {
- LayoutBinder lb = new MockLayoutBinder();
+ MockLayoutBinder lb = new MockLayoutBinder();
mExprModel = lb.getModel();
IdentifierExpr a = lb.addVariable("a", int.class.getCanonicalName(),
null);
@@ -446,8 +484,25 @@
}
@Test
+ public void testInterExprDependency() {
+ MockLayoutBinder lb = new MockLayoutBinder();
+ mExprModel = lb.getModel();
+ IdentifierExpr u = lb.addVariable("u", User.class.getCanonicalName(),
+ null);
+ final Expr uComment = parse(lb, "u.comment", FieldAccessExpr.class);
+ final TernaryExpr uTernary = parse(lb, "u.getUseComment ? u.comment : `xx`", TernaryExpr.class);
+ mExprModel.seal();
+ assertTrue(uTernary.getPred().canBeInvalidated());
+ List<Expr> shouldRead = getShouldRead();
+ assertExactMatch(shouldRead, u, uComment, uTernary.getPred());
+ assertTrue(mExprModel.markBitsRead());
+ shouldRead = getShouldRead();
+ assertExactMatch(shouldRead, uComment, uTernary);
+ }
+
+ @Test
public void testInterExprCircularDependency() {
- LayoutBinder lb = new MockLayoutBinder();
+ MockLayoutBinder lb = new MockLayoutBinder();
mExprModel = lb.getModel();
IdentifierExpr a = lb.addVariable("a", int.class.getCanonicalName(),
null);
@@ -465,7 +520,7 @@
@Test
public void testInterExprCircularDependency2() {
- LayoutBinder lb = new MockLayoutBinder();
+ MockLayoutBinder lb = new MockLayoutBinder();
mExprModel = lb.getModel();
IdentifierExpr a = lb.addVariable("a", boolean.class.getCanonicalName(),
null);
@@ -476,20 +531,15 @@
mExprModel.seal();
List<Expr> shouldRead = getShouldRead();
assertExactMatch(shouldRead, a, b);
+ assertFlags(a, a, b);
+ assertFlags(b, a, b);
List<Expr> readFirst = getReadFirst(shouldRead);
assertExactMatch(readFirst, a, b);
assertTrue(mExprModel.markBitsRead());
shouldRead = getShouldRead();
- // read a and b again, this time for their dependencies and also the rest since everything
- // is ready to be read
- assertExactMatch(shouldRead, a, b, abTernary, baTernary);
- List<Expr> justRead = new ArrayList<Expr>();
+ assertExactMatch(shouldRead, abTernary, baTernary);
readFirst = getReadFirst(shouldRead);
- assertExactMatch(readFirst, a, b);
- Collections.addAll(justRead, a, b);
- readFirst = filterOut(getReadFirst(shouldRead, justRead), justRead);
assertExactMatch(readFirst, abTernary, baTernary);
-
assertFalse(mExprModel.markBitsRead());
shouldRead = getShouldRead();
assertEquals(0, shouldRead.size());
@@ -497,7 +547,7 @@
@Test
public void testInterExprCircularDependency3() {
- LayoutBinder lb = new MockLayoutBinder();
+ MockLayoutBinder lb = new MockLayoutBinder();
mExprModel = lb.getModel();
IdentifierExpr a = lb.addVariable("a", boolean.class.getCanonicalName(),
null);
@@ -514,7 +564,7 @@
shouldRead = getShouldRead();
// read a and b again, this time for their dependencies and also the rest since everything
// is ready to be read
- assertExactMatch(shouldRead, a, b, c, abTernary, abTernary2);
+ assertExactMatch(shouldRead, c, abTernary, abTernary2);
mExprModel.markBitsRead();
shouldRead = getShouldRead();
assertEquals(0, shouldRead.size());
@@ -522,7 +572,7 @@
@Test
public void testInterExprCircularDependency4() {
- LayoutBinder lb = new MockLayoutBinder();
+ MockLayoutBinder lb = new MockLayoutBinder();
mExprModel = lb.getModel();
IdentifierExpr a = lb.addVariable("a", boolean.class.getCanonicalName(),
null);
@@ -537,6 +587,8 @@
final TernaryExpr baTernary = parse(lb, "b ? a : false", TernaryExpr.class);
mExprModel.seal();
List<Expr> shouldRead = getShouldRead();
+ // check if a,b or c should be read. these are easily calculated from binding expressions'
+ // invalidation
assertExactMatch(shouldRead, c, a, b);
List<Expr> justRead = new ArrayList<Expr>();
@@ -546,16 +598,36 @@
assertEquals(0, filterOut(getReadFirst(shouldRead, justRead), justRead).size());
assertTrue(mExprModel.markBitsRead());
shouldRead = getShouldRead();
- assertExactMatch(shouldRead, a, b, d, cTernary.getIfTrue(), cTernary, abTernary, baTernary);
+ // if a and b are not invalid, a won't be read in the first step. But if c's expression
+ // is invalid and c == true, a must be read. Depending on a, d might be read as well.
+ // don't need to read b anymore because `a ? b : true` and `b ? a : false` has the same
+ // invalidation flags.
+ assertExactMatch(shouldRead, a, abTernary, baTernary);
justRead.clear();
readFirst = getReadFirst(shouldRead);
- assertExactMatch(readFirst, a, b, d);
- Collections.addAll(justRead, a, b, d);
+ // first must read `a`.
+ assertExactMatch(readFirst, a);
+ Collections.addAll(justRead, a);
readFirst = filterOut(getReadFirst(shouldRead, justRead), justRead);
- assertExactMatch(readFirst, cTernary.getIfTrue(), abTernary, baTernary);
- Collections.addAll(justRead, cTernary.getIfTrue(), abTernary, baTernary);
+ assertExactMatch(readFirst, abTernary, baTernary);
+ Collections.addAll(justRead, abTernary, baTernary);
+
+ readFirst = filterOut(getReadFirst(shouldRead, justRead), justRead);
+ assertEquals(0, filterOut(getReadFirst(shouldRead, justRead), justRead).size());
+ assertTrue(mExprModel.markBitsRead());
+
+ shouldRead = getShouldRead();
+ // now we can read adf ternary and c ternary
+ justRead.clear();
+ assertExactMatch(shouldRead, d, cTernary.getIfTrue(), cTernary);
+ readFirst = getReadFirst(shouldRead);
+ assertExactMatch(readFirst, d);
+ Collections.addAll(justRead, d);
+ readFirst = filterOut(getReadFirst(shouldRead, justRead), justRead);
+ assertExactMatch(readFirst, cTernary.getIfTrue());
+ Collections.addAll(justRead, cTernary.getIfTrue());
readFirst = filterOut(getReadFirst(shouldRead, justRead), justRead);
assertExactMatch(readFirst, cTernary);
@@ -567,8 +639,33 @@
}
@Test
+ public void testInterExprDeepDependency() {
+ MockLayoutBinder lb = new MockLayoutBinder();
+ mExprModel = lb.getModel();
+ IdentifierExpr a = lb.addVariable("a", boolean.class.getCanonicalName(), null);
+ IdentifierExpr b = lb.addVariable("b", boolean.class.getCanonicalName(), null);
+ IdentifierExpr c = lb.addVariable("c", boolean.class.getCanonicalName(), null);
+ final TernaryExpr t1 = parse(lb, "c ? (a ? b : true) : false", TernaryExpr.class);
+ final TernaryExpr t2 = parse(lb, "c ? (b ? a : false) : true", TernaryExpr.class);
+ final TernaryExpr abTernary = (TernaryExpr) t1.getIfTrue();
+ final TernaryExpr baTernary = (TernaryExpr) t2.getIfTrue();
+ mExprModel.seal();
+ List<Expr> shouldRead = getShouldRead();
+ assertExactMatch(shouldRead, c);
+ assertTrue(mExprModel.markBitsRead());
+ shouldRead = getShouldRead();
+ assertExactMatch(shouldRead, a, b);
+ assertTrue(mExprModel.markBitsRead());
+ shouldRead = getShouldRead();
+ assertExactMatch(shouldRead, a, b, t1.getIfTrue(), t2.getIfTrue(), t1, t2);
+ assertFlags(b, abTernary.getRequirementFlagIndex(true));
+ assertFlags(a, baTernary.getRequirementFlagIndex(true));
+ assertFalse(mExprModel.markBitsRead());
+ }
+
+ @Test
public void testInterExprDependencyNotReadyYet() {
- LayoutBinder lb = new MockLayoutBinder();
+ MockLayoutBinder lb = new MockLayoutBinder();
mExprModel = lb.getModel();
IdentifierExpr a = lb.addVariable("a", boolean.class.getCanonicalName(), null);
IdentifierExpr b = lb.addVariable("b", boolean.class.getCanonicalName(), null);
@@ -592,7 +689,7 @@
@Test
public void testNoFlagsForNonBindingStatic() {
- LayoutBinder lb = new MockLayoutBinder();
+ MockLayoutBinder lb = new MockLayoutBinder();
mExprModel = lb.getModel();
lb.addVariable("a", int.class.getCanonicalName(), null);
final MathExpr parsed = parse(lb, "a * (3 + 2)", MathExpr.class);
@@ -607,7 +704,7 @@
@Test
public void testFlagsForBindingStatic() {
- LayoutBinder lb = new MockLayoutBinder();
+ MockLayoutBinder lb = new MockLayoutBinder();
mExprModel = lb.getModel();
lb.addVariable("a", int.class.getCanonicalName(), null);
final Expr staticParsed = parse(lb, "3 + 2", MathExpr.class);
@@ -625,7 +722,7 @@
@Test
public void testFinalFieldOfAVariable() {
- LayoutBinder lb = new MockLayoutBinder();
+ MockLayoutBinder lb = new MockLayoutBinder();
mExprModel = lb.getModel();
IdentifierExpr user = lb.addVariable("user", User.class.getCanonicalName(),
null);
@@ -641,7 +738,7 @@
@Test
public void testFinalFieldOfAField() {
- LayoutBinder lb = new MockLayoutBinder();
+ MockLayoutBinder lb = new MockLayoutBinder();
mExprModel = lb.getModel();
lb.addVariable("user", User.class.getCanonicalName(), null);
Expr finalFieldGet = parse(lb, "user.subObj.finalField", FieldAccessExpr.class);
@@ -660,7 +757,7 @@
@Test
public void testFinalFieldOfAMethod() {
- LayoutBinder lb = new MockLayoutBinder();
+ MockLayoutBinder lb = new MockLayoutBinder();
mExprModel = lb.getModel();
lb.addVariable("user", User.class.getCanonicalName(), null);
Expr finalFieldGet = parse(lb, "user.anotherSubObj.finalField", FieldAccessExpr.class);
@@ -679,7 +776,7 @@
@Test
public void testFinalOfAClass() {
- LayoutBinder lb = new MockLayoutBinder();
+ MockLayoutBinder lb = new MockLayoutBinder();
mExprModel = lb.getModel();
mExprModel.addImport("View", "android.view.View", null);
FieldAccessExpr fieldAccess = parse(lb, "View.VISIBLE", FieldAccessExpr.class);
@@ -690,7 +787,7 @@
@Test
public void testStaticFieldOfInstance() {
- LayoutBinder lb = new MockLayoutBinder();
+ MockLayoutBinder lb = new MockLayoutBinder();
mExprModel = lb.getModel();
lb.addVariable("myView", "android.view.View", null);
FieldAccessExpr fieldAccess = parse(lb, "myView.VISIBLE", FieldAccessExpr.class);
@@ -707,7 +804,7 @@
@Test
public void testOnDemandImportConflict() {
- LayoutBinder lb = new MockLayoutBinder();
+ MockLayoutBinder lb = new MockLayoutBinder();
mExprModel = lb.getModel();
final IdentifierExpr myView = lb.addVariable("u", "android.view.View",
null);
@@ -722,7 +819,7 @@
@Test
public void testOnDemandImportAlreadyImported() {
- LayoutBinder lb = new MockLayoutBinder();
+ MockLayoutBinder lb = new MockLayoutBinder();
mExprModel = lb.getModel();
final StaticIdentifierExpr ux = mExprModel.addImport("UX", User.class.getCanonicalName(),
null);
@@ -736,7 +833,7 @@
@Test
public void testStaticMethodOfInstance() {
- LayoutBinder lb = new MockLayoutBinder();
+ MockLayoutBinder lb = new MockLayoutBinder();
mExprModel = lb.getModel();
lb.addVariable("user", User.class.getCanonicalName(), null);
MethodCallExpr methodCall = parse(lb, "user.ourStaticMethod()", MethodCallExpr.class);
@@ -750,7 +847,7 @@
@Test
public void testFinalOfStaticField() {
- LayoutBinder lb = new MockLayoutBinder();
+ MockLayoutBinder lb = new MockLayoutBinder();
mExprModel = lb.getModel();
mExprModel.addImport("UX", User.class.getCanonicalName(), null);
FieldAccessExpr fieldAccess = parse(lb, "UX.innerStaticInstance.finalStaticField",
@@ -763,7 +860,7 @@
@Test
public void testFinalOfFinalStaticField() {
- LayoutBinder lb = new MockLayoutBinder();
+ MockLayoutBinder lb = new MockLayoutBinder();
mExprModel = lb.getModel();
mExprModel.addImport("User", User.class.getCanonicalName(), null);
FieldAccessExpr fieldAccess = parse(lb, "User.innerFinalStaticInstance.finalStaticField",
@@ -775,7 +872,7 @@
@Test
public void testLocationTracking() {
- LayoutBinder lb = new MockLayoutBinder();
+ MockLayoutBinder lb = new MockLayoutBinder();
mExprModel = lb.getModel();
final String input = "a > 3 ? b : c";
TernaryExpr ternaryExpr = parse(lb, input, TernaryExpr.class);
@@ -826,7 +923,7 @@
// TODO uncomment when we have inner static access
// @Test
// public void testFinalOfInnerStaticClass() {
-// LayoutBinder lb = new MockLayoutBinder();
+// MockLayoutBinder lb = new MockLayoutBinder();
// mExprModel = lb.getModel();
// mExprModel.addImport("User", User.class.getCanonicalName());
// FieldAccessExpr fieldAccess = parse(lb, "User.InnerStaticClass.finalStaticField", FieldAccessExpr.class);
@@ -861,7 +958,9 @@
private void assertExactMatch(List<Expr> iterable, Expr... exprs) {
int i = 0;
- String log = Arrays.toString(iterable.toArray());
+ String listLog = Arrays.toString(iterable.toArray());
+ String itemsLog = Arrays.toString(exprs);
+ String log = "list: " + listLog + "\nitems: " + itemsLog;
log("list", iterable);
for (Expr expr : exprs) {
assertTrue((i++) + ":must contain " + expr.getUniqueKey() + "\n" + log,
@@ -869,13 +968,13 @@
}
i = 0;
for (Expr expr : iterable) {
- assertTrue((i++) + ":must be expected " + expr.getUniqueKey(),
- ArrayUtils.contains(exprs, expr));
+ assertTrue((i++) + ":must be expected " + expr.getUniqueKey() + "\n" + log,
+ Arrays.asList(exprs).contains(expr));
}
}
private <T extends Expr> T parse(LayoutBinder binder, String input, Class<T> klass) {
- final Expr parsed = binder.parse(input, null);
+ final Expr parsed = binder.parse(input, false, null);
assertTrue(klass.isAssignableFrom(parsed.getClass()));
return (T) parsed;
}
@@ -904,10 +1003,10 @@
}
private List<Expr> getShouldRead() {
- return mExprModel.filterShouldRead(mExprModel.getPendingExpressions());
+ return ExprModel.filterShouldRead(mExprModel.getPendingExpressions());
}
- public static class User extends BaseObservable {
+ public static class User implements Observable {
String name;
@@ -941,6 +1040,23 @@
return true;
}
+ public String comment;
+
+ @Bindable
+ public boolean getUseComment() {
+ return true;
+ }
+
+ @Override
+ public void addOnPropertyChangedCallback(OnPropertyChangedCallback callback) {
+
+ }
+
+ @Override
+ public void removeOnPropertyChangedCallback(OnPropertyChangedCallback callback) {
+
+ }
+
public static class InnerStaticClass {
public static final int finalField = 3;
diff --git a/compiler/src/test/java/android/databinding/tool/expr/ExprTest.java b/compiler/src/test/java/android/databinding/tool/expr/ExprTest.java
index 8c9c367..61d04cb 100644
--- a/compiler/src/test/java/android/databinding/tool/expr/ExprTest.java
+++ b/compiler/src/test/java/android/databinding/tool/expr/ExprTest.java
@@ -16,14 +16,15 @@
package android.databinding.tool.expr;
-import org.junit.Before;
-import org.junit.Test;
-
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.writer.KCode;
+
+import org.junit.Before;
+import org.junit.Test;
import java.util.BitSet;
import java.util.List;
@@ -55,6 +56,16 @@
}
@Override
+ protected KCode generateCode(boolean full) {
+ return new KCode();
+ }
+
+ @Override
+ protected String getInvertibleError() {
+ return null;
+ }
+
+ @Override
public boolean isDynamic() {
return true;
}
@@ -77,6 +88,16 @@
protected List<Dependency> constructDependencies() {
return constructDynamicChildrenDependencies();
}
+
+ @Override
+ protected KCode generateCode(boolean full) {
+ return new KCode();
+ }
+
+ @Override
+ protected String getInvertibleError() {
+ return null;
+ }
};
expr.getUniqueKey();
}
diff --git a/compiler/src/test/java/android/databinding/tool/reflection/SdkVersionTest.java b/compiler/src/test/java/android/databinding/tool/reflection/SdkVersionTest.java
index 0045664..b4ecb01 100644
--- a/compiler/src/test/java/android/databinding/tool/reflection/SdkVersionTest.java
+++ b/compiler/src/test/java/android/databinding/tool/reflection/SdkVersionTest.java
@@ -56,4 +56,12 @@
ModelMethod setElevation = view.getMethods("testCustomCode", 0)[0];
assertEquals(1, SdkUtil.getMinApi(setElevation));
}
+
+ @Test
+ public void testSetForeground() {
+ ModelClass view = ModelAnalyzer.getInstance()
+ .findClass("android.widget.FrameLayout", null);
+ ModelMethod setForeground = view.getMethods("setForeground", 1)[0];
+ assertEquals(1, SdkUtil.getMinApi(setForeground));
+ }
}
diff --git a/compiler/src/test/java/android/databinding/tool/reflection/java/JavaAnalyzer.java b/compiler/src/test/java/android/databinding/tool/reflection/java/JavaAnalyzer.java
index dea092b..1b97cd9 100644
--- a/compiler/src/test/java/android/databinding/tool/reflection/java/JavaAnalyzer.java
+++ b/compiler/src/test/java/android/databinding/tool/reflection/java/JavaAnalyzer.java
@@ -13,6 +13,11 @@
package android.databinding.tool.reflection.java;
+import com.google.common.base.Splitter;
+import com.google.common.base.Strings;
+
+import org.apache.commons.io.FileUtils;
+
import android.databinding.tool.reflection.ModelAnalyzer;
import android.databinding.tool.reflection.ModelClass;
import android.databinding.tool.reflection.SdkUtil;
@@ -20,10 +25,12 @@
import android.databinding.tool.util.L;
import java.io.File;
+import java.io.IOException;
import java.net.MalformedURLException;
import java.net.URL;
import java.net.URLClassLoader;
import java.util.HashMap;
+import java.util.List;
import java.util.Map;
public class JavaAnalyzer extends ModelAnalyzer {
@@ -124,13 +131,40 @@
}
}
- public static void initForTests() {
+ private static String loadAndroidHome() {
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) {
+ if(env.containsKey("ANDROID_HOME")) {
+ return env.get("ANDROID_HOME");
+ }
+ // check for local.properties file
+ File folder = new File(".").getAbsoluteFile();
+ while (folder != null && folder.exists()) {
+ File f = new File(folder, "local.properties");
+ if (f.exists() && f.canRead()) {
+ try {
+ for (String line : FileUtils.readLines(f)) {
+ List<String> keyValue = Splitter.on('=').splitToList(line);
+ if (keyValue.size() == 2) {
+ String key = keyValue.get(0).trim();
+ if (key.equals("sdk.dir")) {
+ return keyValue.get(1).trim();
+ }
+ }
+ }
+ } catch (IOException ignored) {}
+ }
+ folder = folder.getParentFile();
+ }
+
+ return null;
+ }
+
+ public static void initForTests() {
+ String androidHome = loadAndroidHome();
+ if (Strings.isNullOrEmpty(androidHome) || !new File(androidHome).exists()) {
throw new IllegalStateException(
"you need to have ANDROID_HOME set in your environment"
+ " to run compiler tests");
diff --git a/compiler/src/test/java/android/databinding/tool/reflection/java/JavaClass.java b/compiler/src/test/java/android/databinding/tool/reflection/java/JavaClass.java
index cc6891e..3136e7f 100644
--- a/compiler/src/test/java/android/databinding/tool/reflection/java/JavaClass.java
+++ b/compiler/src/test/java/android/databinding/tool/reflection/java/JavaClass.java
@@ -125,6 +125,11 @@
}
@Override
+ public boolean isWildcard() {
+ return false;
+ }
+
+ @Override
public boolean isInterface() {
return mClass.isInterface();
}
diff --git a/compiler/src/test/java/android/databinding/tool/writer/FlagSetTest.java b/compiler/src/test/java/android/databinding/tool/writer/FlagSetTest.java
index 327593a..cf322fd 100644
--- a/compiler/src/test/java/android/databinding/tool/writer/FlagSetTest.java
+++ b/compiler/src/test/java/android/databinding/tool/writer/FlagSetTest.java
@@ -57,4 +57,13 @@
assertEquals(1 << 2, flagSet.buckets[1]);
assertEquals(1 << 10, flagSet.buckets[2]);
}
+
+ @Test
+ public void testLargeValue() {
+ BitSet bs = new BitSet();
+ bs.set(43);
+ FlagSet flagSet = new FlagSet(bs, 1);
+ assertEquals(1, flagSet.buckets.length);
+ assertEquals(1L << 43, flagSet.buckets[0]);
+ }
}
diff --git a/compilerCommon/build.gradle b/compilerCommon/build.gradle
index 61477da..91ce05d 100644
--- a/compilerCommon/build.gradle
+++ b/compilerCommon/build.gradle
@@ -15,10 +15,9 @@
*/
apply plugin: 'java'
-apply plugin: 'com.android.databinding.bintray'
-sourceCompatibility = config.javaTargetCompatibility
-targetCompatibility = config.javaSourceCompatibility
+sourceCompatibility = dataBindingConfig.javaSourceCompatibility
+targetCompatibility = dataBindingConfig.javaTargetCompatibility
sourceSets {
main {
@@ -37,11 +36,12 @@
}
dependencies {
- testCompile group: 'junit', name: 'junit', version: '4.12'
- compile project(':baseLibrary')
- compile 'org.apache.commons:commons-lang3:3.3.2'
+ testCompile 'junit:junit:4.12'
+ compile project(':dataBinding:baseLibrary')
compile 'com.tunnelvisionlabs:antlr4:4.5'
compile 'commons-io:commons-io:2.4'
+ compile 'com.googlecode.juniversalchardet:juniversalchardet:1.0.3'
+ compile 'com.google.guava:guava:17.0'
}
project.tasks.create(name : "generateXmlParser", type : JavaExec) {
@@ -57,3 +57,27 @@
args "BindingExpression.g4", "-visitor", "-o", "src/main/grammar-gen/android/databinding/parser", "-package", "android.databinding.parser"
}
+tasks.create(name : 'exportBuildVersions') << {
+ def props = new HashMap();
+ def buildVersionFile = new File(sourceSets.main.output.resourcesDir,"data_binding_version_info.properties")
+ // Using Java Properties appends date to the output which is bad for incremental compilation.
+ // Instead, we build it manually.
+ props.put("compilerCommon", project.version)
+ props.put("compiler", rootProject.findProject("dataBinding:compiler").version)
+ props.put("baseLibrary", rootProject.findProject("dataBinding:baseLibrary").version)
+ props.put("extensions", dataBindingConfig.extensionsVersion)
+ buildVersionFile.getParentFile().mkdirs()
+ println("writing build versions file to $buildVersionFile")
+ def propText = new StringBuilder();
+ props.each {
+ propText.append(it.key).append("=").append(it.value).append(System.getProperty("line.separator"))
+ }
+ file(buildVersionFile).write(propText.toString())
+}
+
+tasks['jar'].dependsOn('exportBuildVersions')
+tasks['exportBuildVersions'].dependsOn('processResources')
+
+project.ext.pomName = 'Data Binding Compiler Common'
+project.ext.pomDesc = 'Common library that can be shared between different build tools'
+enablePublishing(this, true)
diff --git a/compilerCommon/compilerCommon.iml b/compilerCommon/compilerCommon.iml
new file mode 100644
index 0000000..d9cc2c6
--- /dev/null
+++ b/compilerCommon/compilerCommon.iml
@@ -0,0 +1,32 @@
+<?xml version="1.0" encoding="UTF-8"?>
+<module external.linked.project.id=":dataBinding:compilerCommon" external.linked.project.path="$MODULE_DIR$" external.root.project.path="$MODULE_DIR$/.." external.system.id="GRADLE" external.system.module.group="com.android.databinding" external.system.module.version="1.1" type="JAVA_MODULE" version="4">
+ <component name="NewModuleRootManager" inherit-compiler-output="false">
+ <output url="file://$MODULE_DIR$/build/classes/main" />
+ <output-test url="file://$MODULE_DIR$/build/classes/test" />
+ <exclude-output />
+ <content url="file://$MODULE_DIR$">
+ <sourceFolder url="file://$MODULE_DIR$/src/main/grammar-gen" isTestSource="false" />
+ <sourceFolder url="file://$MODULE_DIR$/src/main/java" isTestSource="false" />
+ <sourceFolder url="file://$MODULE_DIR$/src/main/xml-gen" isTestSource="false" />
+ <sourceFolder url="file://$MODULE_DIR$/src/test/java" isTestSource="true" />
+ <sourceFolder url="file://$MODULE_DIR$/src/main/resources" type="java-resource" />
+ <sourceFolder url="file://$MODULE_DIR$/src/test/resources" type="java-test-resource" />
+ <excludeFolder url="file://$MODULE_DIR$/.gradle" />
+ <excludeFolder url="file://$MODULE_DIR$/build" />
+ </content>
+ <orderEntry type="inheritedJdk" />
+ <orderEntry type="sourceFolder" forTests="false" />
+ <orderEntry type="module" module-name="baseLibrary" />
+ <orderEntry type="library" name="Gradle: com.tunnelvisionlabs:antlr4:4.5" level="project" />
+ <orderEntry type="library" name="Gradle: commons-io:commons-io:2.4" level="project" />
+ <orderEntry type="library" name="Gradle: com.googlecode.juniversalchardet:juniversalchardet:1.0.3" level="project" />
+ <orderEntry type="library" name="Gradle: com.tunnelvisionlabs:antlr4-runtime:4.5" level="project" />
+ <orderEntry type="library" name="Gradle: com.tunnelvisionlabs:antlr4-annotations:4.5" level="project" />
+ <orderEntry type="library" name="Gradle: org.antlr:antlr-runtime:3.5.2" level="project" />
+ <orderEntry type="library" name="Gradle: org.antlr:ST4:4.0.8" level="project" />
+ <orderEntry type="library" name="Gradle: org.abego.treelayout:org.abego.treelayout.core:1.0.1" level="project" />
+ <orderEntry type="library" scope="TEST" name="Gradle: junit:junit:4.12" level="project" />
+ <orderEntry type="library" scope="TEST" name="Gradle: org.hamcrest:hamcrest-core:1.3" level="project" />
+ <orderEntry type="library" name="Gradle: com.google.guava:guava:17.0" level="project" />
+ </component>
+</module>
\ No newline at end of file
diff --git a/compilerCommon/db-compilerCommon-base.iml b/compilerCommon/db-compilerCommon-base.iml
new file mode 100644
index 0000000..38f8122
--- /dev/null
+++ b/compilerCommon/db-compilerCommon-base.iml
@@ -0,0 +1,114 @@
+<?xml version="1.0" encoding="UTF-8"?>
+<module relativePaths="true" type="JAVA_MODULE" version="4">
+ <component name="NewModuleRootManager" inherit-compiler-output="true">
+ <exclude-output />
+ <content url="file://$MODULE_DIR$">
+ <sourceFolder url="file://$MODULE_DIR$/src/main/java" isTestSource="false" />
+ <sourceFolder url="file://$MODULE_DIR$/src/main/xml-gen" isTestSource="false" />
+ <sourceFolder url="file://$MODULE_DIR$/src/main/grammar-gen" isTestSource="false" />
+ <sourceFolder url="file://$MODULE_DIR$/src/test/java" isTestSource="true" />
+ <excludeFolder url="file://$MODULE_DIR$/../../../out/build/dataBinding/compilerCommon/build" />
+ <excludeFolder url="file://$MODULE_DIR$/.gradle" />
+ </content>
+ <orderEntry type="inheritedJdk" />
+ <orderEntry type="sourceFolder" forTests="false" />
+ <orderEntry type="module" module-name="db-baseLibrary" exported="" />
+ <orderEntry type="module-library" exported="">
+ <library>
+ <CLASSES>
+ <root url="jar://$MODULE_DIR$/../../../prebuilts/tools/common/m2/repository/com/tunnelvisionlabs/antlr4/4.5/antlr4-4.5.jar!/" />
+ </CLASSES>
+ <JAVADOC />
+ <SOURCES />
+ </library>
+ </orderEntry>
+ <orderEntry type="module-library" exported="">
+ <library>
+ <CLASSES>
+ <root url="jar://$MODULE_DIR$/../../../prebuilts/tools/common/m2/repository/commons-io/commons-io/2.4/commons-io-2.4.jar!/" />
+ </CLASSES>
+ <JAVADOC />
+ <SOURCES>
+ <root url="jar://$MODULE_DIR$/../../../prebuilts/tools/common/m2/repository/commons-io/commons-io/2.4/commons-io-2.4-sources.jar!/" />
+ </SOURCES>
+ </library>
+ </orderEntry>
+ <orderEntry type="module-library" exported="">
+ <library>
+ <CLASSES>
+ <root url="jar://$MODULE_DIR$/../../../prebuilts/tools/common/m2/repository/com/googlecode/juniversalchardet/juniversalchardet/1.0.3/juniversalchardet-1.0.3.jar!/" />
+ </CLASSES>
+ <JAVADOC />
+ <SOURCES />
+ </library>
+ </orderEntry>
+ <orderEntry type="module-library" exported="">
+ <library>
+ <CLASSES>
+ <root url="jar://$MODULE_DIR$/../../../prebuilts/tools/common/m2/repository/com/tunnelvisionlabs/antlr4-runtime/4.5/antlr4-runtime-4.5.jar!/" />
+ </CLASSES>
+ <JAVADOC />
+ <SOURCES />
+ </library>
+ </orderEntry>
+ <orderEntry type="module-library" exported="">
+ <library>
+ <CLASSES>
+ <root url="jar://$MODULE_DIR$/../../../prebuilts/tools/common/m2/repository/com/tunnelvisionlabs/antlr4-annotations/4.5/antlr4-annotations-4.5.jar!/" />
+ </CLASSES>
+ <JAVADOC />
+ <SOURCES />
+ </library>
+ </orderEntry>
+ <orderEntry type="module-library" exported="">
+ <library>
+ <CLASSES>
+ <root url="jar://$MODULE_DIR$/../../../prebuilts/tools/common/m2/repository/org/antlr/antlr-runtime/3.5.2/antlr-runtime-3.5.2.jar!/" />
+ </CLASSES>
+ <JAVADOC />
+ <SOURCES />
+ </library>
+ </orderEntry>
+ <orderEntry type="module-library" exported="">
+ <library>
+ <CLASSES>
+ <root url="jar://$MODULE_DIR$/../../../prebuilts/tools/common/m2/repository/org/antlr/ST4/4.0.8/ST4-4.0.8.jar!/" />
+ </CLASSES>
+ <JAVADOC />
+ <SOURCES />
+ </library>
+ </orderEntry>
+ <orderEntry type="module-library" exported="">
+ <library>
+ <CLASSES>
+ <root url="jar://$MODULE_DIR$/../../../prebuilts/tools/common/m2/repository/org/abego/treelayout/org.abego.treelayout.core/1.0.1/org.abego.treelayout.core-1.0.1.jar!/" />
+ </CLASSES>
+ <JAVADOC />
+ <SOURCES />
+ </library>
+ </orderEntry>
+ <orderEntry type="module-library" scope="TEST">
+ <library>
+ <CLASSES>
+ <root url="jar://$MODULE_DIR$/../../../prebuilts/tools/common/m2/repository/junit/junit/4.12/junit-4.12.jar!/" />
+ </CLASSES>
+ <JAVADOC />
+ <SOURCES>
+ <root url="jar://$MODULE_DIR$/../../../prebuilts/tools/common/m2/repository/junit/junit/4.12/junit-4.12-sources.jar!/" />
+ </SOURCES>
+ </library>
+ </orderEntry>
+ <orderEntry type="module-library" scope="TEST">
+ <library>
+ <CLASSES>
+ <root url="jar://$MODULE_DIR$/../../../prebuilts/tools/common/m2/repository/org/hamcrest/hamcrest-core/1.3/hamcrest-core-1.3.jar!/" />
+ </CLASSES>
+ <JAVADOC />
+ <SOURCES>
+ <root url="jar://$MODULE_DIR$/../../../prebuilts/tools/common/m2/repository/org/hamcrest/hamcrest-core/1.3/hamcrest-core-1.3-sources.jar!/" />
+ </SOURCES>
+ </library>
+ </orderEntry>
+ <orderEntry type="library" exported="" name="guava-tools" level="project" />
+ </component>
+</module>
\ No newline at end of file
diff --git a/compilerCommon/db-compilerCommon.iml b/compilerCommon/db-compilerCommon.iml
new file mode 100644
index 0000000..3b18b8a
--- /dev/null
+++ b/compilerCommon/db-compilerCommon.iml
@@ -0,0 +1,53 @@
+<?xml version="1.0" encoding="UTF-8"?>
+<module relativePaths="true" type="JAVA_MODULE" version="4">
+ <component name="NewModuleRootManager" inherit-compiler-output="true">
+ <exclude-output />
+ <content url="file://$MODULE_DIR$">
+ <sourceFolder url="file://$MODULE_DIR$/src/main/java" isTestSource="false" />
+ <sourceFolder url="file://$MODULE_DIR$/src/main/xml-gen" isTestSource="false" />
+ <sourceFolder url="file://$MODULE_DIR$/src/main/grammar-gen" isTestSource="false" />
+ <sourceFolder url="file://$MODULE_DIR$/src/test/java" isTestSource="true" />
+ <excludeFolder url="file://$MODULE_DIR$/../../../out/build/dataBinding/compilerCommon/build" />
+ <excludeFolder url="file://$MODULE_DIR$/.gradle" />
+ <excludeFolder url="file://$MODULE_DIR$/build" />
+ </content>
+ <orderEntry type="inheritedJdk" />
+ <orderEntry type="sourceFolder" forTests="false" />
+ <orderEntry type="module" module-name="db-baseLibrary" />
+ <orderEntry type="library" exported="" name="Guava" level="project" />
+ <orderEntry type="library" exported="" name="commons-io-2.4" level="project" />
+ <orderEntry type="module-library" exported="">
+ <library name="juniversalchardet-1.0.3">
+ <CLASSES>
+ <root url="jar://$MODULE_DIR$/../../adt/idea/android/lib/juniversalchardet-1.0.3.jar!/" />
+ </CLASSES>
+ <JAVADOC />
+ <SOURCES />
+ </library>
+ </orderEntry>
+ <orderEntry type="library" exported="" name="antlr4-runtime-4.5" level="project" />
+ <orderEntry type="library" exported="" name="antlr4-annotations-4.5" level="project" />
+ <orderEntry type="module-library" scope="TEST">
+ <library>
+ <CLASSES>
+ <root url="jar://$MODULE_DIR$/../../../prebuilts/tools/common/m2/repository/junit/junit/4.12/junit-4.12.jar!/" />
+ </CLASSES>
+ <JAVADOC />
+ <SOURCES>
+ <root url="jar://$MODULE_DIR$/../../../prebuilts/tools/common/m2/repository/junit/junit/4.12/junit-4.12-sources.jar!/" />
+ </SOURCES>
+ </library>
+ </orderEntry>
+ <orderEntry type="module-library" scope="TEST">
+ <library>
+ <CLASSES>
+ <root url="jar://$MODULE_DIR$/../../../prebuilts/tools/common/m2/repository/org/hamcrest/hamcrest-core/1.3/hamcrest-core-1.3.jar!/" />
+ </CLASSES>
+ <JAVADOC />
+ <SOURCES>
+ <root url="jar://$MODULE_DIR$/../../../prebuilts/tools/common/m2/repository/org/hamcrest/hamcrest-core/1.3/hamcrest-core-1.3-sources.jar!/" />
+ </SOURCES>
+ </library>
+ </orderEntry>
+ </component>
+</module>
\ No newline at end of file
diff --git a/compilerCommon/src/main/java/android/databinding/tool/DataBindingBuilder.java b/compilerCommon/src/main/java/android/databinding/tool/DataBindingBuilder.java
new file mode 100644
index 0000000..0e5d757
--- /dev/null
+++ b/compilerCommon/src/main/java/android/databinding/tool/DataBindingBuilder.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.tool;
+
+import org.apache.commons.io.IOUtils;
+
+import android.databinding.tool.processing.Scope;
+import android.databinding.tool.processing.ScopedException;
+import android.databinding.tool.util.L;
+import android.databinding.tool.util.Preconditions;
+import android.databinding.tool.writer.JavaFileWriter;
+
+import java.io.File;
+import java.io.FileInputStream;
+import java.io.FileNotFoundException;
+import java.io.FileOutputStream;
+import java.io.IOException;
+import java.io.InputStream;
+import java.util.ArrayList;
+import java.util.HashMap;
+import java.util.List;
+import java.util.Map;
+import java.util.Properties;
+
+
+/**
+ * This class is used by Android Gradle plugin.
+ */
+@SuppressWarnings("unused")
+public class DataBindingBuilder {
+ Versions mVersions;
+ private final static String EXCLUDE_PATTERN = "android/databinding/layouts/*.*";
+ public String getCompilerVersion() {
+ return getVersions().compiler;
+ }
+
+ public String getBaseLibraryVersion(String compilerVersion) {
+ return getVersions().baseLibrary;
+ }
+
+ public String getLibraryVersion(String compilerVersion) {
+ return getVersions().extensions;
+ }
+
+ public String getBaseAdaptersVersion(String compilerVersion) {
+ return getVersions().extensions;
+ }
+
+ public void setPrintMachineReadableOutput(boolean machineReadableOutput) {
+ ScopedException.encodeOutput(machineReadableOutput);
+ }
+
+ public boolean getPrintMachineReadableOutput() {
+ return ScopedException.issEncodeOutput();
+ }
+
+ public void setDebugLogEnabled(boolean enableDebugLogs) {
+ L.setDebugLog(enableDebugLogs);
+ }
+
+ public boolean isDebugLogEnabled() {
+ return L.isDebugEnabled();
+ }
+
+ private Versions getVersions() {
+ if (mVersions != null) {
+ return mVersions;
+ }
+ try {
+ Properties props = new Properties();
+ InputStream stream = getClass().getResourceAsStream("/data_binding_version_info.properties");
+ try {
+ props.load(stream);
+ mVersions = new Versions(props);
+ } finally {
+ IOUtils.closeQuietly(stream);
+ }
+ } catch (IOException exception) {
+ L.e(exception, "Cannot read data binding version");
+ }
+ return mVersions;
+ }
+
+ /**
+ * Returns the list of classes that should be excluded from package task
+ *
+ * @param layoutXmlProcessor The LayoutXmlProcessor for this variant
+ * @param generatedClassListFile The location of the File into which data binding compiler wrote
+ * list of generated classes
+ *
+ * @return The list of classes to exclude. They are already in JNI format.
+ */
+ public List<String> getJarExcludeList(LayoutXmlProcessor layoutXmlProcessor,
+ File generatedClassListFile) {
+ List<String> excludes = new ArrayList<String>();
+ String appPkgAsClass = layoutXmlProcessor.getPackage().replace('.', '/');
+ String infoClassAsClass = layoutXmlProcessor.getInfoClassFullName().replace('.', '/');
+
+ excludes.add(infoClassAsClass + ".class");
+ excludes.add(EXCLUDE_PATTERN);
+ excludes.add(appPkgAsClass + "/BR.*");
+ excludes.add("android/databinding/DynamicUtil.class");
+ if (generatedClassListFile != null) {
+ List<String> generatedClasses = readGeneratedClasses(generatedClassListFile);
+ for (String klass : generatedClasses) {
+ excludes.add(klass.replace('.', '/') + ".class");
+ }
+ }
+ Scope.assertNoError();
+ return excludes;
+ }
+
+ private List<String> readGeneratedClasses(File generatedClassListFile) {
+ Preconditions.checkNotNull(generatedClassListFile, "Data binding exclude generated task"
+ + " is not configured properly");
+ Preconditions.check(generatedClassListFile.exists(),
+ "Generated class list does not exist %s", generatedClassListFile.getAbsolutePath());
+ FileInputStream fis = null;
+ try {
+ fis = new FileInputStream(generatedClassListFile);
+ return IOUtils.readLines(fis);
+ } catch (FileNotFoundException e) {
+ L.e(e, "Unable to read generated class list from %s",
+ generatedClassListFile.getAbsoluteFile());
+ } catch (IOException e) {
+ L.e(e, "Unexpected exception while reading %s",
+ generatedClassListFile.getAbsoluteFile());
+ } finally {
+ IOUtils.closeQuietly(fis);
+ }
+ L.e("Could not read data binding generated class list");
+ return null;
+ }
+
+ public JavaFileWriter createJavaFileWriter(File outFolder) {
+ return new GradleFileWriter(outFolder.getAbsolutePath());
+ }
+
+ static class GradleFileWriter extends JavaFileWriter {
+
+ private final String outputBase;
+
+ public GradleFileWriter(String outputBase) {
+ this.outputBase = outputBase;
+ }
+
+ @Override
+ public void writeToFile(String canonicalName, String contents) {
+ String asPath = canonicalName.replace('.', '/');
+ File f = new File(outputBase + "/" + asPath + ".java");
+ //noinspection ResultOfMethodCallIgnored
+ f.getParentFile().mkdirs();
+ FileOutputStream fos = null;
+ try {
+ fos = new FileOutputStream(f);
+ IOUtils.write(contents, fos);
+ } catch (IOException e) {
+ L.e(e, "cannot write file " + f.getAbsolutePath());
+ } finally {
+ IOUtils.closeQuietly(fos);
+ }
+ }
+ }
+
+ private static class Versions {
+ final String compilerCommon;
+ final String compiler;
+ final String baseLibrary;
+ final String extensions;
+
+ public Versions(Properties properties) {
+ compilerCommon = properties.getProperty("compilerCommon");
+ compiler = properties.getProperty("compiler");
+ baseLibrary = properties.getProperty("baseLibrary");
+ extensions = properties.getProperty("extensions");
+ Preconditions.checkNotNull(compilerCommon, "cannot read compiler common version");
+ Preconditions.checkNotNull(compiler, "cannot read compiler version");
+ Preconditions.checkNotNull(baseLibrary, "cannot read baseLibrary version");
+ Preconditions.checkNotNull(extensions, "cannot read extensions version");
+ }
+ }
+}
\ No newline at end of file
diff --git a/compilerCommon/src/main/java/android/databinding/tool/LayoutXmlProcessor.java b/compilerCommon/src/main/java/android/databinding/tool/LayoutXmlProcessor.java
index 046bddb..41436f0 100644
--- a/compilerCommon/src/main/java/android/databinding/tool/LayoutXmlProcessor.java
+++ b/compilerCommon/src/main/java/android/databinding/tool/LayoutXmlProcessor.java
@@ -13,25 +13,28 @@
package android.databinding.tool;
-import org.apache.commons.lang3.StringEscapeUtils;
+import com.google.common.escape.Escaper;
+
+import org.apache.commons.io.FileUtils;
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.util.L;
+import android.databinding.tool.util.Preconditions;
+import android.databinding.tool.util.SourceCodeEscapers;
import android.databinding.tool.writer.JavaFileWriter;
import java.io.File;
import java.io.FilenameFilter;
import java.io.IOException;
-import java.io.StringWriter;
+import java.net.URI;
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;
@@ -52,36 +55,82 @@
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;
+ private final OriginalFileLookup mOriginalFileLookup;
- public LayoutXmlProcessor(String applicationPackage, List<File> resources,
- JavaFileWriter fileWriter, int minSdk, boolean isLibrary) {
+ public LayoutXmlProcessor(String applicationPackage,
+ JavaFileWriter fileWriter, int minSdk, boolean isLibrary,
+ OriginalFileLookup originalFileLookup) {
mFileWriter = fileWriter;
mResourceBundle = new ResourceBundle(applicationPackage);
- mResources = resources;
mMinSdk = minSdk;
mIsLibrary = isLibrary;
+ mOriginalFileLookup = originalFileLookup;
}
- public static List<File> getLayoutFiles(List<File> resources) {
- List<File> result = new ArrayList<File>();
- for (File resource : resources) {
- if (!resource.exists() || !resource.canRead()) {
- continue;
- }
- if (resource.isDirectory()) {
- for (File layoutFolder : resource.listFiles(layoutFolderFilter)) {
- for (File xmlFile : layoutFolder.listFiles(xmlFileFilter)) {
- result.add(xmlFile);
- }
+ private static void processIncrementalInputFiles(ResourceInput input,
+ ProcessFileCallback callback)
+ throws IOException, ParserConfigurationException, XPathExpressionException,
+ SAXException {
+ processExistingIncrementalFiles(input.getRootInputFolder(), input.getAdded(), callback);
+ processExistingIncrementalFiles(input.getRootInputFolder(), input.getChanged(), callback);
+ processRemovedIncrementalFiles(input.getRootInputFolder(), input.getRemoved(), callback);
+ }
- }
- } else if (xmlFileFilter.accept(resource.getParentFile(), resource.getName())) {
- result.add(resource);
+ private static void processExistingIncrementalFiles(File inputRoot, List<File> files,
+ ProcessFileCallback callback)
+ throws IOException, XPathExpressionException, SAXException,
+ ParserConfigurationException {
+ for (File file : files) {
+ File parent = file.getParentFile();
+ if (inputRoot.equals(parent)) {
+ callback.processOtherRootFile(file);
+ } else if (layoutFolderFilter.accept(parent, parent.getName())) {
+ callback.processLayoutFile(file);
+ } else {
+ callback.processOtherFile(parent, file);
}
}
- return result;
+ }
+
+ private static void processRemovedIncrementalFiles(File inputRoot, List<File> files,
+ ProcessFileCallback callback)
+ throws IOException {
+ for (File file : files) {
+ File parent = file.getParentFile();
+ if (inputRoot.equals(parent)) {
+ callback.processRemovedOtherRootFile(file);
+ } else if (layoutFolderFilter.accept(parent, parent.getName())) {
+ callback.processRemovedLayoutFile(file);
+ } else {
+ callback.processRemovedOtherFile(parent, file);
+ }
+ }
+ }
+
+ private static void processAllInputFiles(ResourceInput input, ProcessFileCallback callback)
+ throws IOException, XPathExpressionException, SAXException,
+ ParserConfigurationException {
+ FileUtils.deleteDirectory(input.getRootOutputFolder());
+ Preconditions.check(input.getRootOutputFolder().mkdirs(), "out dir should be re-created");
+ Preconditions.check(input.getRootInputFolder().isDirectory(), "it must be a directory");
+ for (File firstLevel : input.getRootInputFolder().listFiles()) {
+ if (firstLevel.isDirectory()) {
+ if (layoutFolderFilter.accept(firstLevel, firstLevel.getName())) {
+ callback.processLayoutFolder(firstLevel);
+ for (File xmlFile : firstLevel.listFiles(xmlFileFilter)) {
+ callback.processLayoutFile(xmlFile);
+ }
+ } else {
+ callback.processOtherFolder(firstLevel);
+ for (File file : firstLevel.listFiles()) {
+ callback.processOtherFile(firstLevel, file);
+ }
+ }
+ } else {
+ callback.processOtherRootFile(firstLevel);
+ }
+
+ }
}
/**
@@ -91,74 +140,142 @@
return mResourceBundle;
}
- public boolean processResources(int minSdk)
+ public boolean processResources(final ResourceInput input)
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(), minSdk);
- if (bindingLayout != null && !bindingLayout.isEmpty()) {
- mResourceBundle.addLayoutBundle(bindingLayout);
+ final LayoutFileParser layoutFileParser = new LayoutFileParser();
+ final URI inputRootUri = input.getRootInputFolder().toURI();
+ ProcessFileCallback callback = new ProcessFileCallback() {
+ private File convertToOutFile(File file) {
+ final String subPath = toSystemDependentPath(inputRootUri
+ .relativize(file.toURI()).getPath());
+ return new File(input.getRootOutputFolder(), subPath);
}
+ @Override
+ public void processLayoutFile(File file)
+ throws ParserConfigurationException, SAXException, XPathExpressionException,
+ IOException {
+ final File output = convertToOutFile(file);
+ final ResourceBundle.LayoutFileBundle bindingLayout = layoutFileParser
+ .parseXml(file, output, mResourceBundle.getAppPackage(), mOriginalFileLookup);
+ if (bindingLayout != null && !bindingLayout.isEmpty()) {
+ mResourceBundle.addLayoutBundle(bindingLayout);
+ }
+ }
+
+ @Override
+ public void processOtherFile(File parentFolder, File file) throws IOException {
+ final File outParent = convertToOutFile(parentFolder);
+ FileUtils.copyFile(file, new File(outParent, file.getName()));
+ }
+
+ @Override
+ public void processRemovedLayoutFile(File file) {
+ mResourceBundle.addRemovedFile(file);
+ final File out = convertToOutFile(file);
+ FileUtils.deleteQuietly(out);
+ }
+
+ @Override
+ public void processRemovedOtherFile(File parentFolder, File file) throws IOException {
+ final File outParent = convertToOutFile(parentFolder);
+ FileUtils.deleteQuietly(new File(outParent, file.getName()));
+ }
+
+ @Override
+ public void processOtherFolder(File folder) {
+ //noinspection ResultOfMethodCallIgnored
+ convertToOutFile(folder).mkdirs();
+ }
+
+ @Override
+ public void processLayoutFolder(File folder) {
+ //noinspection ResultOfMethodCallIgnored
+ convertToOutFile(folder).mkdirs();
+ }
+
+ @Override
+ public void processOtherRootFile(File file) throws IOException {
+ final File outFile = convertToOutFile(file);
+ if (file.isDirectory()) {
+ FileUtils.copyDirectory(file, outFile);
+ } else {
+ FileUtils.copyFile(file, outFile);
+ }
+ }
+
+ @Override
+ public void processRemovedOtherRootFile(File file) throws IOException {
+ final File outFile = convertToOutFile(file);
+ FileUtils.deleteQuietly(outFile);
+ }
+ };
+ if (input.isIncremental()) {
+ processIncrementalInputFiles(input, callback);
+ } else {
+ processAllInputFiles(input, callback);
}
mProcessingComplete = true;
return true;
}
+ public static String toSystemDependentPath(String path) {
+ if (File.separatorChar != '/') {
+ path = path.replace('/', File.separatorChar);
+ }
+ return path;
+ }
+
public void writeLayoutInfoFiles(File xmlOutDir) throws JAXBException {
if (mWritten) {
return;
}
- JAXBContext context = JAXBContext.newInstance(ResourceBundle.LayoutFileBundle.class);
- Marshaller marshaller = context.createMarshaller();
-
for (List<ResourceBundle.LayoutFileBundle> layouts : mResourceBundle.getLayoutBundles()
.values()) {
for (ResourceBundle.LayoutFileBundle layout : layouts) {
- writeXmlFile(xmlOutDir, layout, marshaller);
+ writeXmlFile(xmlOutDir, layout);
}
}
+ for (File file : mResourceBundle.getRemovedFiles()) {
+ String exportFileName = generateExportFileName(file);
+ FileUtils.deleteQuietly(new File(xmlOutDir, exportFileName));
+ }
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);
+ private void writeXmlFile(File xmlOutDir, ResourceBundle.LayoutFileBundle layout)
+ throws JAXBException {
+ String filename = generateExportFileName(layout);
+ mFileWriter.writeToFile(new File(xmlOutDir, filename), layout.toXML());
}
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.
*/
- public 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 static String generateExportFileName(ResourceBundle.LayoutFileBundle layout) {
+ return generateExportFileName(layout.getFileName(), layout.getDirectory());
+ }
+
+ private static String generateExportFileName(File file) {
+ final String fileName = file.getName();
+ return generateExportFileName(fileName.substring(0, fileName.lastIndexOf('.')),
+ file.getParentFile().getName());
+ }
+
+ public static String generateExportFileName(String fileName, String dirName) {
+ return fileName + '-' + dirName + ".xml";
+ }
+
+ public static String exportLayoutNameFromInfoFileName(String infoFileName) {
+ return infoFileName.substring(0, infoFileName.indexOf('-'));
}
public void writeInfoClass(/*Nullable*/ File sdkDir, File xmlOutDir,
@@ -166,13 +283,18 @@
writeInfoClass(sdkDir, xmlOutDir, exportClassListTo, false, false);
}
+ public String getPackage() {
+ return mResourceBundle.getAppPackage();
+ }
+
public void writeInfoClass(/*Nullable*/ File sdkDir, File xmlOutDir, File exportClassListTo,
boolean enableDebugLogs, boolean printEncodedErrorLogs) {
- final String sdkPath = sdkDir == null ? null : StringEscapeUtils.escapeJava(sdkDir.getAbsolutePath());
+ Escaper javaEscaper = SourceCodeEscapers.javaCharEscaper();
+ final String sdkPath = sdkDir == null ? null : javaEscaper.escape(sdkDir.getAbsolutePath());
final Class annotation = BindingBuildInfo.class;
- final String layoutInfoPath = StringEscapeUtils.escapeJava(xmlOutDir.getAbsolutePath());
+ final String layoutInfoPath = javaEscaper.escape(xmlOutDir.getAbsolutePath());
final String exportClassListToPath = exportClassListTo == null ? "" :
- StringEscapeUtils.escapeJava(exportClassListTo.getAbsolutePath());
+ javaEscaper.escape(exportClassListTo.getAbsolutePath());
String classString = "package " + RESOURCE_BUNDLE_PACKAGE + ";\n\n" +
"import " + annotation.getCanonicalName() + ";\n\n" +
"@" + annotation.getSimpleName() + "(buildId=\"" + mBuildId + "\", " +
@@ -201,4 +323,111 @@
return name.toLowerCase().endsWith(".xml");
}
};
+
+ /**
+ * Helper interface that can find the original copy of a resource XML.
+ */
+ public interface OriginalFileLookup {
+
+ /**
+ * @param file The intermediate build file
+ * @return The original file or null if original File cannot be found.
+ */
+ File getOriginalFileFor(File file);
+ }
+
+ /**
+ * API agnostic class to get resource changes incrementally.
+ */
+ public static class ResourceInput {
+ private final boolean mIncremental;
+ private final File mRootInputFolder;
+ private final File mRootOutputFolder;
+
+ private List<File> mAdded = new ArrayList<File>();
+ private List<File> mRemoved = new ArrayList<File>();
+ private List<File> mChanged = new ArrayList<File>();
+
+ public ResourceInput(boolean incremental, File rootInputFolder, File rootOutputFolder) {
+ mIncremental = incremental;
+ mRootInputFolder = rootInputFolder;
+ mRootOutputFolder = rootOutputFolder;
+ }
+
+ public void added(File file) {
+ mAdded.add(file);
+ }
+ public void removed(File file) {
+ mRemoved.add(file);
+ }
+ public void changed(File file) {
+ mChanged.add(file);
+ }
+
+ public boolean shouldCopy() {
+ return !mRootInputFolder.equals(mRootOutputFolder);
+ }
+
+ List<File> getAdded() {
+ return mAdded;
+ }
+
+ List<File> getRemoved() {
+ return mRemoved;
+ }
+
+ List<File> getChanged() {
+ return mChanged;
+ }
+
+ File getRootInputFolder() {
+ return mRootInputFolder;
+ }
+
+ File getRootOutputFolder() {
+ return mRootOutputFolder;
+ }
+
+ public boolean isIncremental() {
+ return mIncremental;
+ }
+
+ @Override
+ public String toString() {
+ StringBuilder out = new StringBuilder();
+ out.append("ResourceInput{")
+ .append("mIncremental=").append(mIncremental)
+ .append(", mRootInputFolder=").append(mRootInputFolder)
+ .append(", mRootOutputFolder=").append(mRootOutputFolder);
+ logFiles(out, "added", mAdded);
+ logFiles(out, "removed", mRemoved);
+ logFiles(out, "changed", mChanged);
+ return out.toString();
+
+ }
+
+ private static void logFiles(StringBuilder out, String name, List<File> files) {
+ out.append("\n ").append(name);
+ for (File file : files) {
+ out.append("\n - ").append(file.getAbsolutePath());
+ }
+ }
+ }
+
+ private interface ProcessFileCallback {
+ void processLayoutFile(File file)
+ throws ParserConfigurationException, SAXException, XPathExpressionException,
+ IOException;
+ void processOtherFile(File parentFolder, File file) throws IOException;
+ void processRemovedLayoutFile(File file);
+ void processRemovedOtherFile(File parentFolder, File file) throws IOException;
+
+ void processOtherFolder(File folder);
+
+ void processLayoutFolder(File folder);
+
+ void processOtherRootFile(File file) throws IOException;
+
+ void processRemovedOtherRootFile(File file) throws IOException;
+ }
}
diff --git a/compilerCommon/src/main/java/android/databinding/tool/processing/ErrorMessages.java b/compilerCommon/src/main/java/android/databinding/tool/processing/ErrorMessages.java
index a4ed1cb..a2ec898 100644
--- a/compilerCommon/src/main/java/android/databinding/tool/processing/ErrorMessages.java
+++ b/compilerCommon/src/main/java/android/databinding/tool/processing/ErrorMessages.java
@@ -36,4 +36,14 @@
+ " configurations";
public static final String MULTI_CONFIG_ID_USED_AS_IMPORT =
"Cannot use the same id (%s) for a View and an include tag.";
+ public static final String ROOT_TAG_NOT_SUPPORTED = "android:tag is not supported on root " +
+ "elements of data bound layouts unless targeting API version 14 or greater. Value " +
+ "is '%s'";
+ public static final String SYNTAX_ERROR = "Syntax error: %s";
+ public static final String CANNOT_FIND_GETTER_CALL =
+ "Cannot find the getter for attribute '%s' with value type %s on %s.";
+ public static final String EXPRESSION_NOT_INVERTIBLE =
+ "The expression %s cannot cannot be inverted: %s";
+ public static final String TWO_WAY_EVENT_ATTRIBUTE =
+ "The attribute %s is a two-way binding event attribute and cannot be assigned.";
}
diff --git a/compilerCommon/src/main/java/android/databinding/tool/processing/Scope.java b/compilerCommon/src/main/java/android/databinding/tool/processing/Scope.java
index c723c80..9e0a692 100644
--- a/compilerCommon/src/main/java/android/databinding/tool/processing/Scope.java
+++ b/compilerCommon/src/main/java/android/databinding/tool/processing/Scope.java
@@ -34,7 +34,7 @@
public class Scope {
private static ThreadLocal<ScopeEntry> sScopeItems = new ThreadLocal<ScopeEntry>();
- static List<ScopedException> sDeferredExceptions = new ArrayList<>();
+ static List<ScopedException> sDeferredExceptions = new ArrayList<ScopedException>();
public static void enter(final Location location) {
enter(new LocationScopeProvider() {
@@ -165,7 +165,7 @@
return Arrays.asList(locations.get(0).toAbsoluteLocation());
}
// We have more than 1 location. Depending on the scope, we may or may not want all of them
- List<Location> chosen = new ArrayList<>();
+ List<Location> chosen = new ArrayList<Location>();
for (Location location : locations) {
Location absLocation = location.toAbsoluteLocation();
if (validatedContained(entry.mParent, absLocation)) {
diff --git a/compilerCommon/src/main/java/android/databinding/tool/processing/ScopedErrorReport.java b/compilerCommon/src/main/java/android/databinding/tool/processing/ScopedErrorReport.java
index dfd2039..b645b37 100644
--- a/compilerCommon/src/main/java/android/databinding/tool/processing/ScopedErrorReport.java
+++ b/compilerCommon/src/main/java/android/databinding/tool/processing/ScopedErrorReport.java
@@ -16,9 +16,8 @@
package android.databinding.tool.processing;
-import org.apache.commons.lang3.StringUtils;
-
import android.databinding.tool.store.Location;
+import android.databinding.tool.util.StringUtils;
import java.util.List;
diff --git a/compilerCommon/src/main/java/android/databinding/tool/processing/ScopedException.java b/compilerCommon/src/main/java/android/databinding/tool/processing/ScopedException.java
index 238a895..0f98072 100644
--- a/compilerCommon/src/main/java/android/databinding/tool/processing/ScopedException.java
+++ b/compilerCommon/src/main/java/android/databinding/tool/processing/ScopedException.java
@@ -16,10 +16,14 @@
package android.databinding.tool.processing;
-import org.apache.commons.lang3.StringUtils;
+
+import com.google.common.base.Joiner;
+import com.google.common.base.Splitter;
+import com.google.common.base.Strings;
import android.databinding.tool.store.Location;
import android.databinding.tool.util.L;
+import android.databinding.tool.util.StringUtils;
import java.util.ArrayList;
import java.util.List;
@@ -40,7 +44,8 @@
private String mScopeLog;
public ScopedException(String message, Object... args) {
- super(message == null ? "unknown data binding exception" : String.format(message, args));
+ super(message == null ? "unknown data binding exception" :
+ args.length == 0 ? message : String.format(message, args));
mScopedErrorReport = Scope.createReport();
mScopeLog = L.isDebugEnabled() ? Scope.produceScopeLog() : null;
}
@@ -84,7 +89,7 @@
}
}
sb.append(ERROR_LOG_SUFFIX);
- return StringUtils.join(StringUtils.split(sb.toString(), System.lineSeparator()), " ");
+ return Joiner.on(' ').join(Splitter.on(StringUtils.LINE_SEPARATOR).split(sb));
}
public ScopedErrorReport getScopedErrorReport() {
@@ -98,7 +103,7 @@
public static ScopedException createFromOutput(String output) {
String message = "";
String file = "";
- List<Location> locations = new ArrayList<>();
+ List<Location> locations = new ArrayList<Location>();
int msgStart = output.indexOf(MSG_KEY);
if (msgStart < 0) {
message = output;
@@ -133,11 +138,11 @@
}
}
return new ScopedException(message.trim(),
- new ScopedErrorReport(StringUtils.isEmpty(file) ? null : file.trim(), locations));
+ new ScopedErrorReport(Strings.isNullOrEmpty(file) ? null : file.trim(), locations));
}
public static List<ScopedException> extractErrors(String output) {
- List<ScopedException> errors = new ArrayList<>();
+ List<ScopedException> errors = new ArrayList<ScopedException>();
int index = output.indexOf(ERROR_LOG_PREFIX);
final int limit = output.length();
while (index >= 0 && index < limit) {
@@ -157,4 +162,8 @@
public static void encodeOutput(boolean encodeOutput) {
sEncodeOutput = encodeOutput;
}
+
+ public static boolean issEncodeOutput() {
+ return sEncodeOutput;
+ }
}
diff --git a/compilerCommon/src/main/java/android/databinding/tool/processing/scopes/LocationScopeProvider.java b/compilerCommon/src/main/java/android/databinding/tool/processing/scopes/LocationScopeProvider.java
index ee7f8a9..4272cfd 100644
--- a/compilerCommon/src/main/java/android/databinding/tool/processing/scopes/LocationScopeProvider.java
+++ b/compilerCommon/src/main/java/android/databinding/tool/processing/scopes/LocationScopeProvider.java
@@ -25,5 +25,5 @@
* An item that is tight to locations in a source file.
*/
public interface LocationScopeProvider extends ScopeProvider {
- public List<Location> provideScopeLocation();
+ List<Location> provideScopeLocation();
}
diff --git a/compilerCommon/src/main/java/android/databinding/tool/store/LayoutFileParser.java b/compilerCommon/src/main/java/android/databinding/tool/store/LayoutFileParser.java
index 91be28f..4b20131 100644
--- a/compilerCommon/src/main/java/android/databinding/tool/store/LayoutFileParser.java
+++ b/compilerCommon/src/main/java/android/databinding/tool/store/LayoutFileParser.java
@@ -13,35 +13,36 @@
package android.databinding.tool.store;
-import org.antlr.v4.runtime.ANTLRInputStream;
-import org.antlr.v4.runtime.CommonTokenStream;
-import org.antlr.v4.runtime.ParserRuleContext;
-import org.antlr.v4.runtime.misc.NotNull;
-import org.apache.commons.io.FileUtils;
-import org.apache.commons.lang3.StringEscapeUtils;
-import org.apache.commons.lang3.StringUtils;
-import org.w3c.dom.Document;
-import org.w3c.dom.Node;
-import org.w3c.dom.NodeList;
-import org.xml.sax.SAXException;
-
import android.databinding.parser.XMLLexer;
import android.databinding.parser.XMLParser;
import android.databinding.parser.XMLParserBaseVisitor;
+import android.databinding.tool.LayoutXmlProcessor;
import android.databinding.tool.processing.ErrorMessages;
import android.databinding.tool.processing.Scope;
import android.databinding.tool.processing.scopes.FileScopeProvider;
import android.databinding.tool.util.L;
import android.databinding.tool.util.ParserHelper;
import android.databinding.tool.util.Preconditions;
+import android.databinding.tool.util.StringUtils;
import android.databinding.tool.util.XmlEditor;
+import com.google.common.base.Strings;
+
+import org.antlr.v4.runtime.ANTLRInputStream;
+import org.antlr.v4.runtime.CommonTokenStream;
+import org.antlr.v4.runtime.ParserRuleContext;
+import org.antlr.v4.runtime.misc.NotNull;
+import org.apache.commons.io.FileUtils;
+import org.mozilla.universalchardet.UniversalDetector;
+import org.w3c.dom.Document;
+import org.w3c.dom.Node;
+import org.w3c.dom.NodeList;
+import org.xml.sax.SAXException;
+
import java.io.File;
-import java.io.FileReader;
+import java.io.FileInputStream;
import java.io.IOException;
-import java.net.MalformedURLException;
-import java.net.URISyntaxException;
-import java.net.URL;
+import java.io.InputStreamReader;
import java.util.ArrayList;
import java.util.HashMap;
import java.util.List;
@@ -67,31 +68,29 @@
private static final String LAYOUT_PREFIX = "@layout/";
- public ResourceBundle.LayoutFileBundle parseXml(final File xml, String pkg, int minSdk)
+ public ResourceBundle.LayoutFileBundle parseXml(final File inputFile, final File outputFile,
+ String pkg, final LayoutXmlProcessor.OriginalFileLookup originalFileLookup)
throws ParserConfigurationException, IOException, SAXException,
XPathExpressionException {
+ File originalFileFor = originalFileLookup.getOriginalFileFor(inputFile);
+ final String originalFilePath = originalFileFor.getAbsolutePath();
try {
Scope.enter(new FileScopeProvider() {
@Override
public String provideScopeFilePath() {
- return xml.getAbsolutePath();
+ return originalFilePath;
}
});
- final String xmlNoExtension = ParserHelper.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;
- }
- return parseXml(original, pkg);
+ final String encoding = findEncoding(inputFile);
+ stripFile(inputFile, outputFile, encoding, originalFileLookup);
+ return parseOriginalXml(originalFileFor, pkg, encoding);
} finally {
Scope.exit();
}
}
- private ResourceBundle.LayoutFileBundle parseXml(final File original, String pkg)
- throws IOException {
+ private ResourceBundle.LayoutFileBundle parseOriginalXml(final File original, String pkg,
+ String encoding) throws IOException {
try {
Scope.enter(new FileScopeProvider() {
@Override
@@ -100,7 +99,9 @@
}
});
final String xmlNoExtension = ParserHelper.stripExtension(original.getName());
- ANTLRInputStream inputStream = new ANTLRInputStream(new FileReader(original));
+ FileInputStream fin = new FileInputStream(original);
+ InputStreamReader reader = new InputStreamReader(fin, encoding);
+ ANTLRInputStream inputStream = new ANTLRInputStream(reader);
XMLLexer lexer = new XMLLexer(inputStream);
CommonTokenStream tokenStream = new CommonTokenStream(lexer);
XMLParser parser = new XMLParser(tokenStream);
@@ -129,10 +130,22 @@
}
}
+ private static boolean isProcessedElement(String name) {
+ if (Strings.isNullOrEmpty(name)) {
+ return false;
+ }
+ if ("view".equals(name) || "include".equals(name) || name.indexOf('.') >= 0) {
+ return true;
+ }
+ return !name.toLowerCase().equals(name);
+ }
+
private void parseExpressions(String newTag, final XMLParser.ElementContext rootView,
final boolean isMerge, ResourceBundle.LayoutFileBundle bundle) {
- final List<XMLParser.ElementContext> bindingElements = new ArrayList<>();
- final List<XMLParser.ElementContext> otherElementsWithIds = new ArrayList<>();
+ final List<XMLParser.ElementContext> bindingElements
+ = new ArrayList<XMLParser.ElementContext>();
+ final List<XMLParser.ElementContext> otherElementsWithIds
+ = new ArrayList<XMLParser.ElementContext>();
rootView.accept(new XMLParserBaseVisitor<Void>() {
@Override
public Void visitElement(@NotNull XMLParser.ElementContext ctx) {
@@ -140,7 +153,7 @@
bindingElements.add(ctx);
} else {
String name = ctx.elmName.getText();
- if (!"include".equals(name) && !"fragment".equals(name) &&
+ if (isProcessedElement(name) &&
attributeMap(ctx).containsKey("android:id")) {
otherElementsWithIds.add(ctx);
}
@@ -158,13 +171,8 @@
} else if (ctx == rootView) {
return true;
}
- if (hasIncludeChild(ctx)) {
- return true;
- }
- if (XmlEditor.hasExpressionAttributes(ctx)) {
- return true;
- }
- return false;
+ return hasIncludeChild(ctx) || XmlEditor.hasExpressionAttributes(ctx) ||
+ "include".equals(ctx.elmName.getText());
}
private boolean hasIncludeChild(XMLParser.ElementContext ctx) {
@@ -192,7 +200,7 @@
if ("include".equals(nodeName)) {
// get the layout attribute
final String includeValue = attributes.get("layout");
- if (StringUtils.isEmpty(includeValue)) {
+ if (Strings.isNullOrEmpty(includeValue)) {
L.e("%s must include a layout", parent);
}
if (!includeValue.startsWith(LAYOUT_PREFIX)) {
@@ -201,8 +209,7 @@
}
// if user is binding something there, there MUST be a layout file to be
// generated.
- String layoutName = includeValue.substring(LAYOUT_PREFIX.length());
- includedLayoutName = layoutName;
+ includedLayoutName = includeValue.substring(LAYOUT_PREFIX.length());
final ParserRuleContext myParentContent = parent.getParent();
Preconditions.check(myParentContent instanceof XMLParser.ContentContext,
"parent of an include tag must be a content context but it is %s",
@@ -214,7 +221,9 @@
//noinspection SuspiciousMethodCalls
tag = nodeTagMap.get(grandParent);
} else if ("fragment".equals(nodeName)) {
- L.e("fragments do not support data binding expressions.");
+ if (XmlEditor.hasExpressionAttributes(parent)) {
+ L.e("fragments do not support data binding expressions.");
+ }
continue;
} else {
viewName = getViewName(parent);
@@ -234,9 +243,15 @@
for (XMLParser.AttributeContext attr : XmlEditor.expressionAttributes(parent)) {
String value = escapeQuotes(attr.attrValue.getText(), true);
- if (value.charAt(0) == '@' && value.charAt(1) == '{' &&
- value.charAt(value.length() - 1) == '}') {
- final String strippedValue = value.substring(2, value.length() - 1);
+ final boolean isOneWay = value.startsWith("@{");
+ final boolean isTwoWay = value.startsWith("@={");
+ if (isOneWay || isTwoWay) {
+ if (value.charAt(value.length() - 1) != '}') {
+ L.e("Expecting '}' in expression '%s'", attr.attrValue.getText());
+ }
+ final int startIndex = isTwoWay ? 3 : 2;
+ final int endIndex = value.length() - 1;
+ final String strippedValue = value.substring(startIndex, endIndex);
Location attrLocation = new Location(attr);
Location valueLocation = new Location();
// offset to 0 based
@@ -245,8 +260,8 @@
attr.attrValue.getText().indexOf(strippedValue);
valueLocation.endLine = attrLocation.endLine;
valueLocation.endOffset = attrLocation.endOffset - 2; // account for: "}
- bindingTargetBundle.addBinding(escapeQuotes(attr.attrName.getText(), false)
- , strippedValue, attrLocation, valueLocation);
+ bindingTargetBundle.addBinding(escapeQuotes(attr.attrName.getText(), false),
+ strippedValue, isTwoWay, attrLocation, valueLocation);
}
}
}
@@ -262,10 +277,12 @@
String viewName = elm.elmName.getText();
if ("view".equals(viewName)) {
String classNode = attributeMap(elm).get("class");
- if (StringUtils.isEmpty(classNode)) {
+ if (Strings.isNullOrEmpty(classNode)) {
L.e("No class attribute for 'view' node");
}
viewName = classNode;
+ } else if ("include".equals(viewName) && !XmlEditor.hasExpressionAttributes(elm)) {
+ viewName = "android.view.View";
}
return viewName;
}
@@ -281,9 +298,8 @@
String alias = attrMap.get("alias");
Preconditions.check(StringUtils.isNotBlank(type), "Type of an import cannot be empty."
+ " %s in %s", imp.toStringTree(), xml);
- if (StringUtils.isEmpty(alias)) {
- final String[] split = StringUtils.split(type, '.');
- alias = split[split.length - 1];
+ if (Strings.isNullOrEmpty(alias)) {
+ alias = type.substring(type.lastIndexOf('.') + 1);
}
bundle.addImport(alias, type, new Location(imp));
}
@@ -296,7 +312,7 @@
variable.toStringTree(), xml);
Preconditions.checkNotNull(name, "variable must have a name %s in %s",
variable.toStringTree(), xml);
- bundle.addVariable(name, type, new Location(variable));
+ bundle.addVariable(name, type, new Location(variable), true);
}
final XMLParser.AttributeContext className = findAttribute(data, "class");
if (className != null) {
@@ -331,7 +347,7 @@
private List<XMLParser.ElementContext> filter(XMLParser.ElementContext root,
String name) {
- List<XMLParser.ElementContext> result = new ArrayList<>();
+ List<XMLParser.ElementContext> result = new ArrayList<XMLParser.ElementContext>();
if (root == null) {
return result;
}
@@ -349,7 +365,7 @@
private List<XMLParser.ElementContext> filterNot(XMLParser.ElementContext root,
String name) {
- List<XMLParser.ElementContext> result = new ArrayList<>();
+ List<XMLParser.ElementContext> result = new ArrayList<XMLParser.ElementContext>();
if (root == null) {
return result;
}
@@ -369,40 +385,31 @@
return "merge".equals(rootView.elmName.getText()) && filter(rootView, "include").size() > 0;
}
- private File stripFileAndGetOriginal(File xml, String binderId)
+ private void stripFile(File xml, File out, String encoding,
+ LayoutXmlProcessor.OriginalFileLookup originalFileLookup)
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;
+ File actualFile = originalFileLookup == null ? null
+ : originalFileLookup.getOriginalFileFor(xml);
+ // TODO get rid of original file lookup
+ if (actualFile == null) {
+ actualFile = xml;
}
- final Node first = commentElementNodes.item(0);
- String actualFilePath = first.getNodeValue().substring(" From:".length()).trim();
- L.d("actual file to parse: %s", actualFilePath);
- File actualFile = urlToFile(new java.net.URL(actualFilePath));
- if (!actualFile.canRead()) {
- L.d("cannot find original, skipping. %s", actualFile.getAbsolutePath());
- return null;
- }
-
+ // always create id from actual file when available. Gradle may duplicate files.
+ String noExt = ParserHelper.stripExtension(actualFile.getName());
+ String binderId = actualFile.getParentFile().getName() + '/' + noExt;
// now if file has any binding expressions, find and delete them
- // TODO we should rely on namespace to avoid parsing file twice
boolean changed = isBindingLayout(doc, xPath);
if (changed) {
- stripBindingTags(xml, binderId);
+ stripBindingTags(xml, out, binderId, encoding);
+ } else if (!xml.equals(out)){
+ FileUtils.copyFile(xml, out);
}
- return actualFile;
}
private boolean isBindingLayout(Document doc, XPath xPath) throws XPathExpressionException {
@@ -423,28 +430,40 @@
return result;
}
- private void stripBindingTags(File xml, String newTag) throws IOException {
- String res = XmlEditor.strip(xml, newTag);
+ private void stripBindingTags(File xml, File output, String newTag, String encoding) throws IOException {
+ String res = XmlEditor.strip(xml, newTag, encoding);
+ Preconditions.checkNotNull(res, "layout file should've changed %s", xml.getAbsolutePath());
if (res != null) {
L.d("file %s has changed, overwriting %s", xml.getName(), xml.getAbsolutePath());
- FileUtils.writeStringToFile(xml, res);
+ FileUtils.writeStringToFile(output, res, encoding);
}
}
- public static File urlToFile(URL url) throws MalformedURLException {
+ private static String findEncoding(File f) throws IOException {
+ FileInputStream fin = new FileInputStream(f);
try {
- return new File(url.toURI());
- } catch (IllegalArgumentException e) {
- MalformedURLException ex = new MalformedURLException(e.getLocalizedMessage());
- ex.initCause(e);
- throw ex;
- } catch (URISyntaxException e) {
- return new File(url.getPath());
+ UniversalDetector universalDetector = new UniversalDetector(null);
+
+ byte[] buf = new byte[4096];
+ int nread;
+ while ((nread = fin.read(buf)) > 0 && !universalDetector.isDone()) {
+ universalDetector.handleData(buf, 0, nread);
+ }
+
+ universalDetector.dataEnd();
+
+ String encoding = universalDetector.getDetectedCharset();
+ if (encoding == null) {
+ encoding = "utf-8";
+ }
+ return encoding;
+ } finally {
+ fin.close();
}
}
private static Map<String, String> attributeMap(XMLParser.ElementContext root) {
- final Map<String, String> result = new HashMap<>();
+ final Map<String, String> result = new HashMap<String, String>();
for (XMLParser.AttributeContext attr : XmlEditor.attributes(root)) {
result.put(escapeQuotes(attr.attrName.getText(), false),
escapeQuotes(attr.attrValue.getText(), true));
@@ -474,7 +493,7 @@
}
String val = textWithQuotes.substring(start, end);
if (unescapeValue) {
- return StringEscapeUtils.unescapeXml(val);
+ return StringUtils.unescapeXml(val);
} else {
return val;
}
diff --git a/compilerCommon/src/main/java/android/databinding/tool/store/Location.java b/compilerCommon/src/main/java/android/databinding/tool/store/Location.java
index d88f0de..67ed6a1 100644
--- a/compilerCommon/src/main/java/android/databinding/tool/store/Location.java
+++ b/compilerCommon/src/main/java/android/databinding/tool/store/Location.java
@@ -15,13 +15,14 @@
*/
package android.databinding.tool.store;
+
import org.antlr.v4.runtime.ParserRuleContext;
import org.antlr.v4.runtime.Token;
-import org.apache.commons.lang3.StringUtils;
import android.databinding.tool.processing.scopes.LocationScopeProvider;
+import android.databinding.tool.util.StringUtils;
-import java.util.Arrays;
+import java.util.Collections;
import java.util.List;
import javax.xml.bind.annotation.XmlAccessType;
@@ -74,7 +75,7 @@
} else {
endLine = end.getLine() - 1; // token lines start from 1
String endText = end.getText();
- int lastLineStart = endText.lastIndexOf(System.lineSeparator());
+ int lastLineStart = endText.lastIndexOf(StringUtils.LINE_SEPARATOR);
String lastLine = lastLineStart < 0 ? endText : endText.substring(lastLineStart + 1);
endOffset = end.getCharPositionInLine() + lastLine.length() - 1;//end is inclusive
}
@@ -130,12 +131,9 @@
if (startOffset != location.startOffset) {
return false;
}
- if (parentLocation != null ? !parentLocation.equals(location.parentLocation)
- : location.parentLocation != null) {
- return false;
- }
+ return !(parentLocation != null ? !parentLocation.equals(location.parentLocation)
+ : location.parentLocation != null);
- return true;
}
@Override
@@ -161,10 +159,7 @@
if (endLine < other.endLine) {
return false;
}
- if (endLine == other.endLine && endOffset < other.endOffset) {
- return false;
- }
- return true;
+ return !(endLine == other.endLine && endOffset < other.endOffset);
}
private Location getValidParentAbsoluteLocation() {
@@ -233,7 +228,7 @@
return new LocationScopeProvider() {
@Override
public List<Location> provideScopeLocation() {
- return Arrays.asList(Location.this);
+ return Collections.singletonList(Location.this);
}
};
}
diff --git a/compilerCommon/src/main/java/android/databinding/tool/store/ResourceBundle.java b/compilerCommon/src/main/java/android/databinding/tool/store/ResourceBundle.java
index 74a0676..f9d0fe9 100644
--- a/compilerCommon/src/main/java/android/databinding/tool/store/ResourceBundle.java
+++ b/compilerCommon/src/main/java/android/databinding/tool/store/ResourceBundle.java
@@ -13,8 +13,6 @@
package android.databinding.tool.store;
-import org.apache.commons.lang3.ArrayUtils;
-
import android.databinding.tool.processing.ErrorMessages;
import android.databinding.tool.processing.Scope;
import android.databinding.tool.processing.ScopedException;
@@ -25,7 +23,9 @@
import android.databinding.tool.util.Preconditions;
import java.io.File;
+import java.io.InputStream;
import java.io.Serializable;
+import java.io.StringWriter;
import java.util.ArrayList;
import java.util.Arrays;
import java.util.HashMap;
@@ -34,6 +34,10 @@
import java.util.Map;
import java.util.Set;
+import javax.xml.bind.JAXBContext;
+import javax.xml.bind.JAXBException;
+import javax.xml.bind.Marshaller;
+import javax.xml.bind.Unmarshaller;
import javax.xml.bind.annotation.XmlAccessType;
import javax.xml.bind.annotation.XmlAccessorType;
import javax.xml.bind.annotation.XmlAttribute;
@@ -52,6 +56,8 @@
private HashMap<String, List<LayoutFileBundle>> mLayoutBundles
= new HashMap<String, List<LayoutFileBundle>>();
+ private List<File> mRemovedFiles = new ArrayList<File>();
+
public ResourceBundle(String appPackage) {
mAppPackage = appPackage;
}
@@ -86,18 +92,28 @@
public void validateMultiResLayouts() {
for (List<LayoutFileBundle> layoutFileBundles : mLayoutBundles.values()) {
for (LayoutFileBundle layoutFileBundle : layoutFileBundles) {
+ List<BindingTargetBundle> unboundIncludes = new ArrayList<BindingTargetBundle>();
for (BindingTargetBundle target : layoutFileBundle.getBindingTargetBundles()) {
if (target.isBinder()) {
List<LayoutFileBundle> boundTo =
mLayoutBundles.get(target.getIncludedLayout());
if (boundTo == null || boundTo.isEmpty()) {
- L.e("There is no binding for %s", target.getIncludedLayout());
+ L.d("There is no binding for %s, reverting to plain layout",
+ target.getIncludedLayout());
+ if (target.getId() == null) {
+ unboundIncludes.add(target);
+ } else {
+ target.setIncludedLayout(null);
+ target.setInterfaceType("android.view.View");
+ target.mViewName = "android.view.View";
+ }
} else {
String binding = boundTo.get(0).getFullBindingClass();
target.setInterfaceType(binding);
}
}
}
+ layoutFileBundle.getBindingTargetBundles().removeAll(unboundIncludes);
}
}
@@ -116,7 +132,7 @@
bundles.getValue(), ErrorMessages.MULTI_CONFIG_VARIABLE_TYPE_MISMATCH,
new ValidateAndFilterCallback() {
@Override
- public List<NameTypeLocation> get(LayoutFileBundle bundle) {
+ public List<? extends NameTypeLocation> get(LayoutFileBundle bundle) {
return bundle.mVariables;
}
});
@@ -136,7 +152,8 @@
bundle.mConfigName);
for (Map.Entry<String, NameTypeLocation> variable : variableTypes.entrySet()) {
if (!NameTypeLocation.contains(bundle.mVariables, variable.getKey())) {
- bundle.mVariables.add(variable.getValue());
+ NameTypeLocation orig = variable.getValue();
+ bundle.addVariable(orig.name, orig.type, orig.location, false);
L.d("adding missing variable %s to %s / %s", variable.getKey(),
bundle.mFileName, bundle.mConfigName);
}
@@ -155,7 +172,7 @@
Map<String, String> viewTypes = new HashMap<String, String>();
Map<String, String> includes = new HashMap<String, String>();
L.d("validating ids for %s", bundles.getKey());
- Set<String> conflictingIds = new HashSet<>();
+ Set<String> conflictingIds = new HashSet<String>();
for (LayoutFileBundle bundle : bundles.getValue()) {
try {
Scope.enter(bundle);
@@ -284,8 +301,8 @@
private Map<String, NameTypeLocation> validateAndMergeNameTypeLocations(
List<LayoutFileBundle> bundles, String errorMessage,
ValidateAndFilterCallback callback) {
- Map<String, NameTypeLocation> result = new HashMap<>();
- Set<String> mismatched = new HashSet<>();
+ Map<String, NameTypeLocation> result = new HashMap<String, NameTypeLocation>();
+ Set<String> mismatched = new HashSet<String>();
for (LayoutFileBundle bundle : bundles) {
for (NameTypeLocation item : callback.get(bundle)) {
NameTypeLocation existing = result.get(item.name);
@@ -354,6 +371,14 @@
return sharedClassName;
}
+ public void addRemovedFile(File file) {
+ mRemovedFiles.add(file);
+ }
+
+ public List<File> getRemovedFiles() {
+ return mRemovedFiles;
+ }
+
@XmlAccessorType(XmlAccessType.NONE)
@XmlRootElement(name="Layout")
public static class LayoutFileBundle implements Serializable, FileScopeProvider {
@@ -386,10 +411,10 @@
public boolean mHasVariations;
@XmlElement(name="Variables")
- public List<NameTypeLocation> mVariables = new ArrayList<>();
+ public List<VariableDeclaration> mVariables = new ArrayList<VariableDeclaration>();
@XmlElement(name="Imports")
- public List<NameTypeLocation> mImports = new ArrayList<>();
+ public List<NameTypeLocation> mImports = new ArrayList<NameTypeLocation>();
@XmlElementWrapper(name="Targets")
@XmlElement(name="Target")
@@ -404,6 +429,21 @@
public LayoutFileBundle() {
}
+ /**
+ * Updates configuration fields from the given bundle but does not change variables,
+ * binding expressions etc.
+ */
+ public void inheritConfigurationFrom(LayoutFileBundle other) {
+ mFileName = other.mFileName;
+ mModulePackage = other.mModulePackage;
+ mBindingClass = other.mBindingClass;
+ mFullBindingClass = other.mFullBindingClass;
+ mBindingClassName = other.mBindingClassName;
+ mBindingPackage = other.mBindingPackage;
+ mHasVariations = other.mHasVariations;
+ mIsMerge = other.mIsMerge;
+ }
+
public LayoutFileBundle(File file, String fileName, String directory,
String modulePackage, boolean isMerge) {
mFileName = fileName;
@@ -421,10 +461,10 @@
return mClassNameLocationProvider;
}
- public void addVariable(String name, String type, Location location) {
+ public void addVariable(String name, String type, Location location, boolean declared) {
Preconditions.check(!NameTypeLocation.contains(mVariables, name),
"Cannot use same variable name twice. %s in %s", name, location);
- mVariables.add(new NameTypeLocation(name, type, location));
+ mVariables.add(new VariableDeclaration(name, type, location, declared));
}
public void addImport(String alias, String type, Location location) {
@@ -470,7 +510,7 @@
return mHasVariations;
}
- public List<NameTypeLocation> getVariables() {
+ public List<VariableDeclaration> getVariables() {
return mVariables;
}
@@ -544,12 +584,9 @@
: bundle.mDirectory != null) {
return false;
}
- if (mFileName != null ? !mFileName.equals(bundle.mFileName)
- : bundle.mFileName != null) {
- return false;
- }
+ return !(mFileName != null ? !mFileName.equals(bundle.mFileName)
+ : bundle.mFileName != null);
- return true;
}
@Override
@@ -583,6 +620,37 @@
public String provideScopeFilePath() {
return mAbsoluteFilePath;
}
+
+ private static Marshaller sMarshaller;
+ private static Unmarshaller sUmarshaller;
+
+ public String toXML() throws JAXBException {
+ StringWriter writer = new StringWriter();
+ getMarshaller().marshal(this, writer);
+ return writer.getBuffer().toString();
+ }
+
+ public static LayoutFileBundle fromXML(InputStream inputStream) throws JAXBException {
+ return (LayoutFileBundle) getUnmarshaller().unmarshal(inputStream);
+ }
+
+ private static Marshaller getMarshaller() throws JAXBException {
+ if (sMarshaller == null) {
+ JAXBContext context = JAXBContext
+ .newInstance(ResourceBundle.LayoutFileBundle.class);
+ sMarshaller = context.createMarshaller();
+ }
+ return sMarshaller;
+ }
+
+ private static Unmarshaller getUnmarshaller() throws JAXBException {
+ if (sUmarshaller == null) {
+ JAXBContext context = JAXBContext
+ .newInstance(ResourceBundle.LayoutFileBundle.class);
+ sUmarshaller = context.createUnmarshaller();
+ }
+ return sUmarshaller;
+ }
}
@XmlAccessorType(XmlAccessType.NONE)
@@ -631,11 +699,8 @@
if (!name.equals(that.name)) {
return false;
}
- if (!type.equals(that.type)) {
- return false;
- }
+ return type.equals(that.type);
- return true;
}
@Override
@@ -646,7 +711,7 @@
return result;
}
- public static boolean contains(List<NameTypeLocation> list, String name) {
+ public static boolean contains(List<? extends NameTypeLocation> list, String name) {
for (NameTypeLocation ntl : list) {
if (name.equals(ntl.name)) {
return true;
@@ -656,6 +721,21 @@
}
}
+ @XmlAccessorType(XmlAccessType.NONE)
+ public static class VariableDeclaration extends NameTypeLocation {
+ @XmlAttribute(name="declared", required = false)
+ public boolean declared;
+
+ public VariableDeclaration() {
+
+ }
+
+ public VariableDeclaration(String name, String type, Location location, boolean declared) {
+ super(name, type, location);
+ this.declared = declared;
+ }
+ }
+
public static class MarshalledMapType {
public List<NameTypeLocation> entries;
}
@@ -696,8 +776,9 @@
mLocation = location;
}
- public void addBinding(String name, String expr, Location location, Location valueLocation) {
- mBindingBundleList.add(new BindingBundle(name, expr, location, valueLocation));
+ public void addBinding(String name, String expr, boolean isTwoWay, Location location,
+ Location valueLocation) {
+ mBindingBundleList.add(new BindingBundle(name, expr, isTwoWay, location, valueLocation));
}
public void setIncludedLayout(String includedLayout) {
@@ -741,7 +822,7 @@
if (isBinder()) {
mFullClassName = mInterfaceType;
} else if (mViewName.indexOf('.') == -1) {
- if (ArrayUtils.contains(ANDROID_VIEW_PACKAGE_VIEWS, mViewName)) {
+ if (Arrays.asList(ANDROID_VIEW_PACKAGE_VIEWS).contains(mViewName)) {
mFullClassName = "android.view." + mViewName;
} else if("WebView".equals(mViewName)) {
mFullClassName = "android.webkit." + mViewName;
@@ -783,14 +864,16 @@
private String mExpr;
private Location mLocation;
private Location mValueLocation;
+ private boolean mIsTwoWay;
public BindingBundle() {}
- public BindingBundle(String name, String expr, Location location,
+ public BindingBundle(String name, String expr, boolean isTwoWay, Location location,
Location valueLocation) {
mName = name;
mExpr = expr;
mLocation = location;
+ mIsTwoWay = isTwoWay;
mValueLocation = valueLocation;
}
@@ -812,6 +895,10 @@
mExpr = expr;
}
+ public void setTwoWay(boolean isTwoWay) {
+ mIsTwoWay = isTwoWay;
+ }
+
@XmlElement(name="Location")
public Location getLocation() {
return mLocation;
@@ -826,6 +913,11 @@
return mValueLocation;
}
+ @XmlElement(name="TwoWay")
+ public boolean isTwoWay() {
+ return mIsTwoWay;
+ }
+
public void setValueLocation(Location valueLocation) {
mValueLocation = valueLocation;
}
@@ -836,6 +928,6 @@
* Just an inner callback class to process imports and variables w/ the same code.
*/
private interface ValidateAndFilterCallback {
- List<NameTypeLocation> get(LayoutFileBundle bundle);
+ List<? extends NameTypeLocation> get(LayoutFileBundle bundle);
}
}
diff --git a/compilerCommon/src/main/java/android/databinding/tool/util/L.java b/compilerCommon/src/main/java/android/databinding/tool/util/L.java
index fe888ca..37292b2 100644
--- a/compilerCommon/src/main/java/android/databinding/tool/util/L.java
+++ b/compilerCommon/src/main/java/android/databinding/tool/util/L.java
@@ -16,10 +16,12 @@
package android.databinding.tool.util;
-import org.apache.commons.lang3.exception.ExceptionUtils;
-
import android.databinding.tool.processing.ScopedException;
+import java.io.PrintWriter;
+import java.io.StringWriter;
+
+import javax.lang.model.element.Element;
import javax.tools.Diagnostic;
import javax.tools.Diagnostic.Kind;
@@ -27,7 +29,7 @@
private static boolean sEnableDebug = false;
private static final Client sSystemClient = new Client() {
@Override
- public void printMessage(Kind kind, String message) {
+ public void printMessage(Kind kind, String message, Element element) {
if (kind == Kind.ERROR) {
System.err.println(message);
} else {
@@ -39,7 +41,7 @@
private static Client sClient = sSystemClient;
public static void setClient(Client systemClient) {
- L.sClient = systemClient;
+ sClient = systemClient;
}
public static void setDebugLog(boolean enabled) {
@@ -48,24 +50,34 @@
public static void d(String msg, Object... args) {
if (sEnableDebug) {
- printMessage(Diagnostic.Kind.NOTE, String.format(msg, args));
+ printMessage(null, Diagnostic.Kind.NOTE, String.format(msg, args));
+ }
+ }
+
+ public static void d(Element element, String msg, Object... args) {
+ if (sEnableDebug) {
+ printMessage(element, Diagnostic.Kind.NOTE, String.format(msg, args));
}
}
public static void d(Throwable t, String msg, Object... args) {
if (sEnableDebug) {
- printMessage(Diagnostic.Kind.NOTE,
- String.format(msg, args) + " " + ExceptionUtils.getStackTrace(t));
+ printMessage(null, Diagnostic.Kind.NOTE,
+ String.format(msg, args) + " " + getStackTrace(t));
}
}
public static void w(String msg, Object... args) {
- printMessage(Kind.WARNING, String.format(msg, args));
+ printMessage(null, Kind.WARNING, String.format(msg, args));
+ }
+
+ public static void w(Element element, String msg, Object... args) {
+ printMessage(element, Kind.WARNING, String.format(msg, args));
}
public static void w(Throwable t, String msg, Object... args) {
- printMessage(Kind.WARNING,
- String.format(msg, args) + " " + ExceptionUtils.getStackTrace(t));
+ printMessage(null, Kind.WARNING,
+ String.format(msg, args) + " " + getStackTrace(t));
}
private static void tryToThrowScoped(Throwable t, String fullMessage) {
@@ -84,18 +96,24 @@
public static void e(String msg, Object... args) {
String fullMsg = String.format(msg, args);
tryToThrowScoped(null, fullMsg);
- printMessage(Diagnostic.Kind.ERROR, fullMsg);
+ printMessage(null, Diagnostic.Kind.ERROR, fullMsg);
+ }
+
+ public static void e(Element element, String msg, Object... args) {
+ String fullMsg = String.format(msg, args);
+ tryToThrowScoped(null, fullMsg);
+ printMessage(element, Diagnostic.Kind.ERROR, fullMsg);
}
public static void e(Throwable t, String msg, Object... args) {
String fullMsg = String.format(msg, args);
tryToThrowScoped(t, fullMsg);
- printMessage(Diagnostic.Kind.ERROR,
- fullMsg + " " + ExceptionUtils.getStackTrace(t));
+ printMessage(null, Diagnostic.Kind.ERROR,
+ fullMsg + " " + getStackTrace(t));
}
- private static void printMessage(Diagnostic.Kind kind, String message) {
- sClient.printMessage(kind, message);
+ private static void printMessage(Element element, Diagnostic.Kind kind, String message) {
+ sClient.printMessage(kind, message, element);
if (kind == Diagnostic.Kind.ERROR) {
throw new RuntimeException("failure, see logs for details.\n" + message);
}
@@ -105,7 +123,18 @@
return sEnableDebug;
}
- public static interface Client {
- public void printMessage(Diagnostic.Kind kind, String message);
+ public interface Client {
+ void printMessage(Diagnostic.Kind kind, String message, Element element);
+ }
+
+ private static String getStackTrace(Throwable t) {
+ StringWriter sw = new StringWriter();
+ PrintWriter pw = new PrintWriter(sw);
+ try {
+ t.printStackTrace(pw);
+ } finally {
+ pw.close();
+ }
+ return sw.toString();
}
}
diff --git a/compilerCommon/src/main/java/android/databinding/tool/util/ParserHelper.java b/compilerCommon/src/main/java/android/databinding/tool/util/ParserHelper.java
index c3740e5..8b3a93b 100644
--- a/compilerCommon/src/main/java/android/databinding/tool/util/ParserHelper.java
+++ b/compilerCommon/src/main/java/android/databinding/tool/util/ParserHelper.java
@@ -16,8 +16,6 @@
package android.databinding.tool.util;
-import org.apache.commons.lang3.StringUtils;
-
public class ParserHelper {
public static String toClassName(String name) {
StringBuilder builder = new StringBuilder();
diff --git a/compilerCommon/src/main/java/android/databinding/tool/util/SourceCodeEscapers.java b/compilerCommon/src/main/java/android/databinding/tool/util/SourceCodeEscapers.java
new file mode 100644
index 0000000..d979b67
--- /dev/null
+++ b/compilerCommon/src/main/java/android/databinding/tool/util/SourceCodeEscapers.java
@@ -0,0 +1,207 @@
+/*
+ * Copyright (C) 2009 The Guava Authors
+ *
+ * Licensed under the Apache License, Version 2.0 (the "License");
+ * you may not use this file except in compliance with the License.
+ * You may obtain a copy of the License at
+ *
+ * http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing, software
+ * distributed under the License is distributed on an "AS IS" BASIS,
+ * WITHOUT 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 com.google.common.escape.ArrayBasedCharEscaper;
+import com.google.common.escape.CharEscaper;
+import com.google.common.escape.Escaper;
+
+import java.util.HashMap;
+import java.util.Map;
+
+/**
+ * This file has been copied from the google internal implementation of guava. Some unused parts of
+ * the file have been removed.
+ */
+
+/**
+ * A factory for Escaper instances used to escape strings for safe use in
+ * various common programming languages.
+ *
+ * @author Alex Matevossian
+ * @author David Beaumont
+ */
+public final class SourceCodeEscapers {
+ private SourceCodeEscapers() {}
+
+ // For each xxxEscaper() method, please add links to external reference pages
+ // that are considered authoritative for the behavior of that escaper.
+
+ // From: http://en.wikipedia.org/wiki/ASCII#ASCII_printable_characters
+ private static final char PRINTABLE_ASCII_MIN = 0x20; // ' '
+ private static final char PRINTABLE_ASCII_MAX = 0x7E; // '~'
+
+ private static final char[] HEX_DIGITS = "0123456789abcdef".toCharArray();
+
+ /**
+ * Returns an {@link Escaper} instance that escapes special characters in a
+ * string so it can safely be included in either a Java character literal or
+ * string literal. This is the preferred way to escape Java characters for
+ * use in String or character literals.
+ *
+ * <p>For more details, see <a href="http://goo.gl/NsGW7">Escape Sequences for
+ * Character and String Literals</a> in The Java Language Specification.
+ */
+ public static Escaper javaCharEscaper() {
+ return JAVA_CHAR_ESCAPER;
+ }
+
+ /**
+ * Returns an {@link Escaper} instance that escapes special characters in a
+ * string so it can safely be included in either a Java character literal or
+ * string literal. The behavior of this escaper is the same as that of the
+ * {@link #javaStringEscaperWithOctal()} except it also escapes single quotes.
+ *
+ * <p>Unlike {@link #javaCharEscaper} this escaper produces octal escape
+ * sequences ({@literal \}nnn) for characters with values less than 256. While
+ * the escaped output can be shorter than when the standard Unicode escape
+ * sequence ({@literal \}uxxxx) is used, the Java Language Specification
+ * discourages the use of octal for escaping Java strings. It is strongly
+ * recommended that, if possible, you use {@code javaCharEscaper()} in
+ * preference to this method.
+ *
+ * <p>For more details, see <a href="http://goo.gl/NsGW7">Escape Sequences for
+ * Character and String Literals</a> in The Java Language Specification.
+ */
+ public static Escaper javaCharEscaperWithOctal() {
+ return JAVA_CHAR_ESCAPER_WITH_OCTAL;
+ }
+
+ /**
+ * Returns an {@link Escaper} instance that escapes special characters in a
+ * string so it can safely be included in a Java string literal.
+ *
+ * <p><b>Note:</b> Single quotes are not escaped, so it is <b>not safe</b> to
+ * use this escaper for escaping character literals.
+ *
+ * <p>Unlike {@link #javaCharEscaper} this escaper produces octal escape
+ * sequences ({@literal \}nnn) for characters with values less than 256. While
+ * the escaped output can be shorter than when the standard Unicode escape
+ * sequence ({@literal \}uxxxx) is used, the Java Language Specification
+ * discourages the use of octal for escaping Java strings. It is strongly
+ * recommended that, if possible, you use {@code javaCharEscaper()} in
+ * preference to this method.
+ *
+ * <p>For more details, see <a href="http://goo.gl/NsGW7">Escape Sequences for
+ * Character and String Literals</a> in The Java Language Specification.
+ */
+ public static Escaper javaStringEscaperWithOctal() {
+ return JAVA_STRING_ESCAPER_WITH_OCTAL;
+ }
+
+ private static final Escaper JAVA_CHAR_ESCAPER;
+ private static final Escaper JAVA_CHAR_ESCAPER_WITH_OCTAL;
+ private static final Escaper JAVA_STRING_ESCAPER_WITH_OCTAL;
+ static {
+ Map<Character, String> javaMap = new HashMap<Character, String>();
+ javaMap.put('\b', "\\b");
+ javaMap.put('\f', "\\f");
+ javaMap.put('\n', "\\n");
+ javaMap.put('\r', "\\r");
+ javaMap.put('\t', "\\t");
+ javaMap.put('\"', "\\\"");
+ javaMap.put('\\', "\\\\");
+ JAVA_STRING_ESCAPER_WITH_OCTAL = new JavaCharEscaperWithOctal(javaMap);
+ // The only difference is that the char escaper also escapes single quotes.
+ javaMap.put('\'', "\\'");
+ JAVA_CHAR_ESCAPER = new JavaCharEscaper(javaMap);
+ JAVA_CHAR_ESCAPER_WITH_OCTAL = new JavaCharEscaperWithOctal(javaMap);
+ }
+
+ // This escaper does not produce octal escape sequences. See:
+ // http://goo.gl/NsGW7
+ // "Octal escapes are provided for compatibility with C, but can express
+ // only Unicode values \u0000 through \u00FF, so Unicode escapes are
+ // usually preferred."
+ private static class JavaCharEscaper extends ArrayBasedCharEscaper {
+ JavaCharEscaper(Map<Character, String> replacements) {
+ super(replacements, PRINTABLE_ASCII_MIN, PRINTABLE_ASCII_MAX);
+ }
+
+ @Override protected char[] escapeUnsafe(char c) {
+ return asUnicodeHexEscape(c);
+ }
+ }
+
+ private static class JavaCharEscaperWithOctal extends ArrayBasedCharEscaper {
+ JavaCharEscaperWithOctal(Map<Character, String> replacements) {
+ super(replacements, PRINTABLE_ASCII_MIN, PRINTABLE_ASCII_MAX);
+ }
+
+ @Override protected char[] escapeUnsafe(char c) {
+ if (c < 0x100) {
+ return asOctalEscape(c);
+ } else {
+ return asUnicodeHexEscape(c);
+ }
+ }
+ }
+
+ /**
+ * Returns an {@link Escaper} instance that replaces non-ASCII characters
+ * in a string with their Unicode escape sequences ({@code \\uxxxx} where
+ * {@code xxxx} is a hex number). Existing escape sequences won't be affected.
+ *
+ * <p>As existing escape sequences are not re-escaped, this escaper is
+ * idempotent. However this means that there can be no well defined inverse
+ * function for this escaper.
+ *
+ * <p><b>Note:</b> the returned escaper is still a {@code CharEscaper} and
+ * will not combine surrogate pairs into a single code point before escaping.
+ */
+ public static Escaper javaStringUnicodeEscaper() {
+ return JAVA_STRING_UNICODE_ESCAPER;
+ }
+
+ private static final Escaper JAVA_STRING_UNICODE_ESCAPER
+ = new CharEscaper() {
+ @Override protected char[] escape(char c) {
+ if (c < 0x80) {
+ return null;
+ }
+ return asUnicodeHexEscape(c);
+ }
+ };
+
+ // Helper for common case of escaping a single char.
+ private static char[] asUnicodeHexEscape(char c) {
+ // Equivalent to String.format("\\u%04x", (int) c);
+ char[] r = new char[6];
+ r[0] = '\\';
+ r[1] = 'u';
+ r[5] = HEX_DIGITS[c & 0xF];
+ c >>>= 4;
+ r[4] = HEX_DIGITS[c & 0xF];
+ c >>>= 4;
+ r[3] = HEX_DIGITS[c & 0xF];
+ c >>>= 4;
+ r[2] = HEX_DIGITS[c & 0xF];
+ return r;
+ }
+
+ // Helper for backward compatible octal escape sequences (c < 256)
+ private static char[] asOctalEscape(char c) {
+ char[] r = new char[4];
+ r[0] = '\\';
+ r[3] = HEX_DIGITS[c & 0x7];
+ c >>>= 3;
+ r[2] = HEX_DIGITS[c & 0x7];
+ c >>>= 3;
+ r[1] = HEX_DIGITS[c & 0x3];
+ return r;
+ }
+}
diff --git a/compilerCommon/src/main/java/android/databinding/tool/util/StringUtils.java b/compilerCommon/src/main/java/android/databinding/tool/util/StringUtils.java
new file mode 100644
index 0000000..5f535ba
--- /dev/null
+++ b/compilerCommon/src/main/java/android/databinding/tool/util/StringUtils.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.util;
+
+import com.google.common.base.StandardSystemProperty;
+import com.google.common.base.Strings;
+
+import org.antlr.v4.runtime.misc.Nullable;
+
+public class StringUtils {
+
+ public static final String LINE_SEPARATOR = StandardSystemProperty.LINE_SEPARATOR.value();
+ /** The entity for the ampersand character */
+ private static final String AMP_ENTITY = "&";
+ /** The entity for the quote character */
+ private static final String QUOT_ENTITY = """;
+ /** The entity for the apostrophe character */
+ private static final String APOS_ENTITY = "'";
+ /** The entity for the less than character */
+ private static final String LT_ENTITY = "<";
+ /** The entity for the greater than character */
+ private static final String GT_ENTITY = ">";
+ /** The entity for the tab character */
+ private static final String TAB_ENTITY = "	";
+ /** The entity for the carriage return character */
+ private static final String CR_ENTITY = "
";
+ /** The entity for the line feed character */
+ private static final String LFEED_ENTITY = "
";
+
+ public static boolean isNotBlank(@Nullable CharSequence string) {
+ if (string == null) {
+ return false;
+ }
+ for (int i = 0, n = string.length(); i < n; i++) {
+ if (!Character.isWhitespace(string.charAt(i))) {
+ return true;
+ }
+ }
+ return false;
+ }
+
+ public static String capitalize(@Nullable String string) {
+ if (Strings.isNullOrEmpty(string)) {
+ return string;
+ }
+ char ch = string.charAt(0);
+ if (Character.isTitleCase(ch)) {
+ return string;
+ }
+ return Character.toTitleCase(ch) + string.substring(1);
+ }
+
+ public static String unescapeXml(String escaped) {
+ // TODO: unescape unicode codepoints
+ return escaped.replace(QUOT_ENTITY, "\"")
+ .replace(LT_ENTITY, "<")
+ .replace(GT_ENTITY, ">")
+ .replace(APOS_ENTITY, "'")
+ .replace(AMP_ENTITY, "&")
+ .replace(TAB_ENTITY, "\t")
+ .replace(CR_ENTITY, "\r")
+ .replace(LFEED_ENTITY, "\n");
+ }
+
+ private StringUtils() {
+ }
+
+}
diff --git a/compilerCommon/src/main/java/android/databinding/tool/util/XmlEditor.java b/compilerCommon/src/main/java/android/databinding/tool/util/XmlEditor.java
index c5f1c6c..e5a3da2 100644
--- a/compilerCommon/src/main/java/android/databinding/tool/util/XmlEditor.java
+++ b/compilerCommon/src/main/java/android/databinding/tool/util/XmlEditor.java
@@ -16,16 +16,6 @@
package android.databinding.tool.util;
-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.apache.commons.io.FileUtils;
-import org.apache.commons.lang3.StringEscapeUtils;
-import org.apache.commons.lang3.StringUtils;
-import org.apache.commons.lang3.tuple.ImmutablePair;
-import org.apache.commons.lang3.tuple.Pair;
-
import android.databinding.parser.BindingExpressionLexer;
import android.databinding.parser.BindingExpressionParser;
import android.databinding.parser.XMLLexer;
@@ -33,9 +23,19 @@
import android.databinding.parser.XMLParser.AttributeContext;
import android.databinding.parser.XMLParser.ElementContext;
+import com.google.common.base.Joiner;
+import com.google.common.xml.XmlEscapers;
+
+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.apache.commons.io.FileUtils;
+
import java.io.File;
-import java.io.FileReader;
+import java.io.FileInputStream;
import java.io.IOException;
+import java.io.InputStreamReader;
import java.util.ArrayList;
import java.util.Collections;
import java.util.Comparator;
@@ -47,43 +47,43 @@
*/
public class XmlEditor {
- public static String strip(File f, String newTag) throws IOException {
- ANTLRInputStream inputStream = new ANTLRInputStream(new FileReader(f));
+ public static String strip(File f, String newTag, String encoding) throws IOException {
+ FileInputStream fin = new FileInputStream(f);
+ InputStreamReader reader = new InputStreamReader(fin, encoding);
+ ANTLRInputStream inputStream = new ANTLRInputStream(reader);
XMLLexer lexer = new XMLLexer(inputStream);
CommonTokenStream tokenStream = new CommonTokenStream(lexer);
XMLParser parser = new XMLParser(tokenStream);
XMLParser.DocumentContext expr = parser.document();
- XMLParser.ElementContext root = expr.element();
+ ElementContext root = expr.element();
if (root == null || !"layout".equals(nodeName(root))) {
return null; // not a binding layout
}
List<? extends ElementContext> childrenOfRoot = elements(root);
- List<? extends XMLParser.ElementContext> dataNodes = filterNodesByName("data",
- childrenOfRoot);
+ List<? extends ElementContext> dataNodes = filterNodesByName("data", childrenOfRoot);
if (dataNodes.size() > 1) {
L.e("Multiple binding data tags in %s. Expecting a maximum of one.",
f.getAbsolutePath());
}
- ArrayList<String> lines = new ArrayList<>();
+ ArrayList<String> lines = new ArrayList<String>();
lines.addAll(FileUtils.readLines(f, "utf-8"));
- for (android.databinding.parser.XMLParser.ElementContext it : dataNodes) {
+ for (ElementContext it : dataNodes) {
replace(lines, toPosition(it.getStart()), toEndPosition(it.getStop()), "");
}
- List<? extends XMLParser.ElementContext> layoutNodes =
+ List<? extends ElementContext> layoutNodes =
excludeNodesByName("data", childrenOfRoot);
if (layoutNodes.size() != 1) {
L.e("Only one layout element and one data element are allowed. %s has %d",
f.getAbsolutePath(), layoutNodes.size());
}
- final XMLParser.ElementContext layoutNode = layoutNodes.get(0);
+ final ElementContext layoutNode = layoutNodes.get(0);
- ArrayList<Pair<String, android.databinding.parser.XMLParser.ElementContext>> noTag =
- new ArrayList<>();
+ ArrayList<TagAndContext> noTag = new ArrayList<TagAndContext>();
recurseReplace(layoutNode, lines, noTag, newTag, 0);
@@ -93,48 +93,46 @@
replace(lines, rootStartTag, rootEndTag, "");
// Remove the </layout>
- ImmutablePair<Position, Position> endLayoutPositions = findTerminalPositions(root, lines);
+ PositionPair endLayoutPositions = findTerminalPositions(root, lines);
replace(lines, endLayoutPositions.left, endLayoutPositions.right, "");
StringBuilder rootAttributes = new StringBuilder();
for (AttributeContext attr : attributes(root)) {
rootAttributes.append(' ').append(attr.getText());
}
- Pair<String, XMLParser.ElementContext> noTagRoot = null;
- for (Pair<String, XMLParser.ElementContext> pair : noTag) {
- if (pair.getRight() == layoutNode) {
- noTagRoot = pair;
+ TagAndContext noTagRoot = null;
+ for (TagAndContext tagAndContext : noTag) {
+ if (tagAndContext.getContext() == layoutNode) {
+ noTagRoot = tagAndContext;
break;
}
}
if (noTagRoot != null) {
- ImmutablePair<String, XMLParser.ElementContext>
- newRootTag = new ImmutablePair<>(
- noTagRoot.getLeft() + rootAttributes.toString(), layoutNode);
+ TagAndContext newRootTag = new TagAndContext(
+ noTagRoot.getTag() + rootAttributes.toString(), layoutNode);
int index = noTag.indexOf(noTagRoot);
noTag.set(index, newRootTag);
} else {
- ImmutablePair<String, XMLParser.ElementContext> newRootTag =
- new ImmutablePair<>(rootAttributes.toString(), layoutNode);
+ TagAndContext newRootTag =
+ new TagAndContext(rootAttributes.toString(), layoutNode);
noTag.add(newRootTag);
}
//noinspection NullableProblems
- Collections.sort(noTag, new Comparator<Pair<String, XMLParser.ElementContext>>() {
+ Collections.sort(noTag, new Comparator<TagAndContext>() {
@Override
- public int compare(Pair<String, XMLParser.ElementContext> o1,
- Pair<String, XMLParser.ElementContext> o2) {
- Position start1 = toPosition(o1.getRight().getStart());
- Position start2 = toPosition(o2.getRight().getStart());
- int lineCmp = Integer.compare(start2.line, start1.line);
+ public int compare(TagAndContext o1, TagAndContext o2) {
+ Position start1 = toPosition(o1.getContext().getStart());
+ Position start2 = toPosition(o2.getContext().getStart());
+ int lineCmp = start2.line - start1.line;
if (lineCmp != 0) {
return lineCmp;
}
- return Integer.compare(start2.charIndex, start1.charIndex);
+ return start2.charIndex - start1.charIndex;
}
});
- for (Pair<String, android.databinding.parser.XMLParser.ElementContext> it : noTag) {
- XMLParser.ElementContext element = it.getRight();
- String tag = it.getLeft();
+ for (TagAndContext it : noTag) {
+ ElementContext element = it.getContext();
+ String tag = it.getTag();
Position endTagPosition = endTagPosition(element);
fixPosition(lines, endTagPosition);
String line = lines.get(endTagPosition.line);
@@ -142,12 +140,12 @@
line.substring(endTagPosition.charIndex);
lines.set(endTagPosition.line, newLine);
}
- return StringUtils.join(lines, System.getProperty("line.separator"));
+ return Joiner.on(StringUtils.LINE_SEPARATOR).join(lines);
}
private static <T extends XMLParser.ElementContext> List<T>
filterNodesByName(String name, Iterable<T> items) {
- List<T> result = new ArrayList<>();
+ List<T> result = new ArrayList<T>();
for (T item : items) {
if (name.equals(nodeName(item))) {
result.add(item);
@@ -158,7 +156,7 @@
private static <T extends XMLParser.ElementContext> List<T>
excludeNodesByName(String name, Iterable<T> items) {
- List<T> result = new ArrayList<>();
+ List<T> result = new ArrayList<T>();
for (T item : items) {
if (!name.equals(nodeName(item))) {
result.add(item);
@@ -176,41 +174,49 @@
token.getCharPositionInLine() + token.getText().length());
}
- public static String nodeName(XMLParser.ElementContext elementContext) {
+ public static String nodeName(ElementContext elementContext) {
return elementContext.elmName.getText();
}
- public static List<? extends AttributeContext> attributes(XMLParser.ElementContext elementContext) {
- if (elementContext.attribute() == null) {
- return new ArrayList<>();
- } else {
+ public static List<? extends AttributeContext> attributes(ElementContext elementContext) {
+ if (elementContext.attribute() == null)
+ return new ArrayList<AttributeContext>();
+ else {
return elementContext.attribute();
}
}
- public static List<? extends AttributeContext> expressionAttributes (
- XMLParser.ElementContext elementContext) {
- List<AttributeContext> result = new ArrayList<>();
+ public static List<? extends AttributeContext> expressionAttributes(
+ ElementContext elementContext) {
+ List<AttributeContext> result = new ArrayList<AttributeContext>();
for (AttributeContext input : attributes(elementContext)) {
String attrName = input.attrName.getText();
- String value = input.attrValue.getText();
- if (attrName.equals("android:tag") ||
- (value.startsWith("\"@{") && value.endsWith("}\"")) ||
- (value.startsWith("'@{") && value.endsWith("}'"))) {
+ boolean isExpression = attrName.equals("android:tag");
+ if (!isExpression) {
+ final String value = input.attrValue.getText();
+ isExpression = isExpressionText(input.attrValue.getText());
+ }
+ if (isExpression) {
result.add(input);
}
}
return result;
}
- private static Position endTagPosition(XMLParser.ElementContext context) {
+ private static boolean isExpressionText(String value) {
+ // Check if the expression ends with "}" and starts with "@{" or "@={", ignoring
+ // the surrounding quotes.
+ return (value.length() > 5 && value.charAt(value.length() - 2) == '}' &&
+ ("@{".equals(value.substring(1, 3)) || "@={".equals(value.substring(1, 4))));
+ }
+
+ private static Position endTagPosition(ElementContext context) {
if (context.content() == null) {
- // no content, so just subtract from the "/>"
- Position endTag = toEndPosition(context.getStop());
+ // no content, so just choose the start of the "/>"
+ Position endTag = toPosition(context.getStop());
if (endTag.charIndex <= 0) {
L.e("invalid input in %s", context);
}
- endTag.charIndex -= 2;
return endTag;
} else {
// tag with no attributes, but with content
@@ -223,12 +229,11 @@
}
}
- public static List<? extends android.databinding.parser.XMLParser.ElementContext> elements(
- XMLParser.ElementContext context) {
+ public static List<? extends ElementContext> elements(ElementContext context) {
if (context.content() != null && context.content().element() != null) {
return context.content().element();
}
- return new ArrayList<>();
+ return new ArrayList<ElementContext>();
}
private static boolean replace(ArrayList<String> lines, Position start, Position end,
@@ -266,16 +271,22 @@
return line.substring(0, start) + newText + line.substring(end);
}
- public static boolean hasExpressionAttributes(XMLParser.ElementContext context) {
+ public static boolean hasExpressionAttributes(ElementContext context) {
List<? extends AttributeContext> expressions = expressionAttributes(context);
int size = expressions.size();
- //noinspection ConstantConditions
- return size > 1 || (size == 1 &&
- !expressions.get(0).attrName.getText().equals("android:tag"));
+ if (size == 0) {
+ return false;
+ } else if (size > 1) {
+ return true;
+ } else {
+ // android:tag is included, regardless, so we must only count as an expression
+ // if android:tag has a binding expression.
+ return isExpressionText(expressions.get(0).attrValue.getText());
+ }
}
- private static int recurseReplace(XMLParser.ElementContext node, ArrayList<String> lines,
- ArrayList<Pair<String, XMLParser.ElementContext>> noTag,
+ private static int recurseReplace(ElementContext node, ArrayList<String> lines,
+ ArrayList<TagAndContext> noTag,
String newTag, int bindingIndex) {
int nextBindingIndex = bindingIndex;
boolean isMerge = "merge".equals(nodeName(node));
@@ -300,7 +311,7 @@
}
}
if (tag.length() != 0) {
- noTag.add(new ImmutablePair<>(tag, node));
+ noTag.add(new TagAndContext(tag, node));
}
}
@@ -310,7 +321,7 @@
} else {
nextTag = null;
}
- for (XMLParser.ElementContext it : elements(node)) {
+ for (ElementContext it : elements(node)) {
nextBindingIndex = recurseReplace(it, lines, noTag, nextTag, nextBindingIndex);
}
return nextBindingIndex;
@@ -319,11 +330,14 @@
private static String defaultReplacement(XMLParser.AttributeContext attr) {
String textWithQuotes = attr.attrValue.getText();
String escapedText = textWithQuotes.substring(1, textWithQuotes.length() - 1);
- if (!escapedText.startsWith("@{") || !escapedText.endsWith("}")) {
+ final boolean isTwoWay = escapedText.startsWith("@={");
+ final boolean isOneWay = escapedText.startsWith("@{");
+ if ((!isTwoWay && !isOneWay) || !escapedText.endsWith("}")) {
return null;
}
- String text = StringEscapeUtils
- .unescapeXml(escapedText.substring(2, escapedText.length() - 1));
+ final int startIndex = isTwoWay ? 3 : 2;
+ final int endIndex = escapedText.length() - 1;
+ String text = StringUtils.unescapeXml(escapedText.substring(startIndex, endIndex));
ANTLRInputStream inputStream = new ANTLRInputStream(text);
BindingExpressionLexer lexer = new BindingExpressionLexer(inputStream);
CommonTokenStream tokenStream = new CommonTokenStream(lexer);
@@ -342,12 +356,12 @@
if (doubleQuote != null) {
String quotedStr = doubleQuote.getText();
String unquoted = quotedStr.substring(1, quotedStr.length() - 1);
- return StringEscapeUtils.escapeXml10(unquoted);
+ return XmlEscapers.xmlAttributeEscaper().escape(unquoted);
} else {
String quotedStr = stringLiteral.SingleQuoteString().getText();
String unquoted = quotedStr.substring(1, quotedStr.length() - 1);
String unescaped = unquoted.replace("\"", "\\\"").replace("\\`", "`");
- return StringEscapeUtils.escapeXml10(unescaped);
+ return XmlEscapers.xmlAttributeEscaper().escape(unescaped);
}
}
}
@@ -356,8 +370,8 @@
return null;
}
- private static ImmutablePair<Position, Position> findTerminalPositions(
- XMLParser.ElementContext node, ArrayList<String> lines) {
+ private static PositionPair findTerminalPositions(ElementContext node,
+ ArrayList<String> lines) {
Position endPosition = toEndPosition(node.getStop());
Position startPosition = toPosition(node.getStop());
int index;
@@ -368,7 +382,7 @@
startPosition.line++;
startPosition.charIndex = index;
//noinspection unchecked
- return new ImmutablePair<>(startPosition, endPosition);
+ return new PositionPair(startPosition, endPosition);
}
private static String replaceWithSpaces(String line, int start, int end) {
@@ -397,4 +411,31 @@
}
}
+ private static class TagAndContext {
+ private final String mTag;
+ private final ElementContext mElementContext;
+
+ private TagAndContext(String tag, ElementContext elementContext) {
+ mTag = tag;
+ mElementContext = elementContext;
+ }
+
+ private ElementContext getContext() {
+ return mElementContext;
+ }
+
+ private String getTag() {
+ return mTag;
+ }
+ }
+
+ private static class PositionPair {
+ private final Position left;
+ private final Position right;
+
+ private PositionPair(Position left, Position right) {
+ this.left = left;
+ this.right = right;
+ }
+ }
}
diff --git a/data-binding.iml b/data-binding.iml
new file mode 100644
index 0000000..bebe401
--- /dev/null
+++ b/data-binding.iml
@@ -0,0 +1,14 @@
+<?xml version="1.0" encoding="UTF-8"?>
+<module external.linked.project.id="data-binding" external.linked.project.path="$MODULE_DIR$" external.root.project.path="$MODULE_DIR$" external.system.id="GRADLE" external.system.module.group="" external.system.module.version="unspecified" type="JAVA_MODULE" version="4">
+ <component name="NewModuleRootManager" inherit-compiler-output="false">
+ <output url="file://$MODULE_DIR$/build" />
+ <output-test url="file://$MODULE_DIR$/build" />
+ <exclude-output />
+ <content url="file://$MODULE_DIR$">
+ <excludeFolder url="file://$MODULE_DIR$/.gradle" />
+ <excludeFolder url="file://$MODULE_DIR$/build" />
+ </content>
+ <orderEntry type="inheritedJdk" />
+ <orderEntry type="sourceFolder" forTests="false" />
+ </component>
+</module>
\ No newline at end of file
diff --git a/dataBinding/dataBinding.iml b/dataBinding/dataBinding.iml
new file mode 100644
index 0000000..0d65182
--- /dev/null
+++ b/dataBinding/dataBinding.iml
@@ -0,0 +1,14 @@
+<?xml version="1.0" encoding="UTF-8"?>
+<module external.linked.project.id=":dataBinding" external.linked.project.path="$MODULE_DIR$" external.root.project.path="$MODULE_DIR$/.." external.system.id="GRADLE" external.system.module.group="com.android.databinding" external.system.module.version="1.1" type="JAVA_MODULE" version="4">
+ <component name="NewModuleRootManager" inherit-compiler-output="false">
+ <output url="file://$MODULE_DIR$/build" />
+ <output-test url="file://$MODULE_DIR$/build" />
+ <exclude-output />
+ <content url="file://$MODULE_DIR$">
+ <excludeFolder url="file://$MODULE_DIR$/.gradle" />
+ <excludeFolder url="file://$MODULE_DIR$/build" />
+ </content>
+ <orderEntry type="inheritedJdk" />
+ <orderEntry type="sourceFolder" forTests="false" />
+ </component>
+</module>
\ No newline at end of file
diff --git a/databinding.properties b/databinding.properties
index 2a8a107..120a93e 100644
--- a/databinding.properties
+++ b/databinding.properties
@@ -1,11 +1,12 @@
# global settings for projects
-kotlinVersion = 0.12.613
-version = 1.0-rc2-SNAPSHOT
-releaseVersion = 1.0-rc2-SNAPSHOT
-androidPluginVersion = 1.3.0-beta4
-javaTargetCompatibility = 1.7
-javaSourceCompatibility = 1.7
-
+kotlinVersion = 1.0.0-beta-4584
+extensionsVersion = 1.0-rc5
+# we use a public plugin so that it does not need data binding while compiling library
+androidPublicPluginVersion= 1.5.0
+javaTargetCompatibility = 1.6
+javaSourceCompatibility = 1.6
+buildToolsVersion = 22.0.1
+compileSdkVersionStr = 23
prebuildFolderName=prebuilds
group=com.android.databinding
testGroup=com.android.databinding.test
@@ -19,6 +20,6 @@
# mavenRepoAbsolutePath is /Volumes/ssd/src and mavenRepoName is maven-repo
mavenRepoAbsolutePath=.
mavenRepoName=maven-repo
-extraPluginsRepoName=plugins-repo
-extraPluginsVersion=1.0
-
+internalPrebuiltsRepoName=internal-prebuilts
+extraPluginsVersion=1.1
+androidGradlePluginOutRepo=out/repo
diff --git a/developmentPlugins/bintrayPlugin/gradle/wrapper/gradle-wrapper.jar b/developmentPlugins/bintrayPlugin/gradle/wrapper/gradle-wrapper.jar
deleted file mode 100644
index 0087cd3..0000000
--- a/developmentPlugins/bintrayPlugin/gradle/wrapper/gradle-wrapper.jar
+++ /dev/null
Binary files differ
diff --git a/developmentPlugins/bintrayPlugin/gradle/wrapper/gradle-wrapper.properties b/developmentPlugins/bintrayPlugin/gradle/wrapper/gradle-wrapper.properties
deleted file mode 100644
index 72508b4..0000000
--- a/developmentPlugins/bintrayPlugin/gradle/wrapper/gradle-wrapper.properties
+++ /dev/null
@@ -1,6 +0,0 @@
-#Fri Jul 10 10:59:20 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/developmentPlugins/bintrayPlugin/gradlew b/developmentPlugins/bintrayPlugin/gradlew
deleted file mode 100755
index 91a7e26..0000000
--- a/developmentPlugins/bintrayPlugin/gradlew
+++ /dev/null
@@ -1,164 +0,0 @@
-#!/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/developmentPlugins/bintrayPlugin/gradlew.bat b/developmentPlugins/bintrayPlugin/gradlew.bat
deleted file mode 100644
index aec9973..0000000
--- a/developmentPlugins/bintrayPlugin/gradlew.bat
+++ /dev/null
@@ -1,90 +0,0 @@
-@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/developmentPlugins/bintrayPlugin/src/main/groovy/android/databinding/BintrayPlugin.groovy b/developmentPlugins/bintrayPlugin/src/main/groovy/android/databinding/BintrayPlugin.groovy
deleted file mode 100644
index fe9d1b1..0000000
--- a/developmentPlugins/bintrayPlugin/src/main/groovy/android/databinding/BintrayPlugin.groovy
+++ /dev/null
@@ -1,56 +0,0 @@
-/*
- * Copyright (C) 2015 The Android Open Source Project
- *
- * Licensed under the Apache License, Version 2.0 (the "License");
- * you may not use this file except in compliance with the License.
- * You may obtain a copy of the License at
- *
- * http://www.apache.org/licenses/LICENSE-2.0
- *
- * Unless required by applicable law or agreed to in writing, software
- * distributed under the License is distributed on an "AS IS" BASIS,
- * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
- * See the License for the specific language governing permissions and
- * limitations under the License.
- */
-package android.databinding
-
-import org.gradle.api.Plugin
-import org.gradle.api.Project
-import org.gradle.api.publication.maven.internal.ant.DefaultGroovyMavenDeployer
-import org.gradle.api.tasks.Upload
-
-class BintrayPlugin implements Plugin<Project> {
- public static final USER_PROP = "bintrayUser"
- public static final API_KEY_PROP = "bintrayApiKey"
- public static final DEFAULT_TASK_NAME = "uploadToBintray"
-
- @Override
- void apply(Project target) {
- def taskName = DEFAULT_TASK_NAME
- String user = target.getProperties().get(USER_PROP)
- String apiKey = target.getProperties().get(API_KEY_PROP)
- def uploadArchivesTask = target.tasks.findByName("uploadArchives")
- if (uploadArchivesTask == null) {
- throw new RuntimeException("Cannot find uploadArchives task in $target")
- }
- Upload uploadTask = uploadArchivesTask
- def bintrayTask = target.tasks.create(taskName, UploadToBintrayTask, {
- it.dependsOn uploadTask
- it.apiKey = apiKey
- it.username = user
- })
- target.afterEvaluate({
- def mavenDeployerRepo = uploadTask.repositories.findByName("mavenDeployer")
- if (mavenDeployerRepo == null) {
- throw new RuntimeException("Cannot find maven deployer repository")
- }
- DefaultGroovyMavenDeployer mavenDeployer = mavenDeployerRepo
- mavenDeployer.pom.whenConfigured({
- bintrayTask.configureFrom(mavenDeployer)
- })
- })
-
-
- }
-}
diff --git a/developmentPlugins/bintrayPlugin/src/main/groovy/android/databinding/UploadToBintrayTask.groovy b/developmentPlugins/bintrayPlugin/src/main/groovy/android/databinding/UploadToBintrayTask.groovy
deleted file mode 100644
index 0f1cc9a..0000000
--- a/developmentPlugins/bintrayPlugin/src/main/groovy/android/databinding/UploadToBintrayTask.groovy
+++ /dev/null
@@ -1,95 +0,0 @@
-/*
- * Copyright (C) 2015 The Android Open Source Project
- *
- * Licensed under the Apache License, Version 2.0 (the "License");
- * you may not use this file except in compliance with the License.
- * You may obtain a copy of the License at
- *
- * http://www.apache.org/licenses/LICENSE-2.0
- *
- * Unless required by applicable law or agreed to in writing, software
- * distributed under the License is distributed on an "AS IS" BASIS,
- * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
- * See the License for the specific language governing permissions and
- * limitations under the License.
- */
-
-package android.databinding;
-
-import org.gradle.api.DefaultTask
-import org.gradle.api.artifacts.maven.MavenPom
-import org.gradle.api.file.FileCollection
-import org.gradle.api.publication.maven.internal.ant.DefaultGroovyMavenDeployer
-import org.gradle.api.tasks.Input;
-import org.gradle.api.tasks.TaskAction;
-import groovy.json.JsonSlurper;
-
-public class UploadToBintrayTask extends DefaultTask {
- static final String API_URL = "https://api.bintray.com";
- static final String PACKAGE_HEADER = "X-Bintray-Package"
- static final String VERSION_HEADER = "X-Bintray-Version"
- static final String PUBLISH_HEADER = "X-Bintray-Publish"
- static final String OVERRIDE_HEADER = "X-Bintray-Override"
- static final String EXPLODE_HEADER = "X-Bintray-Explode"
- static final String CONTENT_PREFIX = "content/android/android-tools"
- String username;
- String apiKey;
- String pkg;
- String version;
- FileCollection localFiles;
- String mavenRepoAbsPath;
- String targetPath;
- public void configureFrom(DefaultGroovyMavenDeployer deployer) {
- String repoUrl = deployer.repository.url
- if (repoUrl == null) {
- throw new RuntimeException("Cannot find repo url for $deployer")
- }
-
- def pom = deployer.pom
- pkg = pom.groupId + "." + pom.artifactId
- version = pom.version
- mavenRepoAbsPath = repoUrl
- targetPath = "${pkg.replaceAll("\\.", "/")}/${version}"
- localFiles = project.fileTree(mavenRepoAbsPath + "/" + targetPath)
- }
- @TaskAction
- public void upload() {
- if (username == null || apiKey == null) {
- throw new IllegalArgumentException("You should pass your bintray user and " +
- "api key as params e.g. ./gradlew ${BintrayPlugin.DEFAULT_TASK_NAME}" +
- " -P${BintrayPlugin.USER_PROP}=<my username>" +
- " -P${BintrayPlugin.API_KEY_PROP}=<my api key>")
- }
- println(log())
- for (File localFile : localFiles) {
- def p = ['curl', '-u', "$username:$apiKey", "-H",
- "$PACKAGE_HEADER: $pkg", "-H", "$VERSION_HEADER: $version",
- "-X", "PUT", "--data-binary", "@${localFile.getAbsolutePath()}",
- "$API_URL/$CONTENT_PREFIX/$targetPath/${localFile.name}"]
- println("executing $p")
- def execute = p.execute()
- execute.waitFor()
- if (execute.exitValue() != 0) {
- throw new RuntimeException("failed to upload artifact. error: ${execute.err.text}")
- }
- def responseText = execute.text
- def json = new JsonSlurper().parseText(responseText)
- if (json.getAt("message") != "success") {
- throw new RuntimeException("Cannot upload artifact. Error response: " +
- "${json.getAt("message")}")
- }
- println("uploaded $localFile")
- }
- }
-
- public String log() {
- return "UploadToBintrayTask{" +
- "username='" + username + '\'' +
- ", apiKey='" + apiKey + '\'' +
- ", pkg='" + pkg + '\'' +
- ", version='" + version + '\'' +
- ", localFile=" + localFiles +
- ", mavenRepo=" + mavenRepoAbsPath +
- '}';
- }
-}
diff --git a/developmentPlugins/bintrayPlugin/src/main/resources/META-INF/gradle-plugins/com.android.databinding.bintray.properties b/developmentPlugins/bintrayPlugin/src/main/resources/META-INF/gradle-plugins/com.android.databinding.bintray.properties
deleted file mode 100644
index 3cf2d8c..0000000
--- a/developmentPlugins/bintrayPlugin/src/main/resources/META-INF/gradle-plugins/com.android.databinding.bintray.properties
+++ /dev/null
@@ -1,17 +0,0 @@
-#
-# Copyright (C) 2015 The Android Open Source Project
-#
-# Licensed under the Apache License, Version 2.0 (the "License");
-# you may not use this file except in compliance with the License.
-# You may obtain a copy of the License at
-#
-# http://www.apache.org/licenses/LICENSE-2.0
-#
-# Unless required by applicable law or agreed to in writing, software
-# distributed under the License is distributed on an "AS IS" BASIS,
-# WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
-# See the License for the specific language governing permissions and
-# limitations under the License.
-#
-
-implementation-class=android.databinding.BintrayPlugin
\ No newline at end of file
diff --git a/developmentPlugins/build.gradle b/developmentPlugins/build.gradle
index a99d215..dd70646 100644
--- a/developmentPlugins/build.gradle
+++ b/developmentPlugins/build.gradle
@@ -1,13 +1,13 @@
-ext.rootFolder = "${project.projectDir}/.."
+ext.dataBindingRootFolder = "${project.projectDir}/.."
apply from: '../propLoader.gradle'
subprojects {
apply plugin: 'maven'
- group = config.group
- version = config.extraPluginsVersion
+ group = dataBindingConfig.group
+ version = dataBindingConfig.extraPluginsVersion
uploadArchives {
repositories {
mavenDeployer {
- repository(url: "file://${config.extraPluginsRepoDir}")
+ repository(url: "file://${dataBindingConfig.internalPrebuiltsRepoDir}")
}
}
}
diff --git a/developmentPlugins/gradle/wrapper/gradle-wrapper.properties b/developmentPlugins/gradle/wrapper/gradle-wrapper.properties
index c2fda82..2e3da3f 100644
--- a/developmentPlugins/gradle/wrapper/gradle-wrapper.properties
+++ b/developmentPlugins/gradle/wrapper/gradle-wrapper.properties
@@ -3,4 +3,4 @@
distributionPath=wrapper/dists
zipStoreBase=GRADLE_USER_HOME
zipStorePath=wrapper/dists
-distributionUrl=https\://services.gradle.org/distributions/gradle-2.2.1-all.zip
+distributionUrl=https\://services.gradle.org/distributions/gradle-2.10-all.zip
diff --git a/developmentPlugins/localizeMavenPlugin/build.gradle b/developmentPlugins/localizeMavenPlugin/build.gradle
index 02d9b16..6b4c989 100644
--- a/developmentPlugins/localizeMavenPlugin/build.gradle
+++ b/developmentPlugins/localizeMavenPlugin/build.gradle
@@ -36,7 +36,6 @@
repositories {
mavenDeployer {
pom.artifactId = 'localizemaven'
- repository(url: "file://${config.extraPluginsRepoDir}")
}
}
}
\ No newline at end of file
diff --git a/developmentPlugins/localizeMavenPlugin/src/main/groovy/android/databinding/ExportLicensesTask.groovy b/developmentPlugins/localizeMavenPlugin/src/main/groovy/android/databinding/ExportLicensesTask.groovy
index cf3ef59..4db1f39 100644
--- a/developmentPlugins/localizeMavenPlugin/src/main/groovy/android/databinding/ExportLicensesTask.groovy
+++ b/developmentPlugins/localizeMavenPlugin/src/main/groovy/android/databinding/ExportLicensesTask.groovy
@@ -24,7 +24,8 @@
static def knownLicenses = [
[
- libraries: ["kotlin-stdlib", "kotlin-runtime", "kotlin-annotation-processing", "kotlin-gradle-plugin", "kotlin-gradle-plugin-api"],
+ libraries: ["kotlin-stdlib", "kotlin-runtime", "kotlin-annotation-processing", "kotlin-gradle-plugin", "kotlin-gradle-plugin-api",
+ "kdoc", "kotlin-gradle-plugin-core", "kotlin-jdk-annotations", "kotlin-compiler", "kotlin-compiler-embeddable"],
licenses : ["https://raw.githubusercontent.com/JetBrains/kotlin/master/license/LICENSE.txt",
"http://www.apache.org/licenses/LICENSE-2.0.txt"],
notices : ["https://raw.githubusercontent.com/JetBrains/kotlin/master/license/NOTICE.txt"]
@@ -96,9 +97,12 @@
],
[
libraries: ["logkit"],
- licenses: ["unknown. see: http://commons.apache.org/proper/commons-logging/dependencies.html"]
- ]
-
+ licenseText: ["unknown. see: http://commons.apache.org/proper/commons-logging/dependencies.html"]
+ ],
+ [
+ libraries: ["juniversalchardet"],
+ licenses: ["https://mozorg.cdn.mozilla.net/media/MPL/1.1/index.0c5913925d40.txt"]
+ ],
]
Map<String, Object> usedLicenses = new HashMap<>();
@@ -121,12 +125,15 @@
@TaskAction
public void exportNotice() {
- project.configurations.compile.getResolvedConfiguration().getResolvedArtifacts().each {
- add(it)
+ project.configurations.compile.getResolvedConfiguration()
+ .getFirstLevelModuleDependencies().each {
+ if (!it.getModuleGroup().equals("com.android.tools.build")) {
+ it.getAllModuleArtifacts().each { add(it) }
+ }
}
resolveLicenses()
def notice = buildNotice(usedLicenses)
- def noticeFile = new File(project.buildDir,'NOTICE.txt')
+ def noticeFile = new File("${project.projectDir}/src/main/resources",'NOTICE.txt')
noticeFile.delete()
println ("writing notice file to: ${noticeFile.getAbsolutePath()}")
noticeFile << notice
@@ -149,8 +156,7 @@
}
public static String urlToText(String url) {
- //return new URL(url).getText()
- return url
+ return new URL(url).getText()
}
public boolean shouldSkip(ResolvedArtifact artifact) {
@@ -174,6 +180,9 @@
license.licenses.each {
notice.append("\n ****** LICENSE:\n${urlToText(it)}")
}
+ license.licenseText.each {
+ notice.append("\n ****** LICENSE:\n${it}")
+ }
notice.append("\n\n\n")
}
return notice.toString()
diff --git a/developmentPlugins/localizeMavenPlugin/src/main/groovy/android/databinding/LocalizeDependenciesTask.groovy b/developmentPlugins/localizeMavenPlugin/src/main/groovy/android/databinding/LocalizeDependenciesTask.groovy
index a9e8d05..a14fabc 100644
--- a/developmentPlugins/localizeMavenPlugin/src/main/groovy/android/databinding/LocalizeDependenciesTask.groovy
+++ b/developmentPlugins/localizeMavenPlugin/src/main/groovy/android/databinding/LocalizeDependenciesTask.groovy
@@ -46,6 +46,12 @@
private Set<String> fetchTestDependencies = new HashSet<>();
+ // force download these if they are seen as a dependency
+ private Set<String> wildCard = new HashSet<>();
+ {
+ wildCard.add("kotlin-gradle-plugin-core")
+ }
+
List<Artifact> artifactsToResolve = new LinkedList<>();
Set<String> resolvedArtifacts = new HashSet<>();
@@ -64,9 +70,14 @@
LocalizePluginExtension extension = project.extensions.
getByName(MavenDependencyCollectorPlugin.EXTENSION_NAME)
if (extension.localRepoDir == null || extension.otherRepoDirs == null) {
- throw new IllegalArgumentException("you must configure " +
+
+ def msg = "you must configure " +
"${MavenDependencyCollectorPlugin.EXTENSION_NAME} with localRepoDir and" +
- " otherRepoDirs")
+ " otherRepoDirs. localRepoDir: " + extension.localRepoDir +
+ "\notherRepoDir:" + extension.otherRepoDirs;
+ println(msg)
+ println("skipping ${project}")
+ return
}
localRepoDir = extension.localRepoDir
downloadAll(extension.localRepoDir, extension.otherRepoDirs)
@@ -205,10 +216,10 @@
continue
}
if ("test".equals(dependency.scope)) {
- if (fetchTestDependencies.contains(key)) {
+ if (wildCard.contains(dependency.artifact.getArtifactId()) || fetchTestDependencies.contains(key)) {
println("${dependency} is test scope but including because $key is in direct dependencies")
} else {
- println("skipping $dependency because test and not first level dependency")
+ println("skipping $dependency because test and $key is not first level dependency. artifact id: ${dependency.artifact.getArtifactId()}")
continue
}
}
diff --git a/developmentPlugins/localizeMavenPlugin/src/main/groovy/android/databinding/MavenDependencyCollectorPlugin.groovy b/developmentPlugins/localizeMavenPlugin/src/main/groovy/android/databinding/MavenDependencyCollectorPlugin.groovy
index c3a318d..0fd805d 100644
--- a/developmentPlugins/localizeMavenPlugin/src/main/groovy/android/databinding/MavenDependencyCollectorPlugin.groovy
+++ b/developmentPlugins/localizeMavenPlugin/src/main/groovy/android/databinding/MavenDependencyCollectorPlugin.groovy
@@ -24,7 +24,7 @@
static final String EXTENSION_NAME = "localizeMaven"
@Override
void apply(Project project) {
- Project parent = project.parent == null ? project : project.parent;
+ Project parent = project.getRootProject();
def localizeDependenciesTask = parent.tasks.findByName(DEFAULT_TASK_NAME)
if (localizeDependenciesTask == null) {
localizeDependenciesTask = parent.tasks.
@@ -34,11 +34,12 @@
project.allprojects {
afterEvaluate { p ->
- project.tasks.create("collectDependenciesOf${it.getName().capitalize()}", MavenDependencyCollectorTask, {
- it.localizeTask = localizeDependenciesTask
- localizeDependenciesTask.dependsOn it
- })
-
+ if (!p.name.equals("dataBinding")) {
+ project.tasks.create("collectDependenciesOf${it.getName().capitalize()}", MavenDependencyCollectorTask, {
+ it.localizeTask = localizeDependenciesTask
+ localizeDependenciesTask.dependsOn it
+ })
+ }
}
}
project.tasks.create("buildLicenseNotice", ExportLicensesTask) {
diff --git a/extensions/baseAdapters/build.gradle b/extensions/baseAdapters/build.gradle
index 9450d42..d375aeb 100644
--- a/extensions/baseAdapters/build.gradle
+++ b/extensions/baseAdapters/build.gradle
@@ -13,18 +13,25 @@
* See the License for the specific language governing permissions and
* limitations under the License.
*/
-
+buildscript {
+ dependencies {
+ classpath "com.android.tools.build:gradle:${dataBindingConfig.androidPluginVersion}"
+ // NOTE: Do not place your application dependencies here; they belong
+ // in the individual module build.gradle files
+ }
+}
apply plugin: 'com.android.library'
-project.ext.addDataBindingAdapters = false
-apply plugin: 'com.android.databinding'
android {
- compileSdkVersion 21
- buildToolsVersion "21.1.2"
-
+ compileSdkVersion dataBindingConfig.compileSdkVersion
+ buildToolsVersion dataBindingConfig.buildToolsVersion
+ dataBinding {
+ enabled = true
+ addDefaultAdapters = false
+ }
defaultConfig {
minSdkVersion 7
- targetSdkVersion 21
+ targetSdkVersion 23
versionCode 1
versionName "1.0"
}
@@ -81,14 +88,13 @@
uploadArchives {
repositories {
mavenDeployer {
- repository(url: "file://${config.mavenRepoDir}")
pom.artifactId = "adapters"
pom.project {
licenses {
license {
- name config.licenseName
- url config.licenseUrl
- distribution config.licenseDistribution
+ name dataBindingConfig.licenseName
+ url dataBindingConfig.licenseUrl
+ distribution dataBindingConfig.licenseDistribution
}
}
}
@@ -100,7 +106,7 @@
task prebuild(type : Copy) {
dependsOn uploadArchives
from "$buildDir/outputs/aar/baseAdapters-release.aar"
- into config.prebuildFolder
+ into dataBindingConfig.prebuildFolder
rename { String fileName ->
"databinding-adapters.aar"
}
diff --git a/extensions/baseAdapters/src/main/java/android/databinding/adapters/AbsListViewBindingAdapter.java b/extensions/baseAdapters/src/main/java/android/databinding/adapters/AbsListViewBindingAdapter.java
index 50851ac..d2166ff 100644
--- a/extensions/baseAdapters/src/main/java/android/databinding/adapters/AbsListViewBindingAdapter.java
+++ b/extensions/baseAdapters/src/main/java/android/databinding/adapters/AbsListViewBindingAdapter.java
@@ -29,17 +29,8 @@
})
public class AbsListViewBindingAdapter {
- @BindingAdapter("android:onScroll")
- public static void setOnScroll(AbsListView view, OnScroll listener) {
- setOnScroll(view, listener, null);
- }
-
- @BindingAdapter("android:onScrollStateChanged")
- public static void setOnScroll(AbsListView view, OnScrollStateChanged listener) {
- setOnScroll(view, null, listener);
- }
-
- @BindingAdapter({"android:onScroll", "android:onScrollStateChanged"})
+ @BindingAdapter(value = {"android:onScroll", "android:onScrollStateChanged"},
+ requireAll = false)
public static void setOnScroll(AbsListView view, final OnScroll scrollListener,
final OnScrollStateChanged scrollStateListener) {
view.setOnScrollListener(new OnScrollListener() {
diff --git a/extensions/baseAdapters/src/main/java/android/databinding/adapters/AbsSpinnerBindingAdapter.java b/extensions/baseAdapters/src/main/java/android/databinding/adapters/AbsSpinnerBindingAdapter.java
index 1dc6974..2948c03 100644
--- a/extensions/baseAdapters/src/main/java/android/databinding/adapters/AbsSpinnerBindingAdapter.java
+++ b/extensions/baseAdapters/src/main/java/android/databinding/adapters/AbsSpinnerBindingAdapter.java
@@ -20,10 +20,12 @@
import android.widget.ArrayAdapter;
import android.widget.SpinnerAdapter;
+import java.util.List;
+
public class AbsSpinnerBindingAdapter {
@BindingAdapter({"android:entries"})
- public static void setEntries(AbsSpinner view, CharSequence[] entries) {
+ public static <T extends CharSequence> void setEntries(AbsSpinner view, T[] entries) {
if (entries != null) {
SpinnerAdapter oldAdapter = view.getAdapter();
boolean changed = true;
@@ -47,4 +49,20 @@
view.setAdapter(null);
}
}
+
+ @BindingAdapter({"android:entries"})
+ public static <T> void setEntries(AbsSpinner view, List<T> entries) {
+ if (entries != null) {
+ SpinnerAdapter oldAdapter = view.getAdapter();
+ if (oldAdapter instanceof ObservableListAdapter) {
+ ((ObservableListAdapter) oldAdapter).setList(entries);
+ } else {
+ view.setAdapter(new ObservableListAdapter<T>(view.getContext(), entries,
+ android.R.layout.simple_spinner_item,
+ android.R.layout.simple_spinner_dropdown_item, 0));
+ }
+ } else {
+ view.setAdapter(null);
+ }
+ }
}
diff --git a/extensions/baseAdapters/src/main/java/android/databinding/adapters/AdapterViewBindingAdapter.java b/extensions/baseAdapters/src/main/java/android/databinding/adapters/AdapterViewBindingAdapter.java
index ad85d79..5e51913 100644
--- a/extensions/baseAdapters/src/main/java/android/databinding/adapters/AdapterViewBindingAdapter.java
+++ b/extensions/baseAdapters/src/main/java/android/databinding/adapters/AdapterViewBindingAdapter.java
@@ -18,7 +18,11 @@
import android.databinding.BindingAdapter;
import android.databinding.BindingMethod;
import android.databinding.BindingMethods;
+import android.databinding.InverseBindingListener;
+import android.databinding.InverseBindingMethod;
+import android.databinding.InverseBindingMethods;
import android.view.View;
+import android.widget.AbsListView;
import android.widget.AdapterView;
import android.widget.AdapterView.OnItemSelectedListener;
@@ -26,43 +30,50 @@
@BindingMethod(type = AdapterView.class, attribute = "android:onItemClick", method = "setOnItemClickListener"),
@BindingMethod(type = AdapterView.class, attribute = "android:onItemLongClick", method = "setOnItemLongClickListener"),
})
+@InverseBindingMethods({
+ @InverseBindingMethod(type = AbsListView.class, attribute = "android:selectedItemPosition"),
+})
public class AdapterViewBindingAdapter {
- @BindingAdapter("android:onItemSelected")
- public static void setListener(AdapterView view, OnItemSelected listener) {
- setListener(view, listener, null);
+ @BindingAdapter("android:selectedItemPosition")
+ public static void setSelectedItemPosition(AdapterView view, int position) {
+ if (view.getSelectedItemPosition() != position) {
+ view.setSelection(position);
+ }
}
- @BindingAdapter("android:onNothingSelected")
- public static void setListener(AdapterView view, OnNothingSelected listener) {
- setListener(view, null, listener);
- }
-
- @BindingAdapter({"android:onItemSelected", "android:onNothingSelected"})
- public static void setListener(AdapterView view, final OnItemSelected selected,
- final OnNothingSelected nothingSelected) {
- if (selected == null && nothingSelected == null) {
+ @BindingAdapter(value = {"android:onItemSelected", "android:onNothingSelected",
+ "android:selectedItemPositionAttrChanged"}, requireAll = false)
+ public static void setOnItemSelectedListener(AdapterView view, final OnItemSelected selected,
+ final OnNothingSelected nothingSelected, final InverseBindingListener attrChanged) {
+ if (selected == null && nothingSelected == null && attrChanged == null) {
view.setOnItemSelectedListener(null);
} else {
view.setOnItemSelectedListener(
- new OnItemSelectedComponentListener(selected, nothingSelected));
+ new OnItemSelectedComponentListener(selected, nothingSelected, attrChanged));
}
}
public static class OnItemSelectedComponentListener implements OnItemSelectedListener {
private final OnItemSelected mSelected;
private final OnNothingSelected mNothingSelected;
+ private final InverseBindingListener mAttrChanged;
public OnItemSelectedComponentListener(OnItemSelected selected,
- OnNothingSelected nothingSelected) {
+ OnNothingSelected nothingSelected, InverseBindingListener attrChanged) {
this.mSelected = selected;
this.mNothingSelected = nothingSelected;
+ this.mAttrChanged = attrChanged;
}
+
@Override
public void onItemSelected(AdapterView<?> parent, View view, int position, long id) {
if (mSelected != null) {
mSelected.onItemSelected(parent, view, position, id);
}
+ if (mAttrChanged != null) {
+ mAttrChanged.onChange();
+ }
}
@Override
@@ -70,6 +81,9 @@
if (mNothingSelected != null) {
mNothingSelected.onNothingSelected(parent);
}
+ if (mAttrChanged != null) {
+ mAttrChanged.onChange();
+ }
}
}
diff --git a/extensions/baseAdapters/src/main/java/android/databinding/adapters/AutoCompleteTextViewBindingAdapter.java b/extensions/baseAdapters/src/main/java/android/databinding/adapters/AutoCompleteTextViewBindingAdapter.java
index 72f465e..ebcd118 100644
--- a/extensions/baseAdapters/src/main/java/android/databinding/adapters/AutoCompleteTextViewBindingAdapter.java
+++ b/extensions/baseAdapters/src/main/java/android/databinding/adapters/AutoCompleteTextViewBindingAdapter.java
@@ -32,18 +32,8 @@
})
public class AutoCompleteTextViewBindingAdapter {
- @BindingAdapter("android:fixText")
- public static void setListener(AutoCompleteTextView view, FixText listener) {
- setListener(view, listener, null);
- }
-
- @BindingAdapter("android:isValid")
- public static void setListener(AutoCompleteTextView view, IsValid listener) {
- setListener(view, null, listener);
- }
-
- @BindingAdapter({"android:fixText", "android:isValid"})
- public static void setListener(AutoCompleteTextView view, final FixText fixText,
+ @BindingAdapter(value = {"android:fixText", "android:isValid"}, requireAll = false)
+ public static void setValidator(AutoCompleteTextView view, final FixText fixText,
final IsValid isValid) {
if (fixText == null && isValid == null) {
view.setValidator(null);
@@ -70,24 +60,15 @@
}
}
- @BindingAdapter("android:onItemSelected")
- public static void setListener(AutoCompleteTextView view, OnItemSelected listener) {
- setListener(view, listener, null);
- }
-
- @BindingAdapter("android:onNothingSelected")
- public static void setListener(AutoCompleteTextView view, OnNothingSelected listener) {
- setListener(view, null, listener);
- }
-
- @BindingAdapter({"android:onItemSelected", "android:onNothingSelected"})
- public static void setListener(AutoCompleteTextView view, final OnItemSelected selected,
- final OnNothingSelected nothingSelected) {
+ @BindingAdapter(value = {"android:onItemSelected", "android:onNothingSelected"},
+ requireAll = false)
+ public static void setOnItemSelectedListener(AutoCompleteTextView view,
+ final OnItemSelected selected, final OnNothingSelected nothingSelected) {
if (selected == null && nothingSelected == null) {
view.setOnItemSelectedListener(null);
} else {
view.setOnItemSelectedListener(
- new OnItemSelectedComponentListener(selected, nothingSelected));
+ new OnItemSelectedComponentListener(selected, nothingSelected, null));
}
}
diff --git a/extensions/baseAdapters/src/main/java/android/databinding/adapters/CalendarViewBindingAdapter.java b/extensions/baseAdapters/src/main/java/android/databinding/adapters/CalendarViewBindingAdapter.java
index fc7fb2f..f17fd7d 100644
--- a/extensions/baseAdapters/src/main/java/android/databinding/adapters/CalendarViewBindingAdapter.java
+++ b/extensions/baseAdapters/src/main/java/android/databinding/adapters/CalendarViewBindingAdapter.java
@@ -15,12 +15,41 @@
*/
package android.databinding.adapters;
-import android.databinding.BindingMethod;
-import android.databinding.BindingMethods;
+import android.databinding.BindingAdapter;
+import android.databinding.InverseBindingListener;
+import android.databinding.InverseBindingMethod;
+import android.databinding.InverseBindingMethods;
import android.widget.CalendarView;
+import android.widget.CalendarView.OnDateChangeListener;
-@BindingMethods({
- @BindingMethod(type = CalendarView.class, attribute = "android:onSelectedDayChange", method = "setOnDateChangeListener"),
+@InverseBindingMethods({
+ @InverseBindingMethod(type = CalendarView.class, attribute = "android:date"),
})
public class CalendarViewBindingAdapter {
+ @BindingAdapter("android:date")
+ public static void setDate(CalendarView view, long date) {
+ if (view.getDate() != date) {
+ view.setDate(date);
+ }
+ }
+
+ @BindingAdapter(value = {"android:onSelectedDayChange", "android:dateAttrChanged"},
+ requireAll = false)
+ public static void setListeners(CalendarView view, final OnDateChangeListener onDayChange,
+ final InverseBindingListener attrChange) {
+ if (attrChange == null) {
+ view.setOnDateChangeListener(onDayChange);
+ } else {
+ view.setOnDateChangeListener(new OnDateChangeListener() {
+ @Override
+ public void onSelectedDayChange(CalendarView view, int year, int month,
+ int dayOfMonth) {
+ if (onDayChange != null) {
+ onDayChange.onSelectedDayChange(view, year, month, dayOfMonth);
+ }
+ attrChange.onChange();
+ }
+ });
+ }
+ }
}
diff --git a/extensions/baseAdapters/src/main/java/android/databinding/adapters/CompoundButtonBindingAdapter.java b/extensions/baseAdapters/src/main/java/android/databinding/adapters/CompoundButtonBindingAdapter.java
index 3859019..15c5d00 100644
--- a/extensions/baseAdapters/src/main/java/android/databinding/adapters/CompoundButtonBindingAdapter.java
+++ b/extensions/baseAdapters/src/main/java/android/databinding/adapters/CompoundButtonBindingAdapter.java
@@ -18,6 +18,9 @@
import android.databinding.BindingAdapter;
import android.databinding.BindingMethod;
import android.databinding.BindingMethods;
+import android.databinding.InverseBindingListener;
+import android.databinding.InverseBindingMethod;
+import android.databinding.InverseBindingMethods;
import android.widget.CompoundButton;
import android.widget.CompoundButton.OnCheckedChangeListener;
@@ -25,5 +28,35 @@
@BindingMethod(type = CompoundButton.class, attribute = "android:buttonTint", method = "setButtonTintList"),
@BindingMethod(type = CompoundButton.class, attribute = "android:onCheckedChanged", method = "setOnCheckedChangeListener"),
})
+@InverseBindingMethods({
+ @InverseBindingMethod(type = CompoundButton.class, attribute = "android:checked"),
+})
public class CompoundButtonBindingAdapter {
+
+ @BindingAdapter("android:checked")
+ public static void setChecked(CompoundButton view, boolean checked) {
+ if (view.isChecked() != checked) {
+ view.setChecked(checked);
+ }
+ }
+
+ @BindingAdapter(value = {"android:onCheckedChanged", "android:checkedAttrChanged"},
+ requireAll = false)
+ public static void setListeners(CompoundButton view, final OnCheckedChangeListener listener,
+ final InverseBindingListener attrChange) {
+ if (attrChange == null) {
+ view.setOnCheckedChangeListener(listener);
+ } else {
+ view.setOnCheckedChangeListener(new OnCheckedChangeListener() {
+ @Override
+ public void onCheckedChanged(CompoundButton buttonView, boolean isChecked) {
+ if (listener != null) {
+ listener.onCheckedChanged(buttonView, isChecked);
+ }
+ attrChange.onChange();
+ }
+ });
+ }
+ }
+
}
diff --git a/extensions/baseAdapters/src/main/java/android/databinding/adapters/DatePickerBindingAdapter.java b/extensions/baseAdapters/src/main/java/android/databinding/adapters/DatePickerBindingAdapter.java
new file mode 100644
index 0000000..abf345d
--- /dev/null
+++ b/extensions/baseAdapters/src/main/java/android/databinding/adapters/DatePickerBindingAdapter.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.adapters;
+
+import android.databinding.BindingAdapter;
+import android.databinding.InverseBindingListener;
+import android.databinding.InverseBindingMethod;
+import android.databinding.InverseBindingMethods;
+import android.widget.DatePicker;
+import android.widget.DatePicker.OnDateChangedListener;
+import com.android.databinding.library.baseAdapters.R;
+
+@InverseBindingMethods({
+ @InverseBindingMethod(type = DatePicker.class, attribute = "android:year"),
+ @InverseBindingMethod(type = DatePicker.class, attribute = "android:month"),
+ @InverseBindingMethod(type = DatePicker.class, attribute = "android:day", method = "getDayOfMonth"),
+})
+public class DatePickerBindingAdapter {
+ @BindingAdapter(value = {"android:year", "android:month", "android:day",
+ "android:onDateChanged", "android:yearAttrChanged",
+ "android:monthAttrChanged", "android:dayAttrChanged"}, requireAll = false)
+ public static void setListeners(DatePicker view, int year, int month, int day,
+ final OnDateChangedListener listener, final InverseBindingListener yearChanged,
+ final InverseBindingListener monthChanged, final InverseBindingListener dayChanged) {
+ if (year == 0) {
+ year = view.getYear();
+ }
+ if (day == 0) {
+ day = view.getDayOfMonth();
+ }
+ if (yearChanged == null && monthChanged == null && dayChanged == null) {
+ view.init(year, month, day, listener);
+ } else {
+ DateChangedListener oldListener = ListenerUtil.getListener(view, R.id.onDateChanged);
+ if (oldListener == null) {
+ oldListener = new DateChangedListener();
+ ListenerUtil.trackListener(view, oldListener, R.id.onDateChanged);
+ }
+ oldListener.setListeners(listener, yearChanged, monthChanged, dayChanged);
+ view.init(year, month, day, oldListener);
+ }
+ }
+
+ private static class DateChangedListener implements OnDateChangedListener {
+ OnDateChangedListener mListener;
+ InverseBindingListener mYearChanged;
+ InverseBindingListener mMonthChanged;
+ InverseBindingListener mDayChanged;
+
+ public void setListeners(OnDateChangedListener listener, InverseBindingListener yearChanged,
+ InverseBindingListener monthChanged, InverseBindingListener dayChanged) {
+ mListener = listener;
+ mYearChanged = yearChanged;
+ mMonthChanged = monthChanged;
+ mDayChanged = dayChanged;
+ }
+
+ @Override
+ public void onDateChanged(DatePicker view, int year, int monthOfYear, int dayOfMonth) {
+ if (mListener != null) {
+ mListener.onDateChanged(view, year, monthOfYear, dayOfMonth);
+ }
+ if (mYearChanged != null) {
+ mYearChanged.onChange();
+ }
+ if (mMonthChanged != null) {
+ mMonthChanged.onChange();
+ }
+ if (mDayChanged != null) {
+ mDayChanged.onChange();
+ }
+ }
+ }
+}
diff --git a/extensions/baseAdapters/src/main/java/android/databinding/adapters/ImageViewBindingAdapter.java b/extensions/baseAdapters/src/main/java/android/databinding/adapters/ImageViewBindingAdapter.java
index 5236d8d..abf5dfb 100644
--- a/extensions/baseAdapters/src/main/java/android/databinding/adapters/ImageViewBindingAdapter.java
+++ b/extensions/baseAdapters/src/main/java/android/databinding/adapters/ImageViewBindingAdapter.java
@@ -42,7 +42,7 @@
}
@BindingAdapter("android:src")
- public static void setImageUri(ImageView view, Drawable drawable) {
+ public static void setImageDrawable(ImageView view, Drawable drawable) {
view.setImageDrawable(drawable);
}
}
diff --git a/extensions/baseAdapters/src/main/java/android/databinding/adapters/ListenerUtil.java b/extensions/baseAdapters/src/main/java/android/databinding/adapters/ListenerUtil.java
index 9643be2..6869cdb 100644
--- a/extensions/baseAdapters/src/main/java/android/databinding/adapters/ListenerUtil.java
+++ b/extensions/baseAdapters/src/main/java/android/databinding/adapters/ListenerUtil.java
@@ -25,7 +25,7 @@
public class ListenerUtil {
private static SparseArray<WeakHashMap<View, WeakReference<?>>> sListeners =
- new SparseArray<>();
+ new SparseArray<WeakHashMap<View, WeakReference<?>>>();
/**
* This method tracks listeners for a View. Only one listener per listenerResourceId
@@ -61,7 +61,7 @@
synchronized (sListeners) {
WeakHashMap<View, WeakReference<?>> listeners = sListeners.get(listenerResourceId);
if (listeners == null) {
- listeners = new WeakHashMap<>();
+ listeners = new WeakHashMap<View, WeakReference<?>>();
sListeners.put(listenerResourceId, listeners);
}
final WeakReference<T> oldValue;
@@ -79,4 +79,31 @@
}
}
+ /**
+ * Returns the previous value for a listener if one was stored previously with
+ * {@link #trackListener(View, Object, int)}
+ * @param view The View to check for a listener previously stored with
+ * {@link #trackListener(View, Object, int)}
+ * @param listenerResourceId A unique resource ID associated with the listener type.
+ * @return The previously tracked listener. This will be null if the View did not have
+ * a previously-tracked listener.
+ */
+ public static <T> T getListener(View view, int listenerResourceId) {
+ if (VERSION.SDK_INT >= VERSION_CODES.ICE_CREAM_SANDWICH) {
+ return (T) view.getTag(listenerResourceId);
+ } else {
+ synchronized (sListeners) {
+ WeakHashMap<View, WeakReference<?>> listeners = sListeners.get(listenerResourceId);
+ if (listeners == null) {
+ return null;
+ }
+ final WeakReference<T> oldValue = (WeakReference<T>) listeners.get(view);
+ if (oldValue == null) {
+ return null;
+ } else {
+ return oldValue.get();
+ }
+ }
+ }
+ }
}
diff --git a/extensions/baseAdapters/src/main/java/android/databinding/adapters/NumberPickerBindingAdapter.java b/extensions/baseAdapters/src/main/java/android/databinding/adapters/NumberPickerBindingAdapter.java
index 17e53cd..612b320 100644
--- a/extensions/baseAdapters/src/main/java/android/databinding/adapters/NumberPickerBindingAdapter.java
+++ b/extensions/baseAdapters/src/main/java/android/databinding/adapters/NumberPickerBindingAdapter.java
@@ -15,14 +15,49 @@
*/
package android.databinding.adapters;
+import android.databinding.BindingAdapter;
import android.databinding.BindingMethod;
import android.databinding.BindingMethods;
+import android.databinding.InverseBindingListener;
+import android.databinding.InverseBindingMethod;
+import android.databinding.InverseBindingMethods;
+import android.widget.CompoundButton;
+import android.widget.CompoundButton.OnCheckedChangeListener;
import android.widget.NumberPicker;
+import android.widget.NumberPicker.OnValueChangeListener;
@BindingMethods({
@BindingMethod(type = NumberPicker.class, attribute = "android:format", method = "setFormatter"),
@BindingMethod(type = NumberPicker.class, attribute = "android:onScrollStateChange", method = "setOnScrollListener"),
- @BindingMethod(type = NumberPicker.class, attribute = "android:onValueChange", method = "setOnValueChangedListener"),
+})
+@InverseBindingMethods({
+ @InverseBindingMethod(type = NumberPicker.class, attribute = "android:value"),
})
public class NumberPickerBindingAdapter {
+
+ @BindingAdapter("android:value")
+ public static void setValue(NumberPicker view, int value) {
+ if (view.getValue() != value) {
+ view.setValue(value);
+ }
+ }
+
+ @BindingAdapter(value = {"android:onValueChange", "android:valueAttrChanged"},
+ requireAll = false)
+ public static void setListeners(NumberPicker view, final OnValueChangeListener listener,
+ final InverseBindingListener attrChange) {
+ if (attrChange == null) {
+ view.setOnValueChangedListener(listener);
+ } else {
+ view.setOnValueChangedListener(new OnValueChangeListener() {
+ @Override
+ public void onValueChange(NumberPicker picker, int oldVal, int newVal) {
+ if (listener != null) {
+ listener.onValueChange(picker, oldVal, newVal);
+ }
+ attrChange.onChange();
+ }
+ });
+ }
+ }
}
diff --git a/extensions/baseAdapters/src/main/java/android/databinding/adapters/ObservableListAdapter.java b/extensions/baseAdapters/src/main/java/android/databinding/adapters/ObservableListAdapter.java
new file mode 100644
index 0000000..93f76a9
--- /dev/null
+++ b/extensions/baseAdapters/src/main/java/android/databinding/adapters/ObservableListAdapter.java
@@ -0,0 +1,141 @@
+/*
+ * Copyright (C) 2015 The Android Open Source Project
+ *
+ * Licensed under the Apache License, Version 2.0 (the "License");
+ * you may not use this file except in compliance with the License.
+ * You may obtain a copy of the License at
+ *
+ * http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing, software
+ * distributed under the License is distributed on an "AS IS" BASIS,
+ * WITHOUT 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.content.Context;
+import android.databinding.ObservableList;
+import android.databinding.ObservableList.OnListChangedCallback;
+import android.view.LayoutInflater;
+import android.view.View;
+import android.view.ViewGroup;
+import android.widget.BaseAdapter;
+import android.widget.TextView;
+
+import java.util.List;
+
+class ObservableListAdapter<T> extends BaseAdapter {
+ private List<T> mList;
+ private ObservableList.OnListChangedCallback mListChangedCallback;
+ private final Context mContext;
+ private final int mDropDownResourceId;
+ private final int mResourceId;
+ private final int mTextViewResourceId;
+ private final LayoutInflater mLayoutInflater;
+
+ public ObservableListAdapter(Context context, List<T> list, int resourceId,
+ int dropDownResourceId, int textViewResourceId) {
+ mContext = context;
+ mResourceId = resourceId;
+ mDropDownResourceId = dropDownResourceId;
+ mTextViewResourceId = textViewResourceId;
+ mLayoutInflater = (resourceId == 0) ? null :
+ (LayoutInflater) context.getSystemService(Context.LAYOUT_INFLATER_SERVICE);
+ setList(list);
+ }
+
+ public void setList(List<T> list) {
+ if (mList == list) {
+ return;
+ }
+ if (mList instanceof ObservableList) {
+ ((ObservableList) mList).removeOnListChangedCallback(mListChangedCallback);
+ }
+ mList = list;
+ if (mList instanceof ObservableList) {
+ if (mListChangedCallback == null) {
+ mListChangedCallback = new OnListChangedCallback() {
+ @Override
+ public void onChanged(ObservableList observableList) {
+ notifyDataSetChanged();
+ }
+
+ @Override
+ public void onItemRangeChanged(ObservableList observableList, int i,
+ int i1) {
+ notifyDataSetChanged();
+ }
+
+ @Override
+ public void onItemRangeInserted(ObservableList observableList, int i,
+ int i1) {
+ notifyDataSetChanged();
+ }
+
+ @Override
+ public void onItemRangeMoved(ObservableList observableList, int i, int i1,
+ int i2) {
+ notifyDataSetChanged();
+ }
+
+ @Override
+ public void onItemRangeRemoved(ObservableList observableList, int i,
+ int i1) {
+ notifyDataSetChanged();
+ }
+ };
+ }
+ ((ObservableList) mList).addOnListChangedCallback(mListChangedCallback);
+ }
+ notifyDataSetChanged();
+ }
+
+ @Override
+ public int getCount() {
+ return mList.size();
+ }
+
+ @Override
+ public Object getItem(int position) {
+ return mList.get(position);
+ }
+
+ @Override
+ public long getItemId(int position) {
+ return position;
+ }
+
+ @Override
+ public View getView(int position, View convertView, ViewGroup parent) {
+ return getViewForResource(mResourceId, position, convertView, parent);
+ }
+
+ @Override
+ public View getDropDownView(int position, View convertView, ViewGroup parent) {
+ return getViewForResource(mDropDownResourceId, position, convertView, parent);
+ }
+
+ public View getViewForResource(int resourceId, int position, View convertView,
+ ViewGroup parent) {
+ if (convertView == null) {
+ if (resourceId == 0) {
+ convertView = new TextView(mContext);
+ } else {
+ convertView = mLayoutInflater.inflate(resourceId, parent, false);
+ }
+ }
+ TextView text = (TextView) (mTextViewResourceId == 0 ? convertView :
+ convertView.findViewById(mTextViewResourceId));
+ T item = mList.get(position);
+ CharSequence value;
+ if (item instanceof CharSequence) {
+ value = (CharSequence) item;
+ } else {
+ value = String.valueOf(item);
+ }
+ text.setText(value);
+ return convertView;
+ }
+}
diff --git a/extensions/baseAdapters/src/main/java/android/databinding/adapters/RadioGroupBindingAdapter.java b/extensions/baseAdapters/src/main/java/android/databinding/adapters/RadioGroupBindingAdapter.java
index af9f042..a84a691 100644
--- a/extensions/baseAdapters/src/main/java/android/databinding/adapters/RadioGroupBindingAdapter.java
+++ b/extensions/baseAdapters/src/main/java/android/databinding/adapters/RadioGroupBindingAdapter.java
@@ -15,13 +15,41 @@
*/
package android.databinding.adapters;
-import android.databinding.BindingMethod;
-import android.databinding.BindingMethods;
+import android.databinding.BindingAdapter;
+import android.databinding.InverseBindingListener;
+import android.databinding.InverseBindingMethod;
+import android.databinding.InverseBindingMethods;
import android.widget.RadioGroup;
+import android.widget.RadioGroup.OnCheckedChangeListener;
-@BindingMethods({
- @BindingMethod(type = RadioGroup.class, attribute = "android:checkedButton", method = "check"),
- @BindingMethod(type = RadioGroup.class, attribute = "android:onCheckedChanged", method = "setOnCheckedChangeListener"),
+@InverseBindingMethods({
+ @InverseBindingMethod(type = RadioGroup.class, attribute = "android:checkedButton", method = "getCheckedRadioButtonId"),
})
public class RadioGroupBindingAdapter {
+ @BindingAdapter("android:checkedButton")
+ public static void setCheckedButton(RadioGroup view, int id) {
+ if (id != view.getCheckedRadioButtonId()) {
+ view.check(id);
+ }
+ }
+
+ @BindingAdapter(value = {"android:onCheckedChanged", "android:checkedButtonAttrChanged"},
+ requireAll = false)
+ public static void setListeners(RadioGroup view, final OnCheckedChangeListener listener,
+ final InverseBindingListener attrChange) {
+ if (attrChange == null) {
+ view.setOnCheckedChangeListener(listener);
+ } else {
+ view.setOnCheckedChangeListener(new OnCheckedChangeListener() {
+ @Override
+ public void onCheckedChanged(RadioGroup group, int checkedId) {
+ if (listener != null) {
+ listener.onCheckedChanged(group, checkedId);
+ }
+
+ attrChange.onChange();
+ }
+ });
+ }
+ }
}
diff --git a/extensions/baseAdapters/src/main/java/android/databinding/adapters/RatingBarBindingAdapter.java b/extensions/baseAdapters/src/main/java/android/databinding/adapters/RatingBarBindingAdapter.java
index 5e49c72..4896df0 100644
--- a/extensions/baseAdapters/src/main/java/android/databinding/adapters/RatingBarBindingAdapter.java
+++ b/extensions/baseAdapters/src/main/java/android/databinding/adapters/RatingBarBindingAdapter.java
@@ -15,12 +15,40 @@
*/
package android.databinding.adapters;
-import android.databinding.BindingMethod;
-import android.databinding.BindingMethods;
+import android.databinding.BindingAdapter;
+import android.databinding.InverseBindingListener;
+import android.databinding.InverseBindingMethod;
+import android.databinding.InverseBindingMethods;
import android.widget.RatingBar;
+import android.widget.RatingBar.OnRatingBarChangeListener;
-@BindingMethods({
- @BindingMethod(type = RatingBar.class, attribute = "android:onRatingChanged", method = "setOnRatingBarChangeListener"),
+@InverseBindingMethods({
+ @InverseBindingMethod(type = RatingBar.class, attribute = "android:rating"),
})
public class RatingBarBindingAdapter {
+ @BindingAdapter("android:rating")
+ public static void setRating(RatingBar view, float rating) {
+ if (view.getRating() != rating) {
+ view.setRating(rating);
+ }
+ }
+
+ @BindingAdapter(value = {"android:onRatingChanged", "android:ratingAttrChanged"},
+ requireAll = false)
+ public static void setListeners(RatingBar view, final OnRatingBarChangeListener listener,
+ final InverseBindingListener ratingChange) {
+ if (ratingChange == null) {
+ view.setOnRatingBarChangeListener(listener);
+ } else {
+ view.setOnRatingBarChangeListener(new OnRatingBarChangeListener() {
+ @Override
+ public void onRatingChanged(RatingBar ratingBar, float rating, boolean fromUser) {
+ if (listener != null) {
+ listener.onRatingChanged(ratingBar, rating, fromUser);
+ }
+ ratingChange.onChange();
+ }
+ });
+ }
+ }
}
diff --git a/extensions/baseAdapters/src/main/java/android/databinding/adapters/SearchViewBindingAdapter.java b/extensions/baseAdapters/src/main/java/android/databinding/adapters/SearchViewBindingAdapter.java
index 25ddcf8..64d7270 100644
--- a/extensions/baseAdapters/src/main/java/android/databinding/adapters/SearchViewBindingAdapter.java
+++ b/extensions/baseAdapters/src/main/java/android/databinding/adapters/SearchViewBindingAdapter.java
@@ -19,12 +19,9 @@
import android.databinding.BindingAdapter;
import android.databinding.BindingMethod;
import android.databinding.BindingMethods;
-import android.os.Build;
import android.os.Build.VERSION;
import android.os.Build.VERSION_CODES;
-import android.widget.RatingBar;
import android.widget.SearchView;
-import android.widget.SearchView.OnCloseListener;
import android.widget.SearchView.OnQueryTextListener;
import android.widget.SearchView.OnSuggestionListener;
@@ -34,18 +31,9 @@
@BindingMethod(type = SearchView.class, attribute = "android:onClose", method = "setOnCloseListener"),
})
public class SearchViewBindingAdapter {
- @BindingAdapter("android:onQueryTextChange")
- public static void setListener(SearchView view, OnQueryTextChange listener) {
- setListener(view, null, listener);
- }
-
- @BindingAdapter("android:onQueryTextSubmit")
- public static void setListener(SearchView view, OnQueryTextSubmit listener) {
- setListener(view, listener, null);
- }
-
- @BindingAdapter({"android:onQueryTextSubmit", "android:onQueryTextChange"})
- public static void setListener(SearchView view, final OnQueryTextSubmit submit,
+ @BindingAdapter(value = {"android:onQueryTextSubmit", "android:onQueryTextChange"},
+ requireAll = false)
+ public static void setOnQueryTextListener(SearchView view, final OnQueryTextSubmit submit,
final OnQueryTextChange change) {
if (VERSION.SDK_INT >= VERSION_CODES.HONEYCOMB) {
if (submit == null && change == null){
@@ -74,18 +62,9 @@
}
}
- @BindingAdapter("android:onSuggestionClick")
- public static void setListener(SearchView view, OnSuggestionClick listener) {
- setListener(view, null, listener);
- }
-
- @BindingAdapter("android:onSuggestionSelect")
- public static void setListener(SearchView view, OnSuggestionSelect listener) {
- setListener(view, listener, null);
- }
-
- @BindingAdapter({"android:onSuggestionSelect", "android:onSuggestionClick"})
- public static void setListener(SearchView view, final OnSuggestionSelect submit,
+ @BindingAdapter(value = {"android:onSuggestionSelect", "android:onSuggestionClick"},
+ requireAll = false)
+ public static void setOnSuggestListener(SearchView view, final OnSuggestionSelect submit,
final OnSuggestionClick change) {
if (VERSION.SDK_INT >= VERSION_CODES.HONEYCOMB) {
if (submit == null && change == null) {
diff --git a/extensions/baseAdapters/src/main/java/android/databinding/adapters/SeekBarBindingAdapter.java b/extensions/baseAdapters/src/main/java/android/databinding/adapters/SeekBarBindingAdapter.java
index eeac77c..625c037 100644
--- a/extensions/baseAdapters/src/main/java/android/databinding/adapters/SeekBarBindingAdapter.java
+++ b/extensions/baseAdapters/src/main/java/android/databinding/adapters/SeekBarBindingAdapter.java
@@ -16,47 +16,30 @@
package android.databinding.adapters;
import android.databinding.BindingAdapter;
+import android.databinding.InverseBindingListener;
+import android.databinding.InverseBindingMethod;
+import android.databinding.InverseBindingMethods;
import android.widget.SeekBar;
import android.widget.SeekBar.OnSeekBarChangeListener;
+@InverseBindingMethods({
+ @InverseBindingMethod(type = SeekBar.class, attribute = "android:progress"),
+})
public class SeekBarBindingAdapter {
- @BindingAdapter("android:onProgressChanged")
- public static void setListener(SeekBar view, OnProgressChanged listener) {
- setListener(view, null, null, listener);
+
+ @BindingAdapter("android:progress")
+ public static void setProgress(SeekBar view, int progress) {
+ if (progress != view.getProgress()) {
+ view.setProgress(progress);
+ }
}
- @BindingAdapter("android:onStartTrackingTouch")
- public static void setListener(SeekBar view, OnStartTrackingTouch listener) {
- setListener(view, listener, null, null);
- }
-
- @BindingAdapter("android:onStopTrackingTouch")
- public static void setListener(SeekBar view, OnStopTrackingTouch listener) {
- setListener(view, null, listener, null);
- }
-
- @BindingAdapter({"android:onStartTrackingTouch", "android:onStopTrackingTouch"})
- public static void setListener(SeekBar view, final OnStartTrackingTouch start,
- final OnStopTrackingTouch stop) {
- setListener(view, start, stop, null);
- }
-
- @BindingAdapter({"android:onStartTrackingTouch", "android:onProgressChanged"})
- public static void setListener(SeekBar view, final OnStartTrackingTouch start,
- final OnProgressChanged progressChanged) {
- setListener(view, start, null, progressChanged);
- }
-
- @BindingAdapter({"android:onStopTrackingTouch", "android:onProgressChanged"})
- public static void setListener(SeekBar view, final OnStopTrackingTouch stop,
- final OnProgressChanged progressChanged) {
- setListener(view, null, stop, progressChanged);
- }
-
- @BindingAdapter({"android:onStartTrackingTouch", "android:onStopTrackingTouch", "android:onProgressChanged"})
- public static void setListener(SeekBar view, final OnStartTrackingTouch start,
- final OnStopTrackingTouch stop, final OnProgressChanged progressChanged) {
- if (start == null && stop == null && progressChanged == null) {
+ @BindingAdapter(value = {"android:onStartTrackingTouch", "android:onStopTrackingTouch",
+ "android:onProgressChanged", "android:progressAttrChanged"}, requireAll = false)
+ public static void setOnSeekBarChangeListener(SeekBar view, final OnStartTrackingTouch start,
+ final OnStopTrackingTouch stop, final OnProgressChanged progressChanged,
+ final InverseBindingListener attrChanged) {
+ if (start == null && stop == null && progressChanged == null && attrChanged == null) {
view.setOnSeekBarChangeListener(null);
} else {
view.setOnSeekBarChangeListener(new OnSeekBarChangeListener() {
@@ -65,6 +48,9 @@
if (progressChanged != null) {
progressChanged.onProgressChanged(seekBar, progress, fromUser);
}
+ if (attrChanged != null) {
+ attrChanged.onChange();
+ }
}
@Override
diff --git a/extensions/baseAdapters/src/main/java/android/databinding/adapters/TabHostBindingAdapter.java b/extensions/baseAdapters/src/main/java/android/databinding/adapters/TabHostBindingAdapter.java
index 78f06b0..151a711 100644
--- a/extensions/baseAdapters/src/main/java/android/databinding/adapters/TabHostBindingAdapter.java
+++ b/extensions/baseAdapters/src/main/java/android/databinding/adapters/TabHostBindingAdapter.java
@@ -15,12 +15,54 @@
*/
package android.databinding.adapters;
-import android.databinding.BindingMethod;
-import android.databinding.BindingMethods;
+import android.databinding.BindingAdapter;
+import android.databinding.InverseBindingAdapter;
+import android.databinding.InverseBindingListener;
import android.widget.TabHost;
+import android.widget.TabHost.OnTabChangeListener;
-@BindingMethods({
- @BindingMethod(type = TabHost.class, attribute = "android:onTabChanged", method = "setOnTabChangedListener"),
-})
public class TabHostBindingAdapter {
+
+ @InverseBindingAdapter(attribute = "android:currentTab")
+ public static int getCurrentTab(TabHost view) {
+ return view.getCurrentTab();
+ }
+
+ @InverseBindingAdapter(attribute = "android:currentTab")
+ public static String getCurrentTabTag(TabHost view) {
+ return view.getCurrentTabTag();
+ }
+
+ @BindingAdapter("android:currentTab")
+ public static void setCurrentTab(TabHost view, int tab) {
+ if (view.getCurrentTab() != tab) {
+ view.setCurrentTab(tab);
+ }
+ }
+
+ @BindingAdapter("android:currentTab")
+ public static void setCurrentTabTag(TabHost view, String tabTag) {
+ if (view.getCurrentTabTag() != tabTag) {
+ view.setCurrentTabByTag(tabTag);
+ }
+ }
+
+ @BindingAdapter(value = {"android:onTabChanged", "android:currentTabAttrChanged"},
+ requireAll = false)
+ public static void setListeners(TabHost view, final OnTabChangeListener listener,
+ final InverseBindingListener attrChange) {
+ if (attrChange == null) {
+ view.setOnTabChangedListener(listener);
+ } else {
+ view.setOnTabChangedListener(new OnTabChangeListener() {
+ @Override
+ public void onTabChanged(String tabId) {
+ if (listener != null) {
+ listener.onTabChanged(tabId);
+ }
+ attrChange.onChange();
+ }
+ });
+ }
+ }
}
diff --git a/extensions/baseAdapters/src/main/java/android/databinding/adapters/TextViewBindingAdapter.java b/extensions/baseAdapters/src/main/java/android/databinding/adapters/TextViewBindingAdapter.java
index 01ea006..2d3bebb 100644
--- a/extensions/baseAdapters/src/main/java/android/databinding/adapters/TextViewBindingAdapter.java
+++ b/extensions/baseAdapters/src/main/java/android/databinding/adapters/TextViewBindingAdapter.java
@@ -15,14 +15,21 @@
*/
package android.databinding.adapters;
+import com.android.databinding.library.baseAdapters.R;
+
import android.databinding.BindingAdapter;
import android.databinding.BindingMethod;
import android.databinding.BindingMethods;
+import android.databinding.InverseBindingAdapter;
+import android.databinding.InverseBindingListener;
import android.graphics.drawable.Drawable;
import android.os.Build;
import android.text.Editable;
import android.text.InputFilter;
import android.text.InputType;
+import android.text.Spanned;
+import android.text.SpannableString;
+import android.text.SpannableStringBuilder;
import android.text.TextWatcher;
import android.text.method.DialerKeyListener;
import android.text.method.DigitsKeyListener;
@@ -33,8 +40,6 @@
import android.util.TypedValue;
import android.widget.TextView;
-import com.android.databinding.library.baseAdapters.R;
-
@BindingMethods({
@BindingMethod(type = TextView.class, attribute = "android:autoLink", method = "setAutoLinkMask"),
@BindingMethod(type = TextView.class, attribute = "android:drawablePadding", method = "setCompoundDrawablePadding"),
@@ -50,13 +55,31 @@
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:text")
+ public static void setText(TextView view, CharSequence text) {
+ final CharSequence oldText = view.getText();
+ if (text == oldText || (text == null && oldText.length() == 0)) {
+ return;
+ }
+ if (text instanceof Spanned) {
+ if (text.equals(oldText)) {
+ return; // No change in the spans, so don't set anything.
+ }
+ } else if (!haveContentsChanged(text, oldText)) {
+ return; // No content changes, so don't set anything.
+ }
+ view.setText(text);
+ }
+
+ @InverseBindingAdapter(attribute = "android:text", event = "android:textAttrChanged")
+ public static String getTextString(TextView view) {
+ return view.getText().toString();
+ }
+
@BindingAdapter({"android:autoText"})
public static void setAutoText(TextView view, boolean autoText) {
KeyListener listener = view.getKeyListener();
@@ -112,28 +135,40 @@
}
}
+ private static void setIntrinsicBounds(Drawable drawable) {
+ if (drawable != null) {
+ drawable.setBounds(0, 0, drawable.getIntrinsicWidth(), drawable.getIntrinsicHeight());
+ }
+ }
+
@BindingAdapter({"android:drawableBottom"})
public static void setDrawableBottom(TextView view, Drawable drawable) {
+ setIntrinsicBounds(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) {
+ setIntrinsicBounds(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) {
+ setIntrinsicBounds(drawable);
Drawable[] drawables = view.getCompoundDrawables();
- view.setCompoundDrawables(drawables[0], drawables[1], drawable, drawables[3]);
+ view.setCompoundDrawables(drawables[0], drawables[1], drawable,
+ drawables[3]);
}
@BindingAdapter({"android:drawableTop"})
public static void setDrawableTop(TextView view, Drawable drawable) {
+ setIntrinsicBounds(drawable);
Drawable[] drawables = view.getCompoundDrawables();
- view.setCompoundDrawables(drawables[0], drawable, drawables[2], drawables[3]);
+ view.setCompoundDrawables(drawables[0], drawable, drawables[2],
+ drawables[3]);
}
@BindingAdapter({"android:drawableStart"})
@@ -141,6 +176,7 @@
if (Build.VERSION.SDK_INT < Build.VERSION_CODES.JELLY_BEAN_MR1) {
setDrawableLeft(view, drawable);
} else {
+ setIntrinsicBounds(drawable);
Drawable[] drawables = view.getCompoundDrawablesRelative();
view.setCompoundDrawablesRelative(drawable, drawables[1], drawables[2], drawables[3]);
}
@@ -151,6 +187,7 @@
if (Build.VERSION.SDK_INT < Build.VERSION_CODES.JELLY_BEAN_MR1) {
setDrawableRight(view, drawable);
} else {
+ setIntrinsicBounds(drawable);
Drawable[] drawables = view.getCompoundDrawablesRelative();
view.setCompoundDrawablesRelative(drawables[0], drawables[1], drawable, drawables[3]);
}
@@ -286,44 +323,31 @@
view.setTextSize(TypedValue.COMPLEX_UNIT_PX, size);
}
- @BindingAdapter("android:afterTextChanged")
- public static void setListener(TextView view, AfterTextChanged after) {
- setListener(view, null, null, after);
+ private static boolean haveContentsChanged(CharSequence str1, CharSequence str2) {
+ if ((str1 == null) != (str2 == null)) {
+ return true;
+ } else if (str1 == null) {
+ return false;
+ }
+ final int length = str1.length();
+ if (length != str2.length()) {
+ return true;
+ }
+ for (int i = 0; i < length; i++) {
+ if (str1.charAt(i) != str2.charAt(i)) {
+ return true;
+ }
+ }
+ return false;
}
- @BindingAdapter("android:beforeTextChanged")
- public static void setListener(TextView view, BeforeTextChanged before) {
- setListener(view, before, null, null);
- }
-
- @BindingAdapter("android:onTextChanged")
- public static void setListener(TextView view, OnTextChanged onTextChanged) {
- setListener(view, null, onTextChanged, null);
- }
-
- @BindingAdapter({"android:beforeTextChanged", "android:afterTextChanged"})
- public static void setListener(TextView view, final BeforeTextChanged before,
- final AfterTextChanged after) {
- setListener(view, before, null, after);
- }
-
- @BindingAdapter({"android:beforeTextChanged", "android:onTextChanged"})
- public static void setListener(TextView view, final BeforeTextChanged before,
- final OnTextChanged on) {
- setListener(view, before, on, null);
- }
-
- @BindingAdapter({"android:onTextChanged", "android:afterTextChanged"})
- public static void setListener(TextView view,final OnTextChanged on,
- final AfterTextChanged after) {
- setListener(view, null, on, after);
- }
-
- @BindingAdapter({"android:beforeTextChanged", "android:onTextChanged", "android:afterTextChanged"})
- public static void setListener(TextView view, final BeforeTextChanged before,
- final OnTextChanged on, final AfterTextChanged after) {
+ @BindingAdapter(value = {"android:beforeTextChanged", "android:onTextChanged",
+ "android:afterTextChanged", "android:textAttrChanged"}, requireAll = false)
+ public static void setTextWatcher(TextView view, final BeforeTextChanged before,
+ final OnTextChanged on, final AfterTextChanged after,
+ final InverseBindingListener textAttrChanged) {
final TextWatcher newValue;
- if (before == null && after == null && on == null) {
+ if (before == null && after == null && on == null && textAttrChanged == null) {
newValue = null;
} else {
newValue = new TextWatcher() {
@@ -339,6 +363,9 @@
if (on != null) {
on.onTextChanged(s, start, before, count);
}
+ if (textAttrChanged != null) {
+ textAttrChanged.onChange();
+ }
}
@Override
diff --git a/extensions/baseAdapters/src/main/java/android/databinding/adapters/TimePickerBindingAdapter.java b/extensions/baseAdapters/src/main/java/android/databinding/adapters/TimePickerBindingAdapter.java
index 3a11dab..803342d 100644
--- a/extensions/baseAdapters/src/main/java/android/databinding/adapters/TimePickerBindingAdapter.java
+++ b/extensions/baseAdapters/src/main/java/android/databinding/adapters/TimePickerBindingAdapter.java
@@ -15,12 +15,95 @@
*/
package android.databinding.adapters;
-import android.databinding.BindingMethod;
-import android.databinding.BindingMethods;
+import android.databinding.BindingAdapter;
+import android.databinding.InverseBindingAdapter;
+import android.databinding.InverseBindingListener;
+import android.os.Build.VERSION;
+import android.os.Build.VERSION_CODES;
import android.widget.TimePicker;
+import android.widget.TimePicker.OnTimeChangedListener;
-@BindingMethods({
- @BindingMethod(type = TimePicker.class, attribute = "android:onTimeChanged", method = "setOnTimeChangedListener"),
-})
public class TimePickerBindingAdapter {
+
+ @SuppressWarnings("deprecation")
+ @BindingAdapter("android:hour")
+ public static void setHour(TimePicker view, int hour) {
+ if (VERSION.SDK_INT >= VERSION_CODES.M) {
+ if (view.getHour() != hour) {
+ view.setHour(hour);
+ }
+ } else {
+ if (view.getCurrentHour() != hour) {
+ view.setCurrentHour(hour);
+ }
+ }
+ }
+
+ @SuppressWarnings("deprecation")
+ @BindingAdapter("android:minute")
+ public static void setMinute(TimePicker view, int minute) {
+ if (VERSION.SDK_INT >= VERSION_CODES.M) {
+ if (view.getMinute() != minute) {
+ view.setMinute(minute);
+ }
+ } else {
+ if (view.getCurrentMinute() != minute) {
+ view.setCurrentHour(minute);
+ }
+ }
+ }
+
+ @InverseBindingAdapter(attribute = "android:hour")
+ public static int getHour(TimePicker view) {
+ if (VERSION.SDK_INT >= VERSION_CODES.M) {
+ return view.getHour();
+ } else {
+ @SuppressWarnings("deprecation")
+ Integer hour = view.getCurrentHour();
+ if (hour == null) {
+ return 0;
+ } else {
+ return hour;
+ }
+ }
+ }
+
+ @InverseBindingAdapter(attribute = "android:minute")
+ public static int getMinute(TimePicker view) {
+ if (VERSION.SDK_INT >= VERSION_CODES.M) {
+ return view.getMinute();
+ } else {
+ @SuppressWarnings("deprecation")
+ Integer minute = view.getCurrentMinute();
+ if (minute == null) {
+ return 0;
+ } else {
+ return minute;
+ }
+ }
+ }
+
+ @BindingAdapter(value = {"android:onTimeChanged", "android:hourAttrChanged",
+ "android:minuteAttrChanged"}, requireAll = false)
+ public static void setListeners(TimePicker view, final OnTimeChangedListener listener,
+ final InverseBindingListener hourChange, final InverseBindingListener minuteChange) {
+ if (hourChange == null && minuteChange == null) {
+ view.setOnTimeChangedListener(listener);
+ } else {
+ view.setOnTimeChangedListener(new OnTimeChangedListener() {
+ @Override
+ public void onTimeChanged(TimePicker view, int hourOfDay, int minute) {
+ if (listener != null) {
+ listener.onTimeChanged(view, hourOfDay, minute);
+ }
+ if (hourChange != null) {
+ hourChange.onChange();
+ }
+ if (minuteChange != null) {
+ minuteChange.onChange();
+ }
+ }
+ });
+ }
+ }
}
diff --git a/extensions/baseAdapters/src/main/java/android/databinding/adapters/ViewBindingAdapter.java b/extensions/baseAdapters/src/main/java/android/databinding/adapters/ViewBindingAdapter.java
index f9fae7b..aece41b 100644
--- a/extensions/baseAdapters/src/main/java/android/databinding/adapters/ViewBindingAdapter.java
+++ b/extensions/baseAdapters/src/main/java/android/databinding/adapters/ViewBindingAdapter.java
@@ -19,6 +19,7 @@
import android.databinding.BindingAdapter;
import android.databinding.BindingMethod;
import android.databinding.BindingMethods;
+import android.graphics.drawable.Drawable;
import android.os.Build;
import android.os.Build.VERSION;
import android.os.Build.VERSION_CODES;
@@ -60,18 +61,21 @@
public static int FADING_EDGE_VERTICAL = 2;
@BindingAdapter({"android:padding"})
- public static void setPadding(View view, int padding) {
+ public static void setPadding(View view, float paddingFloat) {
+ final int padding = pixelsToDimensionPixelSize(paddingFloat);
view.setPadding(padding, padding, padding, padding);
}
@BindingAdapter({"android:paddingBottom"})
- public static void setPaddingBottom(View view, int padding) {
+ public static void setPaddingBottom(View view, float paddingFloat) {
+ final int padding = pixelsToDimensionPixelSize(paddingFloat);
view.setPadding(view.getPaddingLeft(), view.getPaddingTop(), view.getPaddingRight(),
padding);
}
@BindingAdapter({"android:paddingEnd"})
- public static void setPaddingEnd(View view, int padding) {
+ public static void setPaddingEnd(View view, float paddingFloat) {
+ final int padding = pixelsToDimensionPixelSize(paddingFloat);
if (Build.VERSION.SDK_INT >= Build.VERSION_CODES.JELLY_BEAN_MR1) {
view.setPaddingRelative(view.getPaddingStart(), view.getPaddingTop(), padding,
view.getPaddingBottom());
@@ -82,19 +86,22 @@
}
@BindingAdapter({"android:paddingLeft"})
- public static void setPaddingLeft(View view, int padding) {
+ public static void setPaddingLeft(View view, float paddingFloat) {
+ final int padding = pixelsToDimensionPixelSize(paddingFloat);
view.setPadding(padding, view.getPaddingTop(), view.getPaddingRight(),
view.getPaddingBottom());
}
@BindingAdapter({"android:paddingRight"})
- public static void setPaddingRight(View view, int padding) {
+ public static void setPaddingRight(View view, float paddingFloat) {
+ final int padding = pixelsToDimensionPixelSize(paddingFloat);
view.setPadding(view.getPaddingLeft(), view.getPaddingTop(), padding,
view.getPaddingBottom());
}
@BindingAdapter({"android:paddingStart"})
- public static void setPaddingStart(View view, int padding) {
+ public static void setPaddingStart(View view, float paddingFloat) {
+ final int padding = pixelsToDimensionPixelSize(paddingFloat);
if (Build.VERSION.SDK_INT >= Build.VERSION_CODES.JELLY_BEAN_MR1) {
view.setPaddingRelative(padding, view.getPaddingTop(), view.getPaddingEnd(),
view.getPaddingBottom());
@@ -105,7 +112,8 @@
}
@BindingAdapter({"android:paddingTop"})
- public static void setPaddingTop(View view, int padding) {
+ public static void setPaddingTop(View view, float paddingFloat) {
+ final int padding = pixelsToDimensionPixelSize(paddingFloat);
view.setPadding(view.getPaddingLeft(), padding, view.getPaddingRight(),
view.getPaddingBottom());
}
@@ -133,7 +141,7 @@
}
@BindingAdapter({"android:onLongClickListener", "android:longClickable"})
- public static void setListener(View view, View.OnLongClickListener clickListener,
+ public static void setOnLongClickListener(View view, View.OnLongClickListener clickListener,
boolean clickable) {
view.setOnLongClickListener(clickListener);
view.setLongClickable(clickable);
@@ -146,19 +154,10 @@
view.setLongClickable(clickable);
}
- @BindingAdapter("android:onViewAttachedToWindow")
- public static void setListener(View view, OnViewAttachedToWindow attached) {
- setListener(view, null, attached);
- }
-
- @BindingAdapter("android:onViewDetachedFromWindow")
- public static void setListener(View view, OnViewDetachedFromWindow detached) {
- setListener(view, detached, null);
- }
-
- @BindingAdapter({"android:onViewDetachedFromWindow", "android:onViewAttachedToWindow"})
- public static void setListener(View view, final OnViewDetachedFromWindow detach,
- final OnViewAttachedToWindow attach) {
+ @BindingAdapter(value = {"android:onViewDetachedFromWindow", "android:onViewAttachedToWindow"},
+ requireAll = false)
+ public static void setOnAttachStateChangeListener(View view,
+ final OnViewDetachedFromWindow detach, final OnViewAttachedToWindow attach) {
if (VERSION.SDK_INT >= VERSION_CODES.HONEYCOMB_MR1) {
final OnAttachStateChangeListener newListener;
if (detach == null && attach == null) {
@@ -204,6 +203,32 @@
}
}
+ @SuppressWarnings("deprecation")
+ @BindingAdapter("android:background")
+ public static void setBackground(View view, Drawable drawable) {
+ if (VERSION.SDK_INT >= VERSION_CODES.JELLY_BEAN) {
+ view.setBackground(drawable);
+ } else {
+ view.setBackgroundDrawable(drawable);
+ }
+ }
+
+ // Follows the same conversion mechanism as in TypedValue.complexToDimensionPixelSize as used
+ // when setting padding. It rounds off the float value unless the value is < 1.
+ // When a value is between 0 and 1, it is set to 1. A value less than 0 is set to -1.
+ private static int pixelsToDimensionPixelSize(float pixels) {
+ final int result = (int) (pixels + 0.5f);
+ if (result != 0) {
+ return result;
+ } else if (pixels == 0) {
+ return 0;
+ } else if (pixels > 0) {
+ return 1;
+ } else {
+ return -1;
+ }
+ }
+
@TargetApi(VERSION_CODES.HONEYCOMB_MR1)
public interface OnViewDetachedFromWindow {
void onViewDetachedFromWindow(View v);
diff --git a/extensions/baseAdapters/src/main/java/android/databinding/adapters/ViewGroupBindingAdapter.java b/extensions/baseAdapters/src/main/java/android/databinding/adapters/ViewGroupBindingAdapter.java
index af10818..8b1978c 100644
--- a/extensions/baseAdapters/src/main/java/android/databinding/adapters/ViewGroupBindingAdapter.java
+++ b/extensions/baseAdapters/src/main/java/android/databinding/adapters/ViewGroupBindingAdapter.java
@@ -44,17 +44,8 @@
}
}
- @BindingAdapter("android:onChildViewAdded")
- public static void setListener(ViewGroup view, OnChildViewAdded listener) {
- setListener(view, listener, null);
- }
-
- @BindingAdapter("android:onChildViewRemoved")
- public static void setListener(ViewGroup view, OnChildViewRemoved listener) {
- setListener(view, null, listener);
- }
-
- @BindingAdapter({"android:onChildViewAdded", "android:onChildViewRemoved"})
+ @BindingAdapter(value = {"android:onChildViewAdded", "android:onChildViewRemoved"},
+ requireAll = false)
public static void setListener(ViewGroup view, final OnChildViewAdded added,
final OnChildViewRemoved removed) {
if (added == null && removed == null) {
@@ -78,8 +69,8 @@
}
}
- @BindingAdapter({"android:onAnimationStart", "android:onAnimationEnd",
- "android:onAnimationRepeat"})
+ @BindingAdapter(value = {"android:onAnimationStart", "android:onAnimationEnd",
+ "android:onAnimationRepeat"}, requireAll = false)
public static void setListener(ViewGroup view, final OnAnimationStart start,
final OnAnimationEnd end, final OnAnimationRepeat repeat) {
if (start == null && end == null && repeat == null) {
@@ -110,39 +101,6 @@
}
}
- @BindingAdapter({"android:onAnimationStart", "android:onAnimationEnd"})
- public static void setListener(ViewGroup view, final OnAnimationStart start,
- final OnAnimationEnd end) {
- setListener(view, start, end, null);
- }
-
- @BindingAdapter({"android:onAnimationEnd", "android:onAnimationRepeat"})
- public static void setListener(ViewGroup view, final OnAnimationEnd end,
- final OnAnimationRepeat repeat) {
- setListener(view, null, end, repeat);
- }
-
- @BindingAdapter({"android:onAnimationStart", "android:onAnimationRepeat"})
- public static void setListener(ViewGroup view, final OnAnimationStart start,
- final OnAnimationRepeat repeat) {
- setListener(view, start, null, repeat);
- }
-
- @BindingAdapter("android:onAnimationStart")
- public static void setListener(ViewGroup view, final OnAnimationStart start) {
- setListener(view, start, null, null);
- }
-
- @BindingAdapter("android:onAnimationEnd")
- public static void setListener(ViewGroup view, final OnAnimationEnd end) {
- setListener(view, null, end, null);
- }
-
- @BindingAdapter("android:onAnimationRepeat")
- public static void setListener(ViewGroup view, final OnAnimationRepeat repeat) {
- setListener(view, null, null, repeat);
- }
-
public interface OnChildViewAdded {
void onChildViewAdded(View parent, View child);
}
diff --git a/extensions/baseAdapters/src/main/res/values/ids.xml b/extensions/baseAdapters/src/main/res/values/ids.xml
index 7e76dbe..3383fb5 100644
--- a/extensions/baseAdapters/src/main/res/values/ids.xml
+++ b/extensions/baseAdapters/src/main/res/values/ids.xml
@@ -4,4 +4,6 @@
<item type="id" name="onAttachStateChangeListener"/>
<!-- Used to track TextWatcher for TextView BindingAdapter -->
<item type="id" name="textWatcher"/>
+ <!-- Used to track DatePicker OnDateChanged BindingAdapter -->
+ <item type="id" name="onDateChanged"/>
</resources>
\ No newline at end of file
diff --git a/extensions/build.gradle b/extensions/build.gradle
index 51aad32..0ab14be 100644
--- a/extensions/build.gradle
+++ b/extensions/build.gradle
@@ -13,22 +13,41 @@
* See the License for the specific language governing permissions and
* limitations under the License.
*/
-ext.rootFolder = new File(project.projectDir, "..")
+ext.dataBindingRootFolder = new File(project.projectDir, "..")
buildscript {
- ext.rootFolder = new File(project.projectDir, "..")
+ ext.dataBindingRootFolder = new File(project.projectDir, "..")
apply from: "${project.projectDir}/../propLoader.gradle"
+ apply from: "${project.projectDir}/../supportBundle.gradle"
ext.addRepos(repositories)
- dependencies {
- classpath "com.android.databinding:dataBinder:${config.version}"
- }
}
+def bundleSupportLibTask = tasks['bundleSupportLib']
+
subprojects {
apply plugin: 'maven'
- group = config.group
- version = config.version
+ group = dataBindingConfig.group
+ version = dataBindingConfig.extensionsVersion
+ uploadArchives {
+ repositories {
+ mavenDeployer {
+ repository(url: "file://${dataBindingConfig.internalPrebuiltsRepoDir}")
+ }
+ }
+ }
+ bundleSupportLibTask.dependsOn tasks['uploadArchives']
+
+ def configureOut = tasks.create(name: 'configureOut') << {
+ println("configuring out...")
+ def deployer = tasks['uploadArchives'].repositories.mavenDeployer.repository
+ println "changing ${deployer.url} to ${rootProject.ext.supportRepoOut}"
+ deployer.url = "file://${rootProject.ext.supportRepoOut}"
+ }
+ bundleSupportLibTask.dependsOn configureOut
+ tasks['uploadArchives'].mustRunAfter configureOut
+ tasks['uploadArchives'].mustRunAfter unzipRepo
}
+
task preparePrebuilds() {
}
diff --git a/extensions/gradle/wrapper/gradle-wrapper.properties b/extensions/gradle/wrapper/gradle-wrapper.properties
index e5fd879..50ce379 100644
--- a/extensions/gradle/wrapper/gradle-wrapper.properties
+++ b/extensions/gradle/wrapper/gradle-wrapper.properties
@@ -3,4 +3,4 @@
distributionPath=wrapper/dists
zipStoreBase=GRADLE_USER_HOME
zipStorePath=wrapper/dists
-distributionUrl=https\://services.gradle.org/distributions/gradle-2.2.1-all.zip
+distributionUrl=https\://services.gradle.org/distributions/gradle-2.10-all.zip
diff --git a/extensions/library/build.gradle b/extensions/library/build.gradle
new file mode 100644
index 0000000..339a20f
--- /dev/null
+++ b/extensions/library/build.gradle
@@ -0,0 +1,179 @@
+/*
+ * 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.
+ */
+import com.android.build.api.transform.Format;
+import com.android.build.api.transform.QualifiedContent;
+import com.android.build.api.transform.QualifiedContent.ContentType;
+import com.android.build.api.transform.QualifiedContent.Scope;
+import com.android.build.api.transform.Transform;
+import com.android.build.api.transform.Context;
+import com.android.build.api.transform.TransformInput;
+import com.android.build.api.transform.TransformOutputProvider;
+import com.android.build.api.transform.TransformException;
+import com.android.build.gradle.internal.pipeline.TransformManager;
+// Top-level build file where you can add dataBindingConfiguration options common to all sub-projects/modules.
+
+buildscript {
+ dependencies {
+ classpath "com.android.tools.build:gradle:${dataBindingConfig.androidPluginVersion}"
+ // NOTE: Do not place your application dependencies here; they belong
+ // in the individual module build.gradle files
+ }
+}
+
+apply plugin: 'com.android.library'
+
+android {
+ compileSdkVersion dataBindingConfig.compileSdkVersion
+ buildToolsVersion dataBindingConfig.buildToolsVersion
+
+ defaultConfig {
+ minSdkVersion 7
+ targetSdkVersion 23
+ versionCode 1
+ versionName "1.0"
+ }
+ compileOptions {
+ sourceCompatibility JavaVersion.VERSION_1_6
+ targetCompatibility JavaVersion.VERSION_1_6
+ }
+ 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'
+ }
+}
+
+configurations {
+ jarArchives
+}
+
+
+dependencies {
+ compile 'com.android.support:support-v4:21.0.3'
+ compile "com.android.databinding:baseLibrary:${dataBindingConfig.version}"
+}
+
+//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.
+ }
+ def suffix = name.capitalize()
+
+ def javadocTask = project.tasks.create(name: "javadoc${suffix}", type: Javadoc) {
+ source variant.javaCompile.source
+ classpath = files(variant.javaCompile.classpath.files) + files(
+ "${android.sdkDirectory}/platforms/${android.compileSdkVersion}/android.jar")
+ }
+
+ def javadocJarTask = project.tasks.create(name: "javadocJar${suffix}", type: Jar) {
+ classifier = 'javadoc'
+ from 'build/docs/javadoc'
+ }
+ javadocJarTask.dependsOn javadocTask
+
+ def sourcesJarTask = project.tasks.create(name: "sourceJar${suffix}", type: Jar) {
+ classifier = 'sources'
+ from android.sourceSets.main.java.srcDirs
+ }
+
+ artifacts.add('archives', javadocJarTask);
+ artifacts.add('archives', sourcesJarTask);
+}
+uploadArchives {
+ repositories {
+ mavenDeployer {
+ pom.artifactId = 'library'
+ pom.project {
+ licenses {
+ license {
+ name dataBindingConfig.licenseName
+ url dataBindingConfig.licenseUrl
+ distribution dataBindingConfig.licenseDistribution
+ }
+ }
+ }
+ }
+ }
+}
+
+class ExcludeShimTransform extends Transform {
+ Project project;
+ public ExcludeShimTransform(Project project) {
+ this.project = project;
+ }
+ public Set<ContentType> getInputTypes() {
+ return TransformManager.CONTENT_CLASS;
+ }
+
+ public Set<Scope> getScopes() {
+ def result = new HashSet<Scope>();
+ result.add(Scope.PROJECT);
+ return result;
+ }
+
+ public Set<Scope> getReferencedScopes() {
+ return TransformManager.SCOPE_FULL_LIBRARY;
+ }
+
+ public boolean isIncremental() {
+ return false;
+ }
+
+ public String getName() {
+ return "DataBindingExcludeShimTransform";
+ }
+
+ public void transform(Context context, Collection<TransformInput> inputs,
+ Collection<TransformInput> referencedInputs,
+ TransformOutputProvider outputProvider,
+ boolean isIncremental) throws IOException, TransformException, InterruptedException {
+ inputs.each { transformInput ->
+ transformInput.getDirectoryInputs().each {
+ File outputDir = outputProvider.getContentLocation("data-binding-filtered",
+ it.getContentTypes(), it.getScopes(), Format.DIRECTORY);
+ outputDir.delete();
+ outputDir.mkdirs();
+ FileTree tree = project.fileTree(dir: it.getFile())
+ tree.include '**/*.class'
+ tree.exclude 'android/databinding/DataBindingComponent.*'
+ tree.exclude 'android/databinding/DataBinderMapper.*'
+ tree.copy {
+ into outputDir
+ }
+ }
+ }
+ }
+}
+
+android.registerTransform(new ExcludeShimTransform(project))
+
+task prebuildAar(type : Copy) {
+ dependsOn uploadArchives
+ from "$buildDir/outputs/aar/library-release.aar"
+ into dataBindingConfig.prebuildFolder
+ rename { String fileName ->
+ "databinding-library.aar"
+ }
+}
diff --git a/library/src/androidTest/java/android/databinding/ObservableParcelTest.java b/extensions/library/src/androidTest/java/android/databinding/ObservableParcelTest.java
similarity index 100%
rename from library/src/androidTest/java/android/databinding/ObservableParcelTest.java
rename to extensions/library/src/androidTest/java/android/databinding/ObservableParcelTest.java
diff --git a/library/src/doc/java/com/android/databinding/library/R.java b/extensions/library/src/doc/java/com/android/databinding/library/R.java
similarity index 100%
rename from library/src/doc/java/com/android/databinding/library/R.java
rename to extensions/library/src/doc/java/com/android/databinding/library/R.java
diff --git a/library/src/main/AndroidManifest.xml b/extensions/library/src/main/AndroidManifest.xml
similarity index 100%
rename from library/src/main/AndroidManifest.xml
rename to extensions/library/src/main/AndroidManifest.xml
diff --git a/library/src/main/java/android/databinding/BaseObservable.java b/extensions/library/src/main/java/android/databinding/BaseObservable.java
similarity index 100%
rename from library/src/main/java/android/databinding/BaseObservable.java
rename to extensions/library/src/main/java/android/databinding/BaseObservable.java
diff --git a/library/src/main/java/android/databinding/DataBinderMapper.java b/extensions/library/src/main/java/android/databinding/DataBinderMapper.java
similarity index 100%
rename from library/src/main/java/android/databinding/DataBinderMapper.java
rename to extensions/library/src/main/java/android/databinding/DataBinderMapper.java
diff --git a/library/src/main/java/android/databinding/DataBindingComponent.java b/extensions/library/src/main/java/android/databinding/DataBindingComponent.java
similarity index 100%
rename from library/src/main/java/android/databinding/DataBindingComponent.java
rename to extensions/library/src/main/java/android/databinding/DataBindingComponent.java
diff --git a/library/src/main/java/android/databinding/DataBindingUtil.java b/extensions/library/src/main/java/android/databinding/DataBindingUtil.java
similarity index 93%
rename from library/src/main/java/android/databinding/DataBindingUtil.java
rename to extensions/library/src/main/java/android/databinding/DataBindingUtil.java
index c2e616f..55a6b4e 100644
--- a/library/src/main/java/android/databinding/DataBindingUtil.java
+++ b/extensions/library/src/main/java/android/databinding/DataBindingUtil.java
@@ -115,18 +115,7 @@
final int startChildren = useChildren ? parent.getChildCount() : 0;
final View view = inflater.inflate(layoutId, parent, attachToParent);
if (useChildren) {
- final int endChildren = parent.getChildCount();
- final int childrenAdded = endChildren - startChildren;
- if (childrenAdded == 1) {
- final View childView = parent.getChildAt(endChildren - 1);
- return bind(bindingComponent, childView, layoutId);
- } else {
- final View[] children = new View[childrenAdded];
- for (int i = 0; i < childrenAdded; i++) {
- children[i] = parent.getChildAt(i + startChildren);
- }
- return bind(bindingComponent, children, layoutId);
- }
+ return bindToAddedViews(bindingComponent, parent, startChildren, layoutId);
} else {
return bind(bindingComponent, view, layoutId);
}
@@ -284,13 +273,10 @@
*/
public static <T extends ViewDataBinding> T setContentView(Activity activity, int layoutId,
DataBindingComponent bindingComponent) {
- // Force the content view to exist if it didn't already.
+ activity.setContentView(layoutId);
View decorView = activity.getWindow().getDecorView();
ViewGroup contentView = (ViewGroup) decorView.findViewById(android.R.id.content);
- T binding = inflate(activity.getLayoutInflater(), layoutId, contentView, false,
- bindingComponent);
- activity.setContentView(binding.getRoot(), binding.getRoot().getLayoutParams());
- return binding;
+ return bindToAddedViews(bindingComponent, contentView, 0, layoutId);
}
/**
@@ -303,4 +289,20 @@
public static String convertBrIdToString(int id) {
return sMapper.convertBrIdToString(id);
}
+
+ private static <T extends ViewDataBinding> T bindToAddedViews(DataBindingComponent component,
+ ViewGroup parent, int startChildren, int layoutId) {
+ final int endChildren = parent.getChildCount();
+ final int childrenAdded = endChildren - startChildren;
+ if (childrenAdded == 1) {
+ final View childView = parent.getChildAt(endChildren - 1);
+ return bind(component, childView, layoutId);
+ } else {
+ final View[] children = new View[childrenAdded];
+ for (int i = 0; i < childrenAdded; i++) {
+ children[i] = parent.getChildAt(i + startChildren);
+ }
+ return bind(component, children, layoutId);
+ }
+ }
}
diff --git a/library/src/main/java/android/databinding/ListChangeRegistry.java b/extensions/library/src/main/java/android/databinding/ListChangeRegistry.java
similarity index 98%
rename from library/src/main/java/android/databinding/ListChangeRegistry.java
rename to extensions/library/src/main/java/android/databinding/ListChangeRegistry.java
index e13f867..3b88fe5 100644
--- a/library/src/main/java/android/databinding/ListChangeRegistry.java
+++ b/extensions/library/src/main/java/android/databinding/ListChangeRegistry.java
@@ -25,7 +25,7 @@
CallbackRegistry<ObservableList.OnListChangedCallback, ObservableList,
ListChangeRegistry.ListChanges> {
private static final Pools.SynchronizedPool<ListChanges> sListChanges =
- new Pools.SynchronizedPool<>(10);
+ new Pools.SynchronizedPool<ListChanges>(10);
private static final int ALL = 0;
private static final int CHANGED = 1;
diff --git a/library/src/main/java/android/databinding/MapChangeRegistry.java b/extensions/library/src/main/java/android/databinding/MapChangeRegistry.java
similarity index 100%
rename from library/src/main/java/android/databinding/MapChangeRegistry.java
rename to extensions/library/src/main/java/android/databinding/MapChangeRegistry.java
diff --git a/library/src/main/java/android/databinding/ObservableArrayList.java b/extensions/library/src/main/java/android/databinding/ObservableArrayList.java
similarity index 100%
rename from library/src/main/java/android/databinding/ObservableArrayList.java
rename to extensions/library/src/main/java/android/databinding/ObservableArrayList.java
diff --git a/library/src/main/java/android/databinding/ObservableArrayMap.java b/extensions/library/src/main/java/android/databinding/ObservableArrayMap.java
similarity index 100%
rename from library/src/main/java/android/databinding/ObservableArrayMap.java
rename to extensions/library/src/main/java/android/databinding/ObservableArrayMap.java
diff --git a/library/src/main/java/android/databinding/ObservableBoolean.java b/extensions/library/src/main/java/android/databinding/ObservableBoolean.java
similarity index 100%
rename from library/src/main/java/android/databinding/ObservableBoolean.java
rename to extensions/library/src/main/java/android/databinding/ObservableBoolean.java
diff --git a/library/src/main/java/android/databinding/ObservableByte.java b/extensions/library/src/main/java/android/databinding/ObservableByte.java
similarity index 100%
rename from library/src/main/java/android/databinding/ObservableByte.java
rename to extensions/library/src/main/java/android/databinding/ObservableByte.java
diff --git a/library/src/main/java/android/databinding/ObservableChar.java b/extensions/library/src/main/java/android/databinding/ObservableChar.java
similarity index 100%
rename from library/src/main/java/android/databinding/ObservableChar.java
rename to extensions/library/src/main/java/android/databinding/ObservableChar.java
diff --git a/library/src/main/java/android/databinding/ObservableDouble.java b/extensions/library/src/main/java/android/databinding/ObservableDouble.java
similarity index 100%
rename from library/src/main/java/android/databinding/ObservableDouble.java
rename to extensions/library/src/main/java/android/databinding/ObservableDouble.java
diff --git a/library/src/main/java/android/databinding/ObservableField.java b/extensions/library/src/main/java/android/databinding/ObservableField.java
similarity index 100%
rename from library/src/main/java/android/databinding/ObservableField.java
rename to extensions/library/src/main/java/android/databinding/ObservableField.java
diff --git a/library/src/main/java/android/databinding/ObservableFloat.java b/extensions/library/src/main/java/android/databinding/ObservableFloat.java
similarity index 100%
rename from library/src/main/java/android/databinding/ObservableFloat.java
rename to extensions/library/src/main/java/android/databinding/ObservableFloat.java
diff --git a/library/src/main/java/android/databinding/ObservableInt.java b/extensions/library/src/main/java/android/databinding/ObservableInt.java
similarity index 100%
rename from library/src/main/java/android/databinding/ObservableInt.java
rename to extensions/library/src/main/java/android/databinding/ObservableInt.java
diff --git a/library/src/main/java/android/databinding/ObservableLong.java b/extensions/library/src/main/java/android/databinding/ObservableLong.java
similarity index 100%
rename from library/src/main/java/android/databinding/ObservableLong.java
rename to extensions/library/src/main/java/android/databinding/ObservableLong.java
diff --git a/library/src/main/java/android/databinding/ObservableParcelable.java b/extensions/library/src/main/java/android/databinding/ObservableParcelable.java
similarity index 100%
rename from library/src/main/java/android/databinding/ObservableParcelable.java
rename to extensions/library/src/main/java/android/databinding/ObservableParcelable.java
diff --git a/library/src/main/java/android/databinding/ObservableShort.java b/extensions/library/src/main/java/android/databinding/ObservableShort.java
similarity index 100%
rename from library/src/main/java/android/databinding/ObservableShort.java
rename to extensions/library/src/main/java/android/databinding/ObservableShort.java
diff --git a/library/src/main/java/android/databinding/OnRebindCallback.java b/extensions/library/src/main/java/android/databinding/OnRebindCallback.java
similarity index 100%
rename from library/src/main/java/android/databinding/OnRebindCallback.java
rename to extensions/library/src/main/java/android/databinding/OnRebindCallback.java
diff --git a/library/src/main/java/android/databinding/PropertyChangeRegistry.java b/extensions/library/src/main/java/android/databinding/PropertyChangeRegistry.java
similarity index 100%
rename from library/src/main/java/android/databinding/PropertyChangeRegistry.java
rename to extensions/library/src/main/java/android/databinding/PropertyChangeRegistry.java
diff --git a/library/src/main/java/android/databinding/ViewDataBinding.java b/extensions/library/src/main/java/android/databinding/ViewDataBinding.java
similarity index 76%
rename from library/src/main/java/android/databinding/ViewDataBinding.java
rename to extensions/library/src/main/java/android/databinding/ViewDataBinding.java
index b23d7b8..5e76023 100644
--- a/library/src/main/java/android/databinding/ViewDataBinding.java
+++ b/extensions/library/src/main/java/android/databinding/ViewDataBinding.java
@@ -16,23 +16,31 @@
package android.databinding;
-import com.android.databinding.library.R;
-
import android.annotation.TargetApi;
+import android.content.res.ColorStateList;
import android.databinding.CallbackRegistry.NotifierCallback;
+import android.graphics.drawable.Drawable;
import android.os.Build.VERSION;
import android.os.Build.VERSION_CODES;
import android.os.Handler;
import android.os.Looper;
import android.text.TextUtils;
+import android.util.LongSparseArray;
+import android.util.SparseArray;
+import android.util.SparseBooleanArray;
import android.util.SparseIntArray;
+import android.util.SparseLongArray;
import android.view.Choreographer;
import android.view.LayoutInflater;
import android.view.View;
import android.view.View.OnAttachStateChangeListener;
import android.view.ViewGroup;
+import com.android.databinding.library.R;
+
import java.lang.ref.WeakReference;
+import java.util.List;
+import java.util.Map;
/**
* Base class for generated data binding classes. If possible, the generated binding should
@@ -40,7 +48,7 @@
* binding is unknown, {@link DataBindingUtil#bind(View)} or
* {@link DataBindingUtil#inflate(LayoutInflater, int, ViewGroup, boolean)} should be used.
*/
-public abstract class ViewDataBinding {
+public abstract class ViewDataBinding extends BaseObservable {
/**
* Instead of directly accessing Build.VERSION.SDK_INT, generated code uses this value so that
@@ -292,8 +300,8 @@
* @param variableId the BR id of the variable to be set. For example, if the variable is
* <code>x</code>, then variableId will be <code>BR.x</code>.
* @param value The new value of the variable to be set.
- * @return <code>true</code> if the variable exists in the binding or <code>false</code>
- * otherwise.
+ * @return <code>true</code> if the variable is declared or used in the binding or
+ * <code>false</code> otherwise.
*/
public abstract boolean setVariable(int variableId, Object value);
@@ -561,6 +569,324 @@
return bindings;
}
+ /** @hide */
+ protected int getColorFromResource(int resourceId) {
+ if (VERSION.SDK_INT >= VERSION_CODES.M) {
+ return getRoot().getContext().getColor(resourceId);
+ } else {
+ return getRoot().getResources().getColor(resourceId);
+ }
+ }
+
+ /** @hide */
+ protected ColorStateList getColorStateListFromResource(int resourceId) {
+ if (VERSION.SDK_INT >= VERSION_CODES.M) {
+ return getRoot().getContext().getColorStateList(resourceId);
+ } else {
+ return getRoot().getResources().getColorStateList(resourceId);
+ }
+ }
+
+ /** @hide */
+ protected Drawable getDrawableFromResource(int resourceId) {
+ if (VERSION.SDK_INT >= VERSION_CODES.LOLLIPOP) {
+ return getRoot().getContext().getDrawable(resourceId);
+ } else {
+ return getRoot().getResources().getDrawable(resourceId);
+ }
+ }
+
+ /** @hide */
+ protected static <T> T getFromArray(T[] arr, int index) {
+ if (arr == null || index < 0 || index >= arr.length) {
+ return null;
+ }
+ return arr[index];
+ }
+
+ /** @hide */
+ protected static <T> void setTo(T[] arr, int index, T value) {
+ if (arr == null || index < 0 || index >= arr.length) {
+ return;
+ }
+ arr[index] = value;
+ }
+
+ /** @hide */
+ protected static boolean getFromArray(boolean[] arr, int index) {
+ if (arr == null || index < 0 || index >= arr.length) {
+ return false;
+ }
+ return arr[index];
+ }
+
+ /** @hide */
+ protected static void setTo(boolean[] arr, int index, boolean value) {
+ if (arr == null || index < 0 || index >= arr.length) {
+ return;
+ }
+ arr[index] = value;
+ }
+
+ /** @hide */
+ protected static byte getFromArray(byte[] arr, int index) {
+ if (arr == null || index < 0 || index >= arr.length) {
+ return 0;
+ }
+ return arr[index];
+ }
+
+ /** @hide */
+ protected static void setTo(byte[] arr, int index, byte value) {
+ if (arr == null || index < 0 || index >= arr.length) {
+ return;
+ }
+ arr[index] = value;
+ }
+
+ /** @hide */
+ protected static short getFromArray(short[] arr, int index) {
+ if (arr == null || index < 0 || index >= arr.length) {
+ return 0;
+ }
+ return arr[index];
+ }
+
+ /** @hide */
+ protected static void setTo(short[] arr, int index, short value) {
+ if (arr == null || index < 0 || index >= arr.length) {
+ return;
+ }
+ arr[index] = value;
+ }
+
+ /** @hide */
+ protected static char getFromArray(char[] arr, int index) {
+ if (arr == null || index < 0 || index >= arr.length) {
+ return 0;
+ }
+ return arr[index];
+ }
+
+ /** @hide */
+ protected static void setTo(char[] arr, int index, char value) {
+ if (arr == null || index < 0 || index >= arr.length) {
+ return;
+ }
+ arr[index] = value;
+ }
+
+ /** @hide */
+ protected static int getFromArray(int[] arr, int index) {
+ if (arr == null || index < 0 || index >= arr.length) {
+ return 0;
+ }
+ return arr[index];
+ }
+
+ /** @hide */
+ protected static void setTo(int[] arr, int index, int value) {
+ if (arr == null || index < 0 || index >= arr.length) {
+ return;
+ }
+ arr[index] = value;
+ }
+
+ /** @hide */
+ protected static long getFromArray(long[] arr, int index) {
+ if (arr == null || index < 0 || index >= arr.length) {
+ return 0;
+ }
+ return arr[index];
+ }
+
+ /** @hide */
+ protected static void setTo(long[] arr, int index, long value) {
+ if (arr == null || index < 0 || index >= arr.length) {
+ return;
+ }
+ arr[index] = value;
+ }
+
+ /** @hide */
+ protected static float getFromArray(float[] arr, int index) {
+ if (arr == null || index < 0 || index >= arr.length) {
+ return 0;
+ }
+ return arr[index];
+ }
+
+ /** @hide */
+ protected static void setTo(float[] arr, int index, float value) {
+ if (arr == null || index < 0 || index >= arr.length) {
+ return;
+ }
+ arr[index] = value;
+ }
+
+ /** @hide */
+ protected static double getFromArray(double[] arr, int index) {
+ if (arr == null || index < 0 || index >= arr.length) {
+ return 0;
+ }
+ return arr[index];
+ }
+
+ /** @hide */
+ protected static void setTo(double[] arr, int index, double value) {
+ if (arr == null || index < 0 || index >= arr.length) {
+ return;
+ }
+ arr[index] = value;
+ }
+
+ /** @hide */
+ protected static <T> T getFromList(List<T> list, int index) {
+ if (list == null || index < 0 || index >= list.size()) {
+ return null;
+ }
+ return list.get(index);
+ }
+
+ /** @hide */
+ protected static <T> void setTo(List<T> list, int index, T value) {
+ if (list == null || index < 0 || index >= list.size()) {
+ return;
+ }
+ list.set(index, value);
+ }
+
+ /** @hide */
+ protected static <T> T getFromList(SparseArray<T> list, int index) {
+ if (list == null || index < 0) {
+ return null;
+ }
+ return list.get(index);
+ }
+
+ /** @hide */
+ protected static <T> void setTo(SparseArray<T> list, int index, T value) {
+ if (list == null || index < 0 || index >= list.size()) {
+ return;
+ }
+ list.put(index, value);
+ }
+
+ /** @hide */
+ @TargetApi(VERSION_CODES.JELLY_BEAN)
+ protected static <T> T getFromList(LongSparseArray<T> list, int index) {
+ if (list == null || index < 0) {
+ return null;
+ }
+ return list.get(index);
+ }
+
+ /** @hide */
+ @TargetApi(VERSION_CODES.JELLY_BEAN)
+ protected static <T> void setTo(LongSparseArray<T> list, int index, T value) {
+ if (list == null || index < 0 || index >= list.size()) {
+ return;
+ }
+ list.put(index, value);
+ }
+
+ /** @hide */
+ protected static <T> T getFromList(android.support.v4.util.LongSparseArray<T> list, int index) {
+ if (list == null || index < 0) {
+ return null;
+ }
+ return list.get(index);
+ }
+
+ /** @hide */
+ protected static <T> void setTo(android.support.v4.util.LongSparseArray<T> list, int index,
+ T value) {
+ if (list == null || index < 0 || index >= list.size()) {
+ return;
+ }
+ list.put(index, value);
+ }
+
+ /** @hide */
+ protected static boolean getFromList(SparseBooleanArray list, int index) {
+ if (list == null || index < 0) {
+ return false;
+ }
+ return list.get(index);
+ }
+
+ /** @hide */
+ protected static void setTo(SparseBooleanArray list, int index, boolean value) {
+ if (list == null || index < 0 || index >= list.size()) {
+ return;
+ }
+ list.put(index, value);
+ }
+
+ /** @hide */
+ protected static int getFromList(SparseIntArray list, int index) {
+ if (list == null || index < 0) {
+ return 0;
+ }
+ return list.get(index);
+ }
+
+ /** @hide */
+ protected static void setTo(SparseIntArray list, int index, int value) {
+ if (list == null || index < 0 || index >= list.size()) {
+ return;
+ }
+ list.put(index, value);
+ }
+
+ /** @hide */
+ @TargetApi(VERSION_CODES.JELLY_BEAN_MR2)
+ protected static long getFromList(SparseLongArray list, int index) {
+ if (list == null || index < 0) {
+ return 0;
+ }
+ return list.get(index);
+ }
+
+ /** @hide */
+ @TargetApi(VERSION_CODES.JELLY_BEAN_MR2)
+ protected static void setTo(SparseLongArray list, int index, long value) {
+ if (list == null || index < 0 || index >= list.size()) {
+ return;
+ }
+ list.put(index, value);
+ }
+
+ /** @hide */
+ protected static <K, T> T getFrom(Map<K, T> map, K key) {
+ if (map == null) {
+ return null;
+ }
+ return map.get(key);
+ }
+
+ /** @hide */
+ protected static <K, T> void setTo(Map<K, T> map, K key, T value) {
+ if (map == null) {
+ return;
+ }
+ map.put(key, value);
+ }
+
+ /** @hide */
+ protected static void setBindingInverseListener(ViewDataBinding binder,
+ InverseBindingListener oldListener, PropertyChangedInverseListener listener) {
+ if (oldListener != listener) {
+ if (oldListener != null) {
+ binder.removeOnPropertyChangedCallback(
+ (PropertyChangedInverseListener) oldListener);
+ }
+ if (listener != null) {
+ binder.addOnPropertyChangedCallback(listener);
+ }
+ }
+ }
+
/**
* Walks the view hierarchy under roots and pulls out tagged Views, includes, and views with
* IDs into an Object[] that is returned. This is used to walk the view hierarchy once to find
@@ -945,4 +1271,26 @@
this.layoutIds[index] = layoutIds;
}
}
+
+ /**
+ * This class is used by generated subclasses of {@link ViewDataBinding} to listen for
+ * changes on variables of Bindings. This is important for two-way data binding on variables
+ * in included Bindings.
+ * @hide
+ */
+ protected static abstract class PropertyChangedInverseListener
+ extends Observable.OnPropertyChangedCallback implements InverseBindingListener {
+ final int mPropertyId;
+
+ public PropertyChangedInverseListener(int propertyId) {
+ mPropertyId = propertyId;
+ }
+
+ @Override
+ public void onPropertyChanged(Observable sender, int propertyId) {
+ if (propertyId == mPropertyId || propertyId == 0) {
+ onChange();
+ }
+ }
+ }
}
diff --git a/library/src/main/java/android/databinding/ViewStubProxy.java b/extensions/library/src/main/java/android/databinding/ViewStubProxy.java
similarity index 100%
rename from library/src/main/java/android/databinding/ViewStubProxy.java
rename to extensions/library/src/main/java/android/databinding/ViewStubProxy.java
diff --git a/library/src/main/res/values/ids.xml b/extensions/library/src/main/res/values/ids.xml
similarity index 100%
rename from library/src/main/res/values/ids.xml
rename to extensions/library/src/main/res/values/ids.xml
diff --git a/extensions/publishExtensions.sh b/extensions/publishExtensions.sh
new file mode 100755
index 0000000..32c032e
--- /dev/null
+++ b/extensions/publishExtensions.sh
@@ -0,0 +1,2 @@
+ #!/bin/bash
+ ./gradlew l:upArch --configure-on-demand && ./gradlew bA:upArch --configure-on-demand
diff --git a/extensions/settings.gradle b/extensions/settings.gradle
index 80ebcc8..da0d959 100644
--- a/extensions/settings.gradle
+++ b/extensions/settings.gradle
@@ -17,4 +17,5 @@
/**
* These are projects that requires a compiled version of data binding.
*/
+include ':library'
include ':baseAdapters'
diff --git a/gradle/wrapper/gradle-wrapper.properties b/gradle/wrapper/gradle-wrapper.properties
index e5fd879..50ce379 100644
--- a/gradle/wrapper/gradle-wrapper.properties
+++ b/gradle/wrapper/gradle-wrapper.properties
@@ -3,4 +3,4 @@
distributionPath=wrapper/dists
zipStoreBase=GRADLE_USER_HOME
zipStorePath=wrapper/dists
-distributionUrl=https\://services.gradle.org/distributions/gradle-2.2.1-all.zip
+distributionUrl=https\://services.gradle.org/distributions/gradle-2.10-all.zip
diff --git a/gradlePlugin/build.gradle b/gradlePlugin/build.gradle
deleted file mode 100644
index 203105a..0000000
--- a/gradlePlugin/build.gradle
+++ /dev/null
@@ -1,58 +0,0 @@
-/*
- * 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: 'com.android.databinding.bintray'
-sourceCompatibility = config.javaTargetCompatibility
-targetCompatibility = config.javaSourceCompatibility
-
-
-tasks.create(name : 'copyBuildVersion') << {
- def buildVersionFile = new File(sourceSets.main.output.resourcesDir,"data_binding_build_info")
- buildVersionFile.delete()
- println "writing build version file"
- buildVersionFile << project.version
-}
-
-tasks['uploadArchives'].dependsOn('copyBuildVersion')
-tasks['copyBuildVersion'].dependsOn('processResources')
-
-dependencies {
- compile "com.android.tools.build:gradle:${config.androidPluginVersion}"
- compile gradleApi()
- compile project(":compilerCommon")
-}
-
-compileJava {
- options.compilerArgs = ["-proc:none"]
-}
-
-uploadArchives {
- repositories {
- mavenDeployer {
- pom.artifactId = 'dataBinder'
- pom.project {
- licenses {
- license {
- name config.licenseName
- url config.licenseUrl
- distribution config.licenseDistribution
- }
- }
- }
- }
- }
-}
\ No newline at end of file
diff --git a/gradlePlugin/gradle/wrapper/gradle-wrapper.jar b/gradlePlugin/gradle/wrapper/gradle-wrapper.jar
deleted file mode 100644
index 3d0dee6..0000000
--- a/gradlePlugin/gradle/wrapper/gradle-wrapper.jar
+++ /dev/null
Binary files differ
diff --git a/gradlePlugin/gradle/wrapper/gradle-wrapper.properties b/gradlePlugin/gradle/wrapper/gradle-wrapper.properties
deleted file mode 100644
index 8b80a06..0000000
--- a/gradlePlugin/gradle/wrapper/gradle-wrapper.properties
+++ /dev/null
@@ -1,6 +0,0 @@
-#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/gradlePlugin/gradlew b/gradlePlugin/gradlew
deleted file mode 100755
index 91a7e26..0000000
--- a/gradlePlugin/gradlew
+++ /dev/null
@@ -1,164 +0,0 @@
-#!/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/gradlePlugin/gradlew.bat b/gradlePlugin/gradlew.bat
deleted file mode 100644
index aec9973..0000000
--- a/gradlePlugin/gradlew.bat
+++ /dev/null
@@ -1,90 +0,0 @@
-@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/gradlePlugin/src/main/java/android/databinding/tool/DataBinderPlugin.java b/gradlePlugin/src/main/java/android/databinding/tool/DataBinderPlugin.java
deleted file mode 100644
index a116dc1..0000000
--- a/gradlePlugin/src/main/java/android/databinding/tool/DataBinderPlugin.java
+++ /dev/null
@@ -1,381 +0,0 @@
-/*
- * Copyright (C) 2015 The Android Open Source Project
- *
- * Licensed under the Apache License, Version 2.0 (the "License");
- * you may not use this file except in compliance with the License.
- * You may obtain a copy of the License at
- *
- * http://www.apache.org/licenses/LICENSE-2.0
- *
- * Unless required by applicable law or agreed to in writing, software
- * distributed under the License is distributed on an "AS IS" BASIS,
- * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
- * See the License for the specific language governing permissions and
- * limitations under the License.
- */
-package android.databinding.tool;
-
-import com.google.common.base.Preconditions;
-
-import com.android.build.gradle.AppExtension;
-import com.android.build.gradle.BaseExtension;
-import com.android.build.gradle.LibraryExtension;
-import com.android.build.gradle.api.ApplicationVariant;
-import com.android.build.gradle.api.LibraryVariant;
-import com.android.build.gradle.api.TestVariant;
-import com.android.build.gradle.internal.api.ApplicationVariantImpl;
-import com.android.build.gradle.internal.api.LibraryVariantImpl;
-import com.android.build.gradle.internal.api.TestVariantImpl;
-import com.android.build.gradle.internal.core.GradleVariantConfiguration;
-import com.android.build.gradle.internal.variant.ApplicationVariantData;
-import com.android.build.gradle.internal.variant.BaseVariantData;
-import com.android.build.gradle.internal.variant.LibraryVariantData;
-import com.android.build.gradle.internal.variant.TestVariantData;
-import com.android.build.gradle.tasks.ProcessAndroidResources;
-import com.android.builder.model.ApiVersion;
-
-import org.apache.commons.io.IOUtils;
-import org.apache.commons.lang3.StringUtils;
-import org.apache.commons.lang3.exception.ExceptionUtils;
-import org.gradle.api.Action;
-import org.gradle.api.Plugin;
-import org.gradle.api.Project;
-import org.gradle.api.Task;
-import org.gradle.api.logging.LogLevel;
-import org.gradle.api.logging.Logger;
-import org.gradle.api.plugins.ExtraPropertiesExtension;
-import org.gradle.api.tasks.bundling.Jar;
-import org.gradle.api.tasks.compile.AbstractCompile;
-
-import android.databinding.tool.processing.ScopedException;
-import android.databinding.tool.util.L;
-import android.databinding.tool.writer.JavaFileWriter;
-
-import java.io.File;
-import java.io.FileOutputStream;
-import java.io.IOException;
-import java.io.InputStream;
-import java.lang.reflect.Field;
-import java.util.Arrays;
-import java.util.List;
-
-import javax.tools.Diagnostic;
-import javax.xml.bind.JAXBException;
-
-public class DataBinderPlugin implements Plugin<Project> {
-
- private static final String INVOKED_FROM_IDE_PROPERTY = "android.injected.invoked.from.ide";
- private static final String PRINT_ENCODED_ERRORS_PROPERTY
- = "android.databinding.injected.print.encoded.errors";
- private Logger logger;
- private boolean printEncodedErrors = false;
-
- class GradleFileWriter extends JavaFileWriter {
-
- private final String outputBase;
-
- public GradleFileWriter(String outputBase) {
- this.outputBase = outputBase;
- }
-
- @Override
- public void writeToFile(String canonicalName, String contents) {
- String asPath = canonicalName.replace('.', '/');
- File f = new File(outputBase + "/" + asPath + ".java");
- logD("Asked to write to " + canonicalName + ". outputting to:" +
- f.getAbsolutePath());
- //noinspection ResultOfMethodCallIgnored
- f.getParentFile().mkdirs();
- FileOutputStream fos = null;
- try {
- fos = new FileOutputStream(f);
- IOUtils.write(contents, fos);
- } catch (IOException e) {
- logE(e, "cannot write file " + f.getAbsolutePath());
- } finally {
- IOUtils.closeQuietly(fos);
- }
- }
- }
-
- private boolean safeGetBooleanProperty(Project project, String property) {
- boolean hasProperty = project.hasProperty(property);
- if (!hasProperty) {
- return false;
- }
- try {
- if (Boolean.parseBoolean(String.valueOf(project.getProperties().get(property)))) {
- return true;
- }
- } catch (Throwable t) {
- L.w("unable to read property %s", project);
- }
- return false;
- }
-
- private boolean resolvePrintEncodedErrors(Project project) {
- return safeGetBooleanProperty(project, INVOKED_FROM_IDE_PROPERTY) ||
- safeGetBooleanProperty(project, PRINT_ENCODED_ERRORS_PROPERTY);
- }
-
- @Override
- public void apply(Project project) {
- if (project == null) {
- return;
- }
- setupLogger(project);
-
- String myVersion = readMyVersion();
- logD("data binding plugin version is %s", myVersion);
- if (StringUtils.isEmpty(myVersion)) {
- throw new IllegalStateException("cannot read version of the plugin :/");
- }
- printEncodedErrors = resolvePrintEncodedErrors(project);
- ScopedException.encodeOutput(printEncodedErrors);
- project.getDependencies().add("compile", "com.android.databinding:library:" + myVersion);
- boolean addAdapters = true;
- if (project.hasProperty("ext")) {
- Object ext = project.getProperties().get("ext");
- if (ext instanceof ExtraPropertiesExtension) {
- ExtraPropertiesExtension propExt = (ExtraPropertiesExtension) ext;
- if (propExt.has("addDataBindingAdapters")) {
- addAdapters = Boolean.valueOf(
- String.valueOf(propExt.get("addDataBindingAdapters")));
- }
- }
- }
- if (addAdapters) {
- project.getDependencies()
- .add("compile", "com.android.databinding:adapters:" + myVersion);
- }
- project.getDependencies().add("provided", "com.android.databinding:compiler:" + myVersion);
- project.afterEvaluate(new Action<Project>() {
- @Override
- public void execute(Project project) {
- try {
- createXmlProcessor(project);
- } catch (Throwable t) {
- logE(t, "failed to setup data binding");
- }
- }
- });
- }
-
- private void setupLogger(Project project) {
- logger = project.getLogger();
- L.setClient(new L.Client() {
- @Override
- public void printMessage(Diagnostic.Kind kind, String message) {
- if (kind == Diagnostic.Kind.ERROR) {
- logE(null, message);
- } else {
- logD(message);
- }
- }
- });
- }
-
- String readMyVersion() {
- try {
- InputStream stream = getClass().getResourceAsStream("/data_binding_build_info");
- try {
- return IOUtils.toString(stream, "utf-8").trim();
- } finally {
- IOUtils.closeQuietly(stream);
- }
- } catch (IOException exception) {
- logE(exception, "Cannot read data binding version");
- }
- return null;
- }
-
- private void createXmlProcessor(Project project)
- throws NoSuchFieldException, IllegalAccessException {
- L.d("creating xml processor for " + project);
- Object androidExt = project.getExtensions().getByName("android");
- if (!(androidExt instanceof BaseExtension)) {
- return;
- }
- if (androidExt instanceof AppExtension) {
- createXmlProcessorForApp(project, (AppExtension) androidExt);
- } else if (androidExt instanceof LibraryExtension) {
- createXmlProcessorForLibrary(project, (LibraryExtension) androidExt);
- } else {
- logE(new UnsupportedOperationException("cannot understand android ext"),
- "unsupported android extension. What is it? %s", androidExt);
- }
- }
-
- private void createXmlProcessorForLibrary(Project project, LibraryExtension lib)
- throws NoSuchFieldException, IllegalAccessException {
- File sdkDir = lib.getSdkDirectory();
- L.d("create xml processor for " + lib);
- for (TestVariant variant : lib.getTestVariants()) {
- logD("test variant %s. dir name %s", variant, variant.getDirName());
- BaseVariantData variantData = getVariantData(variant);
- attachXmlProcessor(project, variantData, sdkDir, false);//tests extend apk variant
- }
- for (LibraryVariant variant : lib.getLibraryVariants()) {
- logD("library variant %s. dir name %s", variant, variant.getDirName());
- BaseVariantData variantData = getVariantData(variant);
- attachXmlProcessor(project, variantData, sdkDir, true);
- }
- }
-
- private void createXmlProcessorForApp(Project project, AppExtension appExt)
- throws NoSuchFieldException, IllegalAccessException {
- L.d("create xml processor for " + appExt);
- File sdkDir = appExt.getSdkDirectory();
- for (TestVariant testVariant : appExt.getTestVariants()) {
- TestVariantData variantData = getVariantData(testVariant);
- attachXmlProcessor(project, variantData, sdkDir, false);
- }
- for (ApplicationVariant appVariant : appExt.getApplicationVariants()) {
- ApplicationVariantData variantData = getVariantData(appVariant);
- attachXmlProcessor(project, variantData, sdkDir, false);
- }
- }
-
- private LibraryVariantData getVariantData(LibraryVariant variant)
- throws NoSuchFieldException, IllegalAccessException {
- Field field = LibraryVariantImpl.class.getDeclaredField("variantData");
- field.setAccessible(true);
- return (LibraryVariantData) field.get(variant);
- }
-
- private TestVariantData getVariantData(TestVariant variant)
- throws IllegalAccessException, NoSuchFieldException {
- Field field = TestVariantImpl.class.getDeclaredField("variantData");
- field.setAccessible(true);
- return (TestVariantData) field.get(variant);
- }
-
- private ApplicationVariantData getVariantData(ApplicationVariant variant)
- throws IllegalAccessException, NoSuchFieldException {
- Field field = ApplicationVariantImpl.class.getDeclaredField("variantData");
- field.setAccessible(true);
- return (ApplicationVariantData) field.get(variant);
- }
-
- private void attachXmlProcessor(Project project, final BaseVariantData variantData,
- final File sdkDir,
- final Boolean isLibrary) {
- final GradleVariantConfiguration configuration = variantData.getVariantConfiguration();
- final ApiVersion minSdkVersion = configuration.getMinSdkVersion();
- ProcessAndroidResources generateRTask = variantData.generateRClassTask;
- final String packageName = generateRTask.getPackageForR();
- String fullName = configuration.getFullName();
- List<File> resourceFolders = Arrays.asList(variantData.mergeResourcesTask.getOutputDir());
-
- final File codeGenTargetFolder = new File(project.getBuildDir() + "/data-binding-info/" +
- configuration.getDirName());
- String writerOutBase = codeGenTargetFolder.getAbsolutePath();
- JavaFileWriter fileWriter = new GradleFileWriter(writerOutBase);
- final LayoutXmlProcessor xmlProcessor = new LayoutXmlProcessor(packageName, resourceFolders,
- fileWriter, minSdkVersion.getApiLevel(), isLibrary);
- final ProcessAndroidResources processResTask = generateRTask;
- final File xmlOutDir = new File(project.getBuildDir() + "/layout-info/" +
- configuration.getDirName());
- final File generatedClassListOut = isLibrary ? new File(xmlOutDir, "_generated.txt") : null;
- logD("xml output for %s is %s", variantData, xmlOutDir);
- String layoutTaskName = "dataBindingLayouts" + StringUtils
- .capitalize(processResTask.getName());
- String infoClassTaskName = "dataBindingInfoClass" + StringUtils
- .capitalize(processResTask.getName());
-
- final DataBindingProcessLayoutsTask[] processLayoutsTasks
- = new DataBindingProcessLayoutsTask[1];
- project.getTasks().create(layoutTaskName,
- DataBindingProcessLayoutsTask.class,
- new Action<DataBindingProcessLayoutsTask>() {
- @Override
- public void execute(final DataBindingProcessLayoutsTask task) {
- processLayoutsTasks[0] = task;
- task.setXmlProcessor(xmlProcessor);
- task.setSdkDir(sdkDir);
- task.setXmlOutFolder(xmlOutDir);
- task.setMinSdk(minSdkVersion.getApiLevel());
-
- logD("TASK adding dependency on %s for %s", task, processResTask);
- processResTask.dependsOn(task);
- processResTask.getInputs().dir(xmlOutDir);
- for (Object dep : processResTask.getDependsOn()) {
- if (dep == task) {
- continue;
- }
- logD("adding dependency on %s for %s", dep, task);
- task.dependsOn(dep);
- }
- processResTask.doLast(new Action<Task>() {
- @Override
- public void execute(Task unused) {
- try {
- task.writeLayoutXmls();
- } catch (JAXBException e) {
- // gradle sometimes fails to resolve JAXBException.
- // We get stack trace manually to ensure we have the log
- logE(e, "cannot write layout xmls %s",
- ExceptionUtils.getStackTrace(e));
- }
- }
- });
- }
- });
- final DataBindingProcessLayoutsTask processLayoutsTask = processLayoutsTasks[0];
- project.getTasks().create(infoClassTaskName,
- DataBindingExportInfoTask.class,
- new Action<DataBindingExportInfoTask>() {
-
- @Override
- public void execute(DataBindingExportInfoTask task) {
- task.dependsOn(processLayoutsTask);
- task.dependsOn(processResTask);
- task.setXmlProcessor(xmlProcessor);
- task.setSdkDir(sdkDir);
- task.setXmlOutFolder(xmlOutDir);
- task.setExportClassListTo(generatedClassListOut);
- task.setPrintEncodedErrors(printEncodedErrors);
- task.setEnableDebugLogs(logger.isEnabled(LogLevel.DEBUG));
-
- variantData.registerJavaGeneratingTask(task, codeGenTargetFolder);
- }
- });
- String packageJarTaskName = "package" + StringUtils.capitalize(fullName) + "Jar";
- final Task packageTask = project.getTasks().findByName(packageJarTaskName);
- if (packageTask instanceof Jar) {
- String removeGeneratedTaskName = "dataBindingExcludeGeneratedFrom" +
- StringUtils.capitalize(packageTask.getName());
- if (project.getTasks().findByName(removeGeneratedTaskName) == null) {
- final AbstractCompile javaCompileTask = variantData.javacTask;
- Preconditions.checkNotNull(javaCompileTask);
-
- project.getTasks().create(removeGeneratedTaskName,
- DataBindingExcludeGeneratedTask.class,
- new Action<DataBindingExcludeGeneratedTask>() {
- @Override
- public void execute(DataBindingExcludeGeneratedTask task) {
- packageTask.dependsOn(task);
- task.dependsOn(javaCompileTask);
- task.setAppPackage(packageName);
- task.setInfoClassQualifiedName(xmlProcessor.getInfoClassFullName());
- task.setPackageTask((Jar) packageTask);
- task.setLibrary(isLibrary);
- task.setGeneratedClassListFile(generatedClassListOut);
- }
- });
- }
- }
- }
-
- private void logD(String s, Object... args) {
- logger.info(formatLog(s, args));
- }
-
- private void logE(Throwable t, String s, Object... args) {
- logger.error(formatLog(s, args), t);
- }
-
- private String formatLog(String s, Object... args) {
- return "[data binding plugin]: " + String.format(s, args);
- }
-}
diff --git a/gradlePlugin/src/main/java/android/databinding/tool/DataBindingExcludeGeneratedTask.java b/gradlePlugin/src/main/java/android/databinding/tool/DataBindingExcludeGeneratedTask.java
index a58d97a..f311513 100644
--- a/gradlePlugin/src/main/java/android/databinding/tool/DataBindingExcludeGeneratedTask.java
+++ b/gradlePlugin/src/main/java/android/databinding/tool/DataBindingExcludeGeneratedTask.java
@@ -88,6 +88,7 @@
exclude(EXCLUDE_PATTERN);
if (isLibrary) {
exclude(appPkgAsClass + "/BR.*");
+ exclude("android/databinding/DynamicUtil.class");
List<String> generatedClasses = readGeneratedClasses();
for (String klass : generatedClasses) {
exclude(klass.replace('.', '/') + ".class");
diff --git a/gradlePlugin/src/main/java/android/databinding/tool/DataBindingExportInfoTask.java b/gradlePlugin/src/main/java/android/databinding/tool/DataBindingExportInfoTask.java
deleted file mode 100644
index 768fe68..0000000
--- a/gradlePlugin/src/main/java/android/databinding/tool/DataBindingExportInfoTask.java
+++ /dev/null
@@ -1,89 +0,0 @@
-/*
- * Copyright (C) 2015 The Android Open Source Project
- *
- * Licensed under the Apache License, Version 2.0 (the "License");
- * you may not use this file except in compliance with the License.
- * You may obtain a copy of the License at
- *
- * http://www.apache.org/licenses/LICENSE-2.0
- *
- * Unless required by applicable law or agreed to in writing, software
- * distributed under the License is distributed on an "AS IS" BASIS,
- * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
- * See the License for the specific language governing permissions and
- * limitations under the License.
- */
-package android.databinding.tool;
-
-import org.gradle.api.DefaultTask;
-import org.gradle.api.tasks.TaskAction;
-
-import android.databinding.tool.processing.Scope;
-
-import java.io.File;
-
-/**
- * Task to pass environment info to javac
- */
-public class DataBindingExportInfoTask extends DefaultTask {
- private LayoutXmlProcessor xmlProcessor;
- private File sdkDir;
- private File xmlOutFolder;
- private File exportClassListTo;
- private boolean printEncodedErrors;
- private boolean enableDebugLogs = false;
- @TaskAction
- public void exportInfo() {
- xmlProcessor.writeInfoClass(sdkDir, xmlOutFolder, exportClassListTo, enableDebugLogs,
- printEncodedErrors);
- Scope.assertNoError();
- }
-
- public LayoutXmlProcessor getXmlProcessor() {
- return xmlProcessor;
- }
-
- public void setXmlProcessor(LayoutXmlProcessor xmlProcessor) {
- this.xmlProcessor = xmlProcessor;
- }
-
- public File getSdkDir() {
- return sdkDir;
- }
-
- public void setSdkDir(File sdkDir) {
- this.sdkDir = sdkDir;
- }
-
- public File getXmlOutFolder() {
- return xmlOutFolder;
- }
-
- public void setXmlOutFolder(File xmlOutFolder) {
- this.xmlOutFolder = xmlOutFolder;
- }
-
- public File getExportClassListTo() {
- return exportClassListTo;
- }
-
- public void setExportClassListTo(File exportClassListTo) {
- this.exportClassListTo = exportClassListTo;
- }
-
- public boolean isPrintEncodedErrors() {
- return printEncodedErrors;
- }
-
- public void setPrintEncodedErrors(boolean printEncodedErrors) {
- this.printEncodedErrors = printEncodedErrors;
- }
-
- public boolean isEnableDebugLogs() {
- return enableDebugLogs;
- }
-
- public void setEnableDebugLogs(boolean enableDebugLogs) {
- this.enableDebugLogs = enableDebugLogs;
- }
-}
\ No newline at end of file
diff --git a/gradlePlugin/src/main/java/android/databinding/tool/DataBindingProcessLayoutsTask.java b/gradlePlugin/src/main/java/android/databinding/tool/DataBindingProcessLayoutsTask.java
deleted file mode 100644
index 320d4dc..0000000
--- a/gradlePlugin/src/main/java/android/databinding/tool/DataBindingProcessLayoutsTask.java
+++ /dev/null
@@ -1,89 +0,0 @@
-/*
- * Copyright (C) 2015 The Android Open Source Project
- *
- * Licensed under the Apache License, Version 2.0 (the "License");
- * you may not use this file except in compliance with the License.
- * You may obtain a copy of the License at
- *
- * http://www.apache.org/licenses/LICENSE-2.0
- *
- * Unless required by applicable law or agreed to in writing, software
- * distributed under the License is distributed on an "AS IS" BASIS,
- * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
- * See the License for the specific language governing permissions and
- * limitations under the License.
- */
-package android.databinding.tool;
-
-import android.databinding.tool.processing.Scope;
-import android.databinding.tool.util.L;
-
-import org.gradle.api.DefaultTask;
-import org.gradle.api.tasks.TaskAction;
-import org.xml.sax.SAXException;
-import java.io.File;
-import java.io.IOException;
-
-import javax.xml.bind.JAXBException;
-import javax.xml.parsers.ParserConfigurationException;
-import javax.xml.xpath.XPathExpressionException;
-
-/**
- * Task that parses xml files and generated metadata.
- * Will be removed when aapt supports binding tags.
- */
-public class DataBindingProcessLayoutsTask extends DefaultTask {
-
- private LayoutXmlProcessor xmlProcessor;
-
- private File sdkDir;
-
- private File xmlOutFolder;
-
- private int minSdk;
-
- @TaskAction
- public void processResources()
- throws ParserConfigurationException, SAXException, XPathExpressionException,
- IOException {
- L.d("running process layouts task %s", getName());
- xmlProcessor.processResources(minSdk);
- Scope.assertNoError();
- }
-
- public void writeLayoutXmls() throws JAXBException {
- xmlProcessor.writeLayoutInfoFiles(xmlOutFolder);
- }
-
- public LayoutXmlProcessor getXmlProcessor() {
- return xmlProcessor;
- }
-
- public void setXmlProcessor(LayoutXmlProcessor xmlProcessor) {
- this.xmlProcessor = xmlProcessor;
- }
-
- public File getSdkDir() {
- return sdkDir;
- }
-
- public void setSdkDir(File sdkDir) {
- this.sdkDir = sdkDir;
- }
-
- public File getXmlOutFolder() {
- return xmlOutFolder;
- }
-
- public void setXmlOutFolder(File xmlOutFolder) {
- this.xmlOutFolder = xmlOutFolder;
- }
-
- public int getMinSdk() {
- return minSdk;
- }
-
- public void setMinSdk(int minSdk) {
- this.minSdk = minSdk;
- }
-}
diff --git a/gradlePlugin/src/main/resources/META-INF/gradle-plugins/com.android.databinding.properties b/gradlePlugin/src/main/resources/META-INF/gradle-plugins/com.android.databinding.properties
deleted file mode 100644
index 2e04d00..0000000
--- a/gradlePlugin/src/main/resources/META-INF/gradle-plugins/com.android.databinding.properties
+++ /dev/null
@@ -1,17 +0,0 @@
-#
-# 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/integration-tests/App With Spaces/app/build.gradle b/integration-tests/App With Spaces/app/build.gradle
index 4c8a182..15d5165 100644
--- a/integration-tests/App With Spaces/app/build.gradle
+++ b/integration-tests/App With Spaces/app/build.gradle
@@ -15,11 +15,10 @@
*/
apply plugin: 'com.android.application'
-apply plugin: 'com.android.databinding'
android {
- compileSdkVersion 21
- buildToolsVersion "22"
+ compileSdkVersion dataBindingConfig.compileSdkVersion
+ buildToolsVersion dataBindingConfig.buildToolsVersion
defaultConfig {
applicationId "com.android.databinding.appwithspaces"
@@ -28,6 +27,9 @@
versionCode 1
versionName "1.0"
}
+ dataBinding {
+ enabled = true
+ }
buildTypes {
release {
minifyEnabled false
diff --git a/integration-tests/App With Spaces/build.gradle b/integration-tests/App With Spaces/build.gradle
index b13840a..53e677e 100644
--- a/integration-tests/App With Spaces/build.gradle
+++ b/integration-tests/App With Spaces/build.gradle
@@ -14,11 +14,10 @@
* limitations under the License.
*/
buildscript {
- ext.rootFolder = new File(project.projectDir, "../..")
+ ext.dataBindingRootFolder = new File(project.projectDir, "../..")
apply from: "${project.projectDir}/../../propLoader.gradle"
ext.addRepos(repositories)
dependencies {
- classpath "com.android.tools.build:gradle:${config.androidPluginVersion}"
- classpath "com.android.databinding:dataBinder:${config.version}"
+ classpath "com.android.tools.build:gradle:${dataBindingConfig.androidPluginVersion}"
}
}
diff --git a/integration-tests/App With Spaces/gradle/wrapper/gradle-wrapper.properties b/integration-tests/App With Spaces/gradle/wrapper/gradle-wrapper.properties
index 992e276..f429d72 100644
--- a/integration-tests/App With Spaces/gradle/wrapper/gradle-wrapper.properties
+++ b/integration-tests/App With Spaces/gradle/wrapper/gradle-wrapper.properties
@@ -19,4 +19,4 @@
distributionPath=wrapper/dists
zipStoreBase=GRADLE_USER_HOME
zipStorePath=wrapper/dists
-distributionUrl=https\://services.gradle.org/distributions/gradle-2.2.1-all.zip
+distributionUrl=https\://services.gradle.org/distributions/gradle-2.10-all.zip
diff --git a/integration-tests/IndependentLibrary/app/build.gradle b/integration-tests/IndependentLibrary/app/build.gradle
index e4e7ab9..90d7328 100644
--- a/integration-tests/IndependentLibrary/app/build.gradle
+++ b/integration-tests/IndependentLibrary/app/build.gradle
@@ -14,11 +14,10 @@
*/
apply plugin: 'maven'
apply plugin: 'com.android.library'
-apply plugin: 'com.android.databinding'
android {
- compileSdkVersion 21
- buildToolsVersion "21.1.2"
+ compileSdkVersion dataBindingConfig.compileSdkVersion
+ buildToolsVersion dataBindingConfig.buildToolsVersion
defaultConfig {
minSdkVersion 7
@@ -26,6 +25,9 @@
versionCode 1
versionName "1.0"
}
+ dataBinding {
+ enabled = true
+ }
buildTypes {
release {
minifyEnabled false
@@ -47,10 +49,10 @@
uploadArchives {
repositories {
mavenDeployer {
- repository(url: "file://${config.mavenRepoDir}")
+ repository(url: "file://${dataBindingConfig.mavenRepoDir}")
pom.artifactId = 'independent-library'
- pom.version = config.version
- pom.groupId = config.testGroup
+ pom.version = dataBindingConfig.version
+ pom.groupId = dataBindingConfig.testGroup
}
}
}
diff --git a/integration-tests/IndependentLibrary/app/src/main/res/layout-sw600dp-land/library_layout.xml b/integration-tests/IndependentLibrary/app/src/main/res/layout-sw600dp-land/library_layout.xml
new file mode 100644
index 0000000..301663b
--- /dev/null
+++ b/integration-tests/IndependentLibrary/app/src/main/res/layout-sw600dp-land/library_layout.xml
@@ -0,0 +1,29 @@
+<?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.
+ -->
+<layout xmlns:android="http://schemas.android.com/apk/res/android">
+ <data class=".IndependentLibraryBinding">
+ <variable name="foo"
+ type="android.databinding.test.independentlibrary.vo.MyBindableObject"/>
+ </data>
+ <LinearLayout
+ android:orientation="vertical"
+ android:layout_width="match_parent"
+ android:layout_height="match_parent">
+ <TextView android:layout_width="match_parent" android:layout_height="wrap_content"
+ android:id="@+id/fooTextView"
+ android:text='@{foo.field + " " + foo.field}'/>
+ </LinearLayout>
+</layout>
diff --git a/integration-tests/IndependentLibrary/build.gradle b/integration-tests/IndependentLibrary/build.gradle
index db36a5b..ca2e97c 100644
--- a/integration-tests/IndependentLibrary/build.gradle
+++ b/integration-tests/IndependentLibrary/build.gradle
@@ -14,11 +14,10 @@
*/
buildscript {
- ext.rootFolder = new File(project.projectDir, "../..")
+ ext.dataBindingRootFolder = new File(project.projectDir, "../..")
apply from: "${project.projectDir}/../../propLoader.gradle"
ext.addRepos(repositories)
dependencies {
- classpath "com.android.tools.build:gradle:${config.androidPluginVersion}"
- classpath "com.android.databinding:dataBinder:${config.version}"
+ classpath "com.android.tools.build:gradle:${dataBindingConfig.androidPluginVersion}"
}
}
\ No newline at end of file
diff --git a/integration-tests/IndependentLibrary/gradle/wrapper/gradle-wrapper.properties b/integration-tests/IndependentLibrary/gradle/wrapper/gradle-wrapper.properties
index de86a57..8bb2e20 100644
--- a/integration-tests/IndependentLibrary/gradle/wrapper/gradle-wrapper.properties
+++ b/integration-tests/IndependentLibrary/gradle/wrapper/gradle-wrapper.properties
@@ -18,4 +18,4 @@
distributionPath=wrapper/dists
zipStoreBase=GRADLE_USER_HOME
zipStorePath=wrapper/dists
-distributionUrl=https\://services.gradle.org/distributions/gradle-2.2.1-all.zip
+distributionUrl=https\://services.gradle.org/distributions/gradle-2.10-all.zip
diff --git a/integration-tests/MultiModuleTestApp/app/build.gradle b/integration-tests/MultiModuleTestApp/app/build.gradle
index 67a52d5..261a902 100644
--- a/integration-tests/MultiModuleTestApp/app/build.gradle
+++ b/integration-tests/MultiModuleTestApp/app/build.gradle
@@ -15,11 +15,10 @@
*/
apply plugin: 'com.android.application'
-apply plugin: 'com.android.databinding'
android {
- compileSdkVersion 21
- buildToolsVersion "22"
+ compileSdkVersion dataBindingConfig.compileSdkVersion
+ buildToolsVersion dataBindingConfig.buildToolsVersion
defaultConfig {
applicationId "com.android.databinding.multimoduletestapp"
@@ -28,6 +27,9 @@
versionCode 1
versionName "1.0"
}
+ dataBinding {
+ enabled = true
+ }
buildTypes {
release {
minifyEnabled false
@@ -42,10 +44,10 @@
}
}
-println "combined ${config.testGroup}.independent-library:${config.version}"
+println "combined ${dataBindingConfig.testGroup}.independent-library:${dataBindingConfig.version}"
dependencies {
compile fileTree(dir: 'libs', include: ['*.jar'])
compile project(':testlibrary')
compile "com.android.support:support-v4:+"
- compile "${config.testGroup}:independent-library:${config.version}"
+ compile "${dataBindingConfig.testGroup}:independent-library:${dataBindingConfig.version}"
}
diff --git a/integration-tests/MultiModuleTestApp/app/src/androidTest/java/com/android/databinding/multimoduletestapp/EventIdsTest.java b/integration-tests/MultiModuleTestApp/app/src/androidTest/java/com/android/databinding/multimoduletestapp/EventIdsTest.java
index 9a04368..ec6df34 100644
--- a/integration-tests/MultiModuleTestApp/app/src/androidTest/java/com/android/databinding/multimoduletestapp/EventIdsTest.java
+++ b/integration-tests/MultiModuleTestApp/app/src/androidTest/java/com/android/databinding/multimoduletestapp/EventIdsTest.java
@@ -20,7 +20,6 @@
import android.databinding.Observable;
import android.databinding.Observable.OnPropertyChangedCallback;
-import android.os.Debug;
import android.test.AndroidTestCase;
import java.util.HashMap;
@@ -113,7 +112,7 @@
}
private static class EventCounter extends OnPropertyChangedCallback {
- Map<Integer, Integer> mCounter = new HashMap<>();
+ Map<Integer, Integer> mCounter = new HashMap<Integer, Integer>();
@Override
public void onPropertyChanged(Observable observable, int propertyId) {
diff --git a/integration-tests/MultiModuleTestApp/app/src/androidTest/java/com/android/databinding/multimoduletestapp/GeneratedLayoutTest.java b/integration-tests/MultiModuleTestApp/app/src/androidTest/java/com/android/databinding/multimoduletestapp/GeneratedLayoutTest.java
new file mode 100644
index 0000000..f83e9c0
--- /dev/null
+++ b/integration-tests/MultiModuleTestApp/app/src/androidTest/java/com/android/databinding/multimoduletestapp/GeneratedLayoutTest.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 com.android.databinding.multimoduletestapp;
+
+import android.databinding.DataBindingUtil;
+import android.databinding.ViewDataBinding;
+import android.databinding.multimoduletestapp.R;
+import android.test.AndroidTestCase;
+import android.view.LayoutInflater;
+import android.view.View;
+
+public class GeneratedLayoutTest extends AndroidTestCase {
+ public void testBindToGeneratedLayout() {
+ LayoutInflater inflater = LayoutInflater.from(getContext());
+ View view = inflater.inflate(R.layout.library_layout, null);
+ // force override tag
+ view.setTag("layout-sw600dp-land-v13/library_layout_0");
+ ViewDataBinding bind = DataBindingUtil.bind(view);
+ assertEquals("IndependentLibraryBindingSw600dpLandV13Impl",
+ bind.getClass().getSimpleName());
+ }
+
+ public void testBindToDefault() {
+ LayoutInflater inflater = LayoutInflater.from(getContext());
+ View view = inflater.inflate(R.layout.library_layout, null);
+ // force override tag
+ view.setTag("layout/library_layout_0");
+ ViewDataBinding bind = DataBindingUtil.bind(view);
+ assertEquals("IndependentLibraryBindingImpl",
+ bind.getClass().getSimpleName());
+ }
+
+ public void testBindToSw600() {
+ LayoutInflater inflater = LayoutInflater.from(getContext());
+ View view = inflater.inflate(R.layout.library_layout, null);
+ // force override tag
+ view.setTag("layout-sw600dp-land/library_layout_0");
+ ViewDataBinding bind = DataBindingUtil.bind(view);
+ assertEquals("IndependentLibraryBindingSw600dpLandImpl",
+ bind.getClass().getSimpleName());
+ }
+}
diff --git a/integration-tests/MultiModuleTestApp/build.gradle b/integration-tests/MultiModuleTestApp/build.gradle
index 5e5a3e2..7dc5032 100644
--- a/integration-tests/MultiModuleTestApp/build.gradle
+++ b/integration-tests/MultiModuleTestApp/build.gradle
@@ -14,11 +14,10 @@
* limitations under the License.
*/
buildscript {
- ext.rootFolder = new File(project.projectDir, "../..")
+ ext.dataBindingRootFolder = new File(project.projectDir, "../..")
apply from: "${project.projectDir}/../../propLoader.gradle"
ext.addRepos(repositories)
dependencies {
- classpath "com.android.tools.build:gradle:${config.androidPluginVersion}"
- classpath "com.android.databinding:dataBinder:${config.version}"
+ classpath "com.android.tools.build:gradle:${dataBindingConfig.androidPluginVersion}"
}
}
\ No newline at end of file
diff --git a/integration-tests/MultiModuleTestApp/gradle/wrapper/gradle-wrapper.properties b/integration-tests/MultiModuleTestApp/gradle/wrapper/gradle-wrapper.properties
index 992e276..f429d72 100644
--- a/integration-tests/MultiModuleTestApp/gradle/wrapper/gradle-wrapper.properties
+++ b/integration-tests/MultiModuleTestApp/gradle/wrapper/gradle-wrapper.properties
@@ -19,4 +19,4 @@
distributionPath=wrapper/dists
zipStoreBase=GRADLE_USER_HOME
zipStorePath=wrapper/dists
-distributionUrl=https\://services.gradle.org/distributions/gradle-2.2.1-all.zip
+distributionUrl=https\://services.gradle.org/distributions/gradle-2.10-all.zip
diff --git a/integration-tests/MultiModuleTestApp/testlibrary/build.gradle b/integration-tests/MultiModuleTestApp/testlibrary/build.gradle
index bbcff86..d1bde4d 100644
--- a/integration-tests/MultiModuleTestApp/testlibrary/build.gradle
+++ b/integration-tests/MultiModuleTestApp/testlibrary/build.gradle
@@ -15,11 +15,10 @@
*/
apply plugin: 'com.android.library'
-apply plugin: 'com.android.databinding'
android {
- compileSdkVersion 21
- buildToolsVersion "21.1.2"
+ compileSdkVersion dataBindingConfig.compileSdkVersion
+ buildToolsVersion dataBindingConfig.buildToolsVersion
defaultConfig {
minSdkVersion 7
@@ -27,6 +26,9 @@
versionCode 1
versionName "1.0"
}
+ dataBinding {
+ enabled = true
+ }
buildTypes {
release {
minifyEnabled false
diff --git a/integration-tests/TestApp/app/build.gradle b/integration-tests/TestApp/app/build.gradle
index 652bde2..0701944 100644
--- a/integration-tests/TestApp/app/build.gradle
+++ b/integration-tests/TestApp/app/build.gradle
@@ -1,9 +1,8 @@
apply plugin: 'com.android.application'
-apply plugin: 'com.android.databinding'
android {
compileSdkVersion 21
- buildToolsVersion "22"
+ buildToolsVersion dataBindingConfig.buildToolsVersion
defaultConfig {
applicationId "com.android.databinding.testapp"
@@ -12,6 +11,9 @@
versionCode 1
versionName "1.0"
}
+ dataBinding {
+ enabled = true
+ }
buildTypes {
release {
minifyEnabled false
@@ -30,6 +32,9 @@
api7 {
minSdkVersion 7
}
+ api14 {
+ minSdkVersion 14
+ }
}
}
diff --git a/integration-tests/TestApp/app/src/androidTest/java/android/databinding/DataBindingMapperTest.java b/integration-tests/TestApp/app/src/androidTest/java/android/databinding/DataBindingMapperTest.java
new file mode 100644
index 0000000..47075fd
--- /dev/null
+++ b/integration-tests/TestApp/app/src/androidTest/java/android/databinding/DataBindingMapperTest.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;
+
+
+import android.test.AndroidTestCase;
+import android.view.View;
+
+public class DataBindingMapperTest extends AndroidTestCase {
+ public void testNotDataBindingId() {
+ View view = new View(getContext());
+ view.setTag("layout/unexpected");
+ android.databinding.DataBinderMapper mapper = new android.databinding.DataBinderMapper();
+ ViewDataBinding binding = mapper.getDataBinder(null, view, 1);
+ assertNull(binding);
+ }
+ public void testInvalidView() {
+ View view = new View(getContext());
+ view.setTag("layout/unexpected");
+ android.databinding.DataBinderMapper mapper = new android.databinding.DataBinderMapper();
+ Throwable error = null;
+ try {
+ mapper.getDataBinder(null, view, android.databinding.testapp.R.layout.multi_res_layout);
+ } catch (Throwable t) {
+ error = t;
+ }
+ assertNotNull(error);
+ assertEquals("The tag for multi_res_layout is invalid. Received: layout/unexpected",
+ error.getMessage());
+
+ }
+}
diff --git a/integration-tests/TestApp/app/src/androidTestApi14/java/android/databinding/testapp/RootTag.java b/integration-tests/TestApp/app/src/androidTestApi14/java/android/databinding/testapp/RootTag.java
new file mode 100644
index 0000000..132beb6
--- /dev/null
+++ b/integration-tests/TestApp/app/src/androidTestApi14/java/android/databinding/testapp/RootTag.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.testapp;
+
+import android.databinding.testapp.BaseDataBinderTest;
+import android.databinding.testapp.databinding.RootTagBinding;
+
+public class RootTag extends BaseDataBinderTest<RootTagBinding> {
+ public RootTag() {
+ super(RootTagBinding.class);
+ }
+
+ @Override
+ protected void setUp() throws Exception {
+ super.setUp();
+ initBinder();
+ }
+
+ public void testRootTagSet() throws Throwable {
+ mBinder.executePendingBindings();
+ assertEquals("foo", mBinder.textView1.getTag());
+ assertEquals("hello world", mBinder.textView1.getText().toString());
+ }
+}
diff --git a/integration-tests/TestApp/app/src/androidTestApi7/java/android/databinding/testapp/AbsSpinnerBindingAdapterTest.java b/integration-tests/TestApp/app/src/androidTestApi7/java/android/databinding/testapp/AbsSpinnerBindingAdapterTest.java
index d1d45f5..06f6477 100644
--- a/integration-tests/TestApp/app/src/androidTestApi7/java/android/databinding/testapp/AbsSpinnerBindingAdapterTest.java
+++ b/integration-tests/TestApp/app/src/androidTestApi7/java/android/databinding/testapp/AbsSpinnerBindingAdapterTest.java
@@ -17,11 +17,13 @@
import android.databinding.testapp.databinding.AbsSpinnerAdapterTestBinding;
import android.databinding.testapp.vo.AbsSpinnerBindingObject;
-
import android.os.Build;
+import android.test.UiThreadTest;
import android.widget.Spinner;
import android.widget.SpinnerAdapter;
+import java.util.List;
+
public class AbsSpinnerBindingAdapterTest
extends BindingAdapterTestBase<AbsSpinnerAdapterTestBinding, AbsSpinnerBindingObject> {
@@ -38,6 +40,7 @@
mView = mBinder.view;
}
+ @UiThreadTest
public void testEntries() throws Throwable {
if (Build.VERSION.SDK_INT >= Build.VERSION_CODES.LOLLIPOP) {
validateEntries();
@@ -48,6 +51,18 @@
}
}
+ @UiThreadTest
+ public void testList() throws Throwable {
+ if (Build.VERSION.SDK_INT >= Build.VERSION_CODES.LOLLIPOP) {
+ validateList();
+
+ mBindingObject.getList().add(1, "Cruel");
+ mBinder.executePendingBindings();
+
+ validateList();
+ }
+ }
+
private void validateEntries() {
assertEquals(mBindingObject.getEntries().length, mView.getAdapter().getCount());
CharSequence[] entries = mBindingObject.getEntries();
@@ -56,4 +71,13 @@
assertEquals(adapter.getItem(i), entries[i]);
}
}
+
+ private void validateList() {
+ List<String> entries = mBindingObject.getList();
+ SpinnerAdapter adapter = mBinder.view2.getAdapter();
+ assertEquals(entries.size(), adapter.getCount());
+ for (int i = 0; i < entries.size(); i++) {
+ assertEquals(adapter.getItem(i), entries.get(i));
+ }
+ }
}
diff --git a/integration-tests/TestApp/app/src/androidTestApi7/java/android/databinding/testapp/AutoContextTest.java b/integration-tests/TestApp/app/src/androidTestApi7/java/android/databinding/testapp/AutoContextTest.java
new file mode 100644
index 0000000..74869c2
--- /dev/null
+++ b/integration-tests/TestApp/app/src/androidTestApi7/java/android/databinding/testapp/AutoContextTest.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.testapp;
+
+import android.databinding.testapp.databinding.AutoContextBinding;
+
+import android.test.UiThreadTest;
+import android.widget.TextView;
+
+public class AutoContextTest extends BaseDataBinderTest<AutoContextBinding> {
+
+ public AutoContextTest() {
+ super(AutoContextBinding.class);
+ }
+
+ @UiThreadTest
+ public void testContext() throws Throwable {
+ AutoContextBinding binding = initBinder();
+ binding.executePendingBindings();
+ assertNotSame("", binding.textView1.getText().toString());
+ }
+}
diff --git a/integration-tests/TestApp/app/src/androidTestApi7/java/android/databinding/testapp/BracketTest.java b/integration-tests/TestApp/app/src/androidTestApi7/java/android/databinding/testapp/BracketTest.java
index 031509d..136b7cc 100644
--- a/integration-tests/TestApp/app/src/androidTestApi7/java/android/databinding/testapp/BracketTest.java
+++ b/integration-tests/TestApp/app/src/androidTestApi7/java/android/databinding/testapp/BracketTest.java
@@ -54,6 +54,7 @@
mBinder.setSparseBooleanArray(mSparseBooleanArray);
mBinder.setSparseLongArray(mSparseLongArray);
mBinder.setLongSparseArray(mLongSparseArray);
+ mBinder.setIndexObj((Integer) 0);
mBinder.executePendingBindings();
}
@@ -69,4 +70,36 @@
assertEquals("true", mBinder.sparseBooleanArrayText.getText().toString());
assertEquals("5", mBinder.sparseLongArrayText.getText().toString());
}
+
+ @UiThreadTest
+ public void testBracketOutOfBounds() {
+ mBinder.setIndex(1);
+ mBinder.executePendingBindings();
+ assertEquals("", mBinder.arrayText.getText().toString());
+ assertEquals("", mBinder.sparseArrayText.getText().toString());
+ assertEquals("", mBinder.longSparseArrayText.getText().toString());
+ assertEquals("0", mBinder.sparseIntArrayText.getText().toString());
+ assertEquals("false", mBinder.sparseBooleanArrayText.getText().toString());
+ assertEquals("0", mBinder.sparseLongArrayText.getText().toString());
+ mBinder.setIndex(-1);
+ mBinder.executePendingBindings();
+ assertEquals("", mBinder.arrayText.getText().toString());
+ assertEquals("", mBinder.sparseArrayText.getText().toString());
+ assertEquals("", mBinder.longSparseArrayText.getText().toString());
+ assertEquals("0", mBinder.sparseIntArrayText.getText().toString());
+ assertEquals("false", mBinder.sparseBooleanArrayText.getText().toString());
+ assertEquals("0", mBinder.sparseLongArrayText.getText().toString());
+ }
+
+ @UiThreadTest
+ public void testBracketObj() {
+ mBinder.executePendingBindings();
+ assertEquals("Hello World", mBinder.indexObj.getText().toString());
+ assertEquals("Hello", mBinder.sparseArrayTextObj.getText().toString());
+ }
+
+ @UiThreadTest
+ public void testBracketMap() throws Throwable {
+ assertEquals("", mBinder.bracketMap.getText().toString());
+ }
}
diff --git a/integration-tests/TestApp/app/src/androidTestApi7/java/android/databinding/testapp/ConditionalBindingTest.java b/integration-tests/TestApp/app/src/androidTestApi7/java/android/databinding/testapp/ConditionalBindingTest.java
index 6f325c9..73c8c58 100644
--- a/integration-tests/TestApp/app/src/androidTestApi7/java/android/databinding/testapp/ConditionalBindingTest.java
+++ b/integration-tests/TestApp/app/src/androidTestApi7/java/android/databinding/testapp/ConditionalBindingTest.java
@@ -1,6 +1,7 @@
package android.databinding.testapp;
import android.databinding.testapp.databinding.ConditionalBindingBinding;
+import android.databinding.testapp.vo.ConditionalVo;
import android.databinding.testapp.vo.NotBindableVo;
import android.test.UiThreadTest;
@@ -17,6 +18,32 @@
testCorrectness(true, true);
}
+ @UiThreadTest
+ public void testTernary() throws Throwable {
+ ConditionalVo obj4 = new ConditionalVo();
+ initBinder();
+ mBinder.setObj4(obj4);
+ mBinder.executePendingBindings();
+ assertEquals("hello", mBinder.textView1.getText().toString());
+ obj4.setUseHello(true);
+ mBinder.executePendingBindings();
+ assertEquals("Hello World", mBinder.textView1.getText().toString());
+ }
+
+ @UiThreadTest
+ public void testNullListener() throws Throwable {
+ ConditionalVo obj4 = new ConditionalVo();
+ initBinder();
+ mBinder.setObj4(obj4);
+ mBinder.executePendingBindings();
+ mBinder.view1.callOnClick();
+ assertFalse(obj4.wasClicked);
+ mBinder.setCond1(true);
+ mBinder.executePendingBindings();
+ mBinder.view1.callOnClick();
+ assertTrue(obj4.wasClicked);
+ }
+
private void testCorrectness(boolean cond1, boolean cond2) {
NotBindableVo o1 = new NotBindableVo("a");
NotBindableVo o2 = new NotBindableVo("b");
diff --git a/integration-tests/TestApp/app/src/androidTestApi7/java/android/databinding/testapp/ConstExpressionTest.java b/integration-tests/TestApp/app/src/androidTestApi7/java/android/databinding/testapp/ConstExpressionTest.java
new file mode 100644
index 0000000..30d7926
--- /dev/null
+++ b/integration-tests/TestApp/app/src/androidTestApi7/java/android/databinding/testapp/ConstExpressionTest.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;
+
+import android.databinding.testapp.databinding.ConstExpressionTestBinding;
+import android.databinding.testapp.databinding.ExpressionTestBinding;
+import android.test.UiThreadTest;
+
+public class ConstExpressionTest extends BaseDataBinderTest<ConstExpressionTestBinding> {
+ public ConstExpressionTest() {
+ super(ConstExpressionTestBinding.class);
+ }
+
+ @Override
+ protected void setUp() throws Exception {
+ super.setUp();
+ initBinder();
+ }
+
+
+ @UiThreadTest
+ public void testConstantExpression() throws Throwable {
+ mBinder.setVar1(1000);
+ mBinder.setVar2(2000);
+ mBinder.executePendingBindings();
+ assertEquals("1000", mBinder.textView.getText().toString());
+ }
+}
diff --git a/integration-tests/TestApp/app/src/androidTestApi7/java/android/databinding/testapp/ConverterTest.java b/integration-tests/TestApp/app/src/androidTestApi7/java/android/databinding/testapp/ConverterTest.java
new file mode 100644
index 0000000..35a98fc
--- /dev/null
+++ b/integration-tests/TestApp/app/src/androidTestApi7/java/android/databinding/testapp/ConverterTest.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.ConvertersBinding;
+
+import android.test.UiThreadTest;
+
+import java.util.ArrayList;
+import java.util.LinkedList;
+
+public class ConverterTest extends BaseDataBinderTest<ConvertersBinding> {
+ public ConverterTest() {
+ super(ConvertersBinding.class);
+ }
+
+ @UiThreadTest
+ public void testGenericConverter() {
+ initBinder();
+ ArrayList<String> values = new ArrayList<String>();
+ LinkedList<String> linkedValues = new LinkedList<String>();
+ values.add("Hello");
+ values.add("World");
+ linkedValues.add("Holy");
+ linkedValues.add("Cow!");
+ mBinder.setList(values);
+ mBinder.setLinked(linkedValues);
+ mBinder.executePendingBindings();
+ assertEquals("Hello World", mBinder.textView1.getText().toString());
+ assertEquals("Holy Cow!", mBinder.textView2.getText().toString());
+ }
+}
diff --git a/integration-tests/TestApp/app/src/androidTestApi7/java/android/databinding/testapp/DuplicateContextTest.java b/integration-tests/TestApp/app/src/androidTestApi7/java/android/databinding/testapp/DuplicateContextTest.java
new file mode 100644
index 0000000..43a2f8a
--- /dev/null
+++ b/integration-tests/TestApp/app/src/androidTestApi7/java/android/databinding/testapp/DuplicateContextTest.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.testapp;
+
+import android.databinding.testapp.databinding.DuplicateContextBinding;
+
+import android.test.UiThreadTest;
+import android.widget.TextView;
+
+public class DuplicateContextTest extends BaseDataBinderTest<DuplicateContextBinding> {
+
+ public DuplicateContextTest() {
+ super(DuplicateContextBinding.class);
+ }
+
+ @UiThreadTest
+ public void testContext() throws Throwable {
+ DuplicateContextBinding binding = initBinder();
+ binding.setContext("Hello World");
+ binding.executePendingBindings();
+ assertEquals("Hello World", binding.textView1.getText().toString());
+ }
+}
diff --git a/integration-tests/TestApp/app/src/androidTestApi7/java/android/databinding/testapp/ExpressionTest.java b/integration-tests/TestApp/app/src/androidTestApi7/java/android/databinding/testapp/ExpressionTest.java
index ce1eafa..25965a7 100644
--- a/integration-tests/TestApp/app/src/androidTestApi7/java/android/databinding/testapp/ExpressionTest.java
+++ b/integration-tests/TestApp/app/src/androidTestApi7/java/android/databinding/testapp/ExpressionTest.java
@@ -16,11 +16,8 @@
package android.databinding.testapp;
import android.databinding.testapp.databinding.ExpressionTestBinding;
-import android.support.v4.util.ArrayMap;
import android.test.UiThreadTest;
-import java.util.ArrayList;
-
public class ExpressionTest extends BaseDataBinderTest<ExpressionTestBinding> {
public ExpressionTest() {
super(ExpressionTestBinding.class);
@@ -158,4 +155,23 @@
assertEquals(mBinder.getBool1() ? appName : mBinder.getBool2() ? rain : "",
mBinder.textView18.getText().toString());
}
+
+ @UiThreadTest
+ public void testBoundTag() throws Throwable {
+ mBinder.setBool1(false);
+ mBinder.executePendingBindings();
+ assertEquals("bar", mBinder.textView19.getTag());
+ mBinder.setBool1(true);
+ mBinder.executePendingBindings();
+ assertEquals("foo", mBinder.textView19.getTag());
+ }
+
+ @UiThreadTest
+ public void testConstantExpression() throws Throwable {
+ mBinder.setVar1(1000);
+ mBinder.setVar2(2000);
+ mBinder.executePendingBindings();
+ assertEquals("1000", mBinder.textView20.getText().toString());
+ assertEquals("2000", mBinder.textView21.getText().toString());
+ }
}
diff --git a/integration-tests/TestApp/app/src/androidTestApi7/java/android/databinding/testapp/FindFieldTest.java b/integration-tests/TestApp/app/src/androidTestApi7/java/android/databinding/testapp/FindFieldTest.java
index 6c73cde..ca0ed53 100644
--- a/integration-tests/TestApp/app/src/androidTestApi7/java/android/databinding/testapp/FindFieldTest.java
+++ b/integration-tests/TestApp/app/src/androidTestApi7/java/android/databinding/testapp/FindFieldTest.java
@@ -33,4 +33,11 @@
mBinder.executePendingBindings();
assertEquals(obj.mPublicField, mBinder.textView1.getText().toString());
}
+
+ @UiThreadTest
+ public void testFieldOnGeneric() {
+ initBinder();
+ mBinder.executePendingBindings();
+ assertEquals("Hello", mBinder.textView2.getText().toString());
+ }
}
diff --git a/integration-tests/TestApp/app/src/androidTestApi7/java/android/databinding/testapp/FindMethodTest.java b/integration-tests/TestApp/app/src/androidTestApi7/java/android/databinding/testapp/FindMethodTest.java
index f5f5c72..a7799dc 100644
--- a/integration-tests/TestApp/app/src/androidTestApi7/java/android/databinding/testapp/FindMethodTest.java
+++ b/integration-tests/TestApp/app/src/androidTestApi7/java/android/databinding/testapp/FindMethodTest.java
@@ -116,4 +116,11 @@
mBinder.executePendingBindings();
assertEquals("barfoo", mBinder.textView26.getText().toString());
}
+
+ @UiThreadTest
+ public void testPrimitiveToObject() throws Throwable {
+ mBinder.executePendingBindings();
+ assertTrue(mBinder.textView27.getTag() instanceof Integer);
+ assertEquals((Integer)1, mBinder.textView27.getTag());
+ }
}
diff --git a/integration-tests/TestApp/app/src/androidTestApi7/java/android/databinding/testapp/FrameLayoutBindingAdapterTest.java b/integration-tests/TestApp/app/src/androidTestApi7/java/android/databinding/testapp/FrameLayoutBindingAdapterTest.java
index 1f2f835..5246f6b 100644
--- a/integration-tests/TestApp/app/src/androidTestApi7/java/android/databinding/testapp/FrameLayoutBindingAdapterTest.java
+++ b/integration-tests/TestApp/app/src/androidTestApi7/java/android/databinding/testapp/FrameLayoutBindingAdapterTest.java
@@ -19,6 +19,7 @@
import android.databinding.testapp.vo.FrameLayoutBindingObject;
import android.os.Build;
+import android.test.UiThreadTest;
import android.widget.FrameLayout;
public class FrameLayoutBindingAdapterTest
@@ -37,6 +38,7 @@
mView = mBinder.view;
}
+ @UiThreadTest
public void testTint() throws Throwable {
if (Build.VERSION.SDK_INT >= Build.VERSION_CODES.LOLLIPOP) {
assertEquals(mBindingObject.getForegroundTint(),
diff --git a/integration-tests/TestApp/app/src/androidTestApi7/java/android/databinding/testapp/ImageViewBindingAdapterTest.java b/integration-tests/TestApp/app/src/androidTestApi7/java/android/databinding/testapp/ImageViewBindingAdapterTest.java
index 5b995f4..19f025d 100644
--- a/integration-tests/TestApp/app/src/androidTestApi7/java/android/databinding/testapp/ImageViewBindingAdapterTest.java
+++ b/integration-tests/TestApp/app/src/androidTestApi7/java/android/databinding/testapp/ImageViewBindingAdapterTest.java
@@ -66,4 +66,14 @@
assertNotNull(mBinder.view2.getDrawable());
assertNotNull(mBinder.view3.getDrawable());
}
+
+ @UiThreadTest
+ public void testConditionalSource() throws Throwable {
+ mBinder.setObj(null);
+ mBinder.executePendingBindings();
+ assertNotNull(mBinder.view4.getDrawable());
+ mBinder.setObj(new ImageViewBindingObject());
+ mBinder.executePendingBindings();
+ assertNull(mBinder.view4.getDrawable());
+ }
}
diff --git a/integration-tests/TestApp/app/src/androidTestApi7/java/android/databinding/testapp/IncludeTagTest.java b/integration-tests/TestApp/app/src/androidTestApi7/java/android/databinding/testapp/IncludeTagTest.java
index f9ba635..5c2b860 100644
--- a/integration-tests/TestApp/app/src/androidTestApi7/java/android/databinding/testapp/IncludeTagTest.java
+++ b/integration-tests/TestApp/app/src/androidTestApi7/java/android/databinding/testapp/IncludeTagTest.java
@@ -19,6 +19,7 @@
import android.databinding.testapp.vo.NotBindableVo;
import android.test.UiThreadTest;
+import android.widget.FrameLayout;
import android.widget.TextView;
public class IncludeTagTest extends BaseDataBinderTest<LayoutWithIncludeBinding> {
@@ -30,6 +31,8 @@
@UiThreadTest
public void testIncludeTag() {
initBinder();
+ assertNotNull(mBinder.includedPlainLayout);
+ assertTrue(mBinder.includedPlainLayout instanceof FrameLayout);
NotBindableVo vo = new NotBindableVo(3, "a");
mBinder.setOuterObject(vo);
mBinder.executePendingBindings();
diff --git a/integration-tests/TestApp/app/src/androidTestApi7/java/android/databinding/testapp/InstanceAdapterTest.java b/integration-tests/TestApp/app/src/androidTestApi7/java/android/databinding/testapp/InstanceAdapterTest.java
index c4e7dd0..f8ad690 100644
--- a/integration-tests/TestApp/app/src/androidTestApi7/java/android/databinding/testapp/InstanceAdapterTest.java
+++ b/integration-tests/TestApp/app/src/androidTestApi7/java/android/databinding/testapp/InstanceAdapterTest.java
@@ -18,6 +18,8 @@
import android.databinding.DataBindingComponent;
import android.databinding.DataBindingUtil;
import android.databinding.testapp.adapter.InstanceAdapter;
+import android.databinding.testapp.adapter.NameClashAdapter;
+import android.databinding.testapp.adapter.NameClashAdapter.MyAdapter;
import android.databinding.testapp.databinding.IncludeInstanceAdapterBinding;
import android.databinding.testapp.databinding.InstanceAdapterBinding;
import android.test.UiThreadTest;
@@ -38,6 +40,26 @@
private InstanceAdapter mInstanceAdapter = new InstanceAdapter("Hello %s %s %s %s");
@Override
+ public MyAdapter getMyAdapter1() {
+ return null;
+ }
+
+ @Override
+ public android.databinding.testapp.adapter2.NameClashAdapter.MyAdapter getMyAdapter2() {
+ return null;
+ }
+
+ @Override
+ public NameClashAdapter getNameClashAdapter1() {
+ return null;
+ }
+
+ @Override
+ public android.databinding.testapp.adapter2.NameClashAdapter getNameClashAdapter2() {
+ return null;
+ }
+
+ @Override
public InstanceAdapter getInstanceAdapter() {
return mInstanceAdapter;
}
diff --git a/integration-tests/TestApp/app/src/androidTestApi7/java/android/databinding/testapp/LeakTest.java b/integration-tests/TestApp/app/src/androidTestApi7/java/android/databinding/testapp/LeakTest.java
index d9fa756..8f63bcc 100644
--- a/integration-tests/TestApp/app/src/androidTestApi7/java/android/databinding/testapp/LeakTest.java
+++ b/integration-tests/TestApp/app/src/androidTestApi7/java/android/databinding/testapp/LeakTest.java
@@ -82,7 +82,7 @@
public void run() {
getActivity().setContentView(new FrameLayout(getActivity()));
binding.setName("goodbye world");
- binding.getRoot().postOnAnimation(watcher);
+ getActivity().getWindow().getDecorView().postOnAnimation(watcher);
}
});
@@ -93,7 +93,7 @@
public void run() {
assertEquals("hello world", binding.textView.getText().toString());
getActivity().setContentView(binding.getRoot());
- binding.getRoot().postOnAnimation(watcher);
+ getActivity().getWindow().getDecorView().postOnAnimation(watcher);
}
});
diff --git a/integration-tests/TestApp/app/src/androidTestApi7/java/android/databinding/testapp/ListenerTest.java b/integration-tests/TestApp/app/src/androidTestApi7/java/android/databinding/testapp/ListenerTest.java
index 8fc67bf..d15f829 100644
--- a/integration-tests/TestApp/app/src/androidTestApi7/java/android/databinding/testapp/ListenerTest.java
+++ b/integration-tests/TestApp/app/src/androidTestApi7/java/android/databinding/testapp/ListenerTest.java
@@ -16,6 +16,7 @@
import android.databinding.ViewStubProxy;
import android.databinding.testapp.databinding.ListenersBinding;
import android.databinding.testapp.vo.ListenerBindingObject;
+import android.databinding.testapp.vo.ListenerBindingObject.Inner;
import android.test.UiThreadTest;
import android.view.View;
@@ -113,4 +114,15 @@
assertTrue(mBindingObject.inflateCalled);
assertTrue(viewStubProxy.isInflated());
}
+
+ @UiThreadTest
+ public void testBaseObservableClick() throws Throwable {
+ View view = mBinder.click6;
+ Inner inner = new Inner();
+ mBinder.setObj2(inner);
+ mBinder.executePendingBindings();
+ assertFalse(inner.clicked);
+ view.callOnClick();
+ assertTrue(inner.clicked);
+ }
}
diff --git a/integration-tests/TestApp/app/src/androidTestApi7/java/android/databinding/testapp/NameMappingTest.java b/integration-tests/TestApp/app/src/androidTestApi7/java/android/databinding/testapp/NameMappingTest.java
new file mode 100644
index 0000000..ce505d5
--- /dev/null
+++ b/integration-tests/TestApp/app/src/androidTestApi7/java/android/databinding/testapp/NameMappingTest.java
@@ -0,0 +1,71 @@
+/*
+ * Copyright (C) 2015 The Android Open Source Project
+ *
+ * Licensed under the Apache License, Version 2.0 (the "License");
+ * you may not use this file except in compliance with the License.
+ * You may obtain a copy of the License at
+ *
+ * http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing, software
+ * distributed under the License is distributed on an "AS IS" BASIS,
+ * WITHOUT 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.NameMappingTestBinding;
+import android.databinding.testapp.vo.BasicObject;
+import android.test.UiThreadTest;
+import android.databinding.testapp.BR;
+
+import java.util.concurrent.atomic.AtomicBoolean;
+
+public class NameMappingTest extends BaseDataBinderTest<NameMappingTestBinding> {
+
+ public NameMappingTest() {
+ super(NameMappingTestBinding.class);
+ }
+
+ @UiThreadTest
+ public void testChanges() {
+ initBinder();
+ final AtomicBoolean f1 = new AtomicBoolean(false);
+ final AtomicBoolean f2 = new AtomicBoolean(false);
+ BasicObject object = new BasicObject() {
+ @Override
+ public boolean isThisNameDoesNotMatchAnythingElse1() {
+ return f1.get();
+ }
+
+ @Override
+ public boolean getThisNameDoesNotMatchAnythingElse2() {
+ return f2.get();
+ }
+ };
+ mBinder.setObj(object);
+ mBinder.executePendingBindings();
+ for (int i = 0; i < 5; i ++) {
+ boolean f1New = (i & 1) != 0;
+ boolean f2New = (i & 1 << 1) != 0;
+ if (f1New != f1.get()) {
+ f1.set(f1New);
+ object.notifyPropertyChanged(BR.thisNameDoesNotMatchAnythingElse1);
+ }
+ if (f2New != f2.get()) {
+ f1.set(f2New);
+ object.notifyPropertyChanged(BR.thisNameDoesNotMatchAnythingElse2);
+ }
+ mBinder.executePendingBindings();
+ assertEquals(f2.get(), mBinder.textView.isEnabled());
+ assertEquals(f2.get(), mBinder.textView2.isEnabled());
+ assertEquals(false, mBinder.textView3.isEnabled());
+
+ assertEquals(f1.get(), mBinder.textView.isFocusable());
+ assertEquals(f1.get(), mBinder.textView2.isFocusable());
+ assertEquals(false, mBinder.textView3.isFocusable());
+ }
+ }
+}
diff --git a/integration-tests/TestApp/app/src/androidTestApi7/java/android/databinding/testapp/NoVariableIncludeTest.java b/integration-tests/TestApp/app/src/androidTestApi7/java/android/databinding/testapp/NoVariableIncludeTest.java
new file mode 100644
index 0000000..916cb50
--- /dev/null
+++ b/integration-tests/TestApp/app/src/androidTestApi7/java/android/databinding/testapp/NoVariableIncludeTest.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.IncludeNoVariablesBinding;
+import android.test.UiThreadTest;
+import android.view.ViewGroup;
+import android.widget.TextView;
+
+public class NoVariableIncludeTest extends BaseDataBinderTest<IncludeNoVariablesBinding> {
+
+ public NoVariableIncludeTest() {
+ super(IncludeNoVariablesBinding.class);
+ }
+
+ @UiThreadTest
+ public void testInclude() {
+ initBinder();
+ mBinder.executePendingBindings();
+ assertNotNull(mBinder.included);
+ assertNotNull(mBinder.included.textView);
+ String expectedValue = getActivity().getResources().getString(R.string.app_name);
+ assertEquals(expectedValue, mBinder.included.textView.getText().toString());
+ TextView noIdInclude = (TextView) ((ViewGroup) mBinder.getRoot()).getChildAt(1);
+ assertEquals(expectedValue, noIdInclude.getText().toString());
+ }
+}
diff --git a/integration-tests/TestApp/app/src/androidTestApi7/java/android/databinding/testapp/TextViewBindingAdapterTest.java b/integration-tests/TestApp/app/src/androidTestApi7/java/android/databinding/testapp/TextViewBindingAdapterTest.java
index 98272dd..c6bc671 100644
--- a/integration-tests/TestApp/app/src/androidTestApi7/java/android/databinding/testapp/TextViewBindingAdapterTest.java
+++ b/integration-tests/TestApp/app/src/androidTestApi7/java/android/databinding/testapp/TextViewBindingAdapterTest.java
@@ -22,6 +22,7 @@
import android.databinding.adapters.TextViewBindingAdapter;
import android.graphics.drawable.ColorDrawable;
import android.os.Build;
+import android.test.UiThreadTest;
import android.text.Editable;
import android.text.InputFilter;
import android.text.InputType;
@@ -301,4 +302,17 @@
assertTrue(TextViewBindingObject.KeyListener2.class.isInstance(textView.getKeyListener()));
}
+ @UiThreadTest
+ public void testTextWithTheme() throws Throwable {
+ TextView textView = mBinder.textWithTheme;
+ assertNotNull(textView.getTextColors());
+ }
+
+ @UiThreadTest
+ public void testTextWithColor() throws Throwable {
+ TextView textView = mBinder.textWithColor;
+ int expectedColor = mBinder.getRoot().getResources().getColor(
+ android.R.color.holo_blue_bright);
+ assertEquals(expectedColor, textView.getCurrentTextColor());
+ }
}
diff --git a/integration-tests/TestApp/app/src/androidTestApi7/java/android/databinding/testapp/UnnecessaryCalculationTest.java b/integration-tests/TestApp/app/src/androidTestApi7/java/android/databinding/testapp/UnnecessaryCalculationTest.java
new file mode 100644
index 0000000..986156c
--- /dev/null
+++ b/integration-tests/TestApp/app/src/androidTestApi7/java/android/databinding/testapp/UnnecessaryCalculationTest.java
@@ -0,0 +1,74 @@
+/*
+ * Copyright (C) 2015 The Android Open Source Project
+ *
+ * Licensed under the Apache License, Version 2.0 (the "License");
+ * you may not use this file except in compliance with the License.
+ * You may obtain a copy of the License at
+ *
+ * http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing, software
+ * distributed under the License is distributed on an "AS IS" BASIS,
+ * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+ * See the License for the specific language governing permissions and
+ * limitations under the License.
+ */
+package android.databinding.testapp;
+
+import android.databinding.testapp.databinding.UnnecessaryCalculationBinding;
+import android.databinding.testapp.vo.BasicObject;
+import android.support.annotation.UiThread;
+import android.test.UiThreadTest;
+
+import java.util.concurrent.atomic.AtomicInteger;
+
+public class UnnecessaryCalculationTest extends BaseDataBinderTest<UnnecessaryCalculationBinding> {
+
+ public UnnecessaryCalculationTest() {
+ super(UnnecessaryCalculationBinding.class);
+ }
+
+ @Override
+ protected void setUp() throws Exception {
+ super.setUp();
+ initBinder();
+ }
+
+ @UiThreadTest
+ public void testDontSetUnnecessaryFlags() {
+ BasicObjWithCounter obja = new BasicObjWithCounter();
+ BasicObjWithCounter objb = new BasicObjWithCounter();
+ BasicObjWithCounter objc = new BasicObjWithCounter();
+ mBinder.setObja(obja);
+ mBinder.setObjb(objb);
+ mBinder.setObjc(objc);
+ mBinder.setA(true);
+ mBinder.setB(true);
+ mBinder.setC(false);
+ mBinder.executePendingBindings();
+ assertEquals("true", mBinder.textView.getText().toString());
+ assertEquals("true", mBinder.textView2.getText().toString());
+ assertEquals(1, obja.counter);
+ assertEquals(1, objb.counter);
+ assertEquals(0, objc.counter);
+ obja = new BasicObjWithCounter();
+ mBinder.setObja(obja);
+ mBinder.executePendingBindings();
+ assertEquals("true", mBinder.textView.getText().toString());
+ assertEquals("true", mBinder.textView2.getText().toString());
+ assertEquals(1, obja.counter);
+ assertEquals(1, objb.counter);
+ assertEquals(0, objc.counter);
+ }
+
+ private static class BasicObjWithCounter extends BasicObject {
+ int counter = 0;
+
+ @Override
+ public String boolMethod(boolean value) {
+ counter ++;
+ return super.boolMethod(value);
+ }
+ }
+
+}
diff --git a/integration-tests/TestApp/app/src/androidTestApi7/java/android/databinding/testapp/ViewBindingAdapterTest.java b/integration-tests/TestApp/app/src/androidTestApi7/java/android/databinding/testapp/ViewBindingAdapterTest.java
index c9bbb35..3fe918a 100644
--- a/integration-tests/TestApp/app/src/androidTestApi7/java/android/databinding/testapp/ViewBindingAdapterTest.java
+++ b/integration-tests/TestApp/app/src/androidTestApi7/java/android/databinding/testapp/ViewBindingAdapterTest.java
@@ -15,10 +15,10 @@
*/
package android.databinding.testapp;
+import android.content.res.ColorStateList;
import android.databinding.testapp.databinding.ViewAdapterTestBinding;
import android.databinding.testapp.vo.ViewBindingObject;
-
-import android.content.res.ColorStateList;
+import android.graphics.drawable.Drawable;
import android.os.Build;
import android.test.UiThreadTest;
import android.view.View;
@@ -177,4 +177,17 @@
assertEquals(mBindingObject.getTransformPivotY(), view.getPivotY());
}
}
+
+ @UiThreadTest
+ public void testBackgroundDrawableDrawable() throws Throwable {
+ View view = mBinder.backgroundDrawable;
+ Drawable drawable = view.getBackground();
+ assertNotNull(drawable);
+ }
+
+ @UiThreadTest
+ public void testBackgroundDrawableWithTheme() throws Throwable {
+ View view = mBinder.backgroundWithTheme;
+ assertNotNull(view.getBackground());
+ }
}
diff --git a/integration-tests/TestApp/app/src/androidTestApi7/java/android/databinding/testapp/ViewWithTagTest.java b/integration-tests/TestApp/app/src/androidTestApi7/java/android/databinding/testapp/ViewWithTagTest.java
index 05ad2b1..f1f5e9e 100644
--- a/integration-tests/TestApp/app/src/androidTestApi7/java/android/databinding/testapp/ViewWithTagTest.java
+++ b/integration-tests/TestApp/app/src/androidTestApi7/java/android/databinding/testapp/ViewWithTagTest.java
@@ -37,5 +37,6 @@
assertTrue(view2 instanceof TextView);
assertEquals("i don't have tag", ((TextView) view2).getText().toString());
assertEquals("i have a tag", view1.getTag().toString());
+ assertEquals("Hello", view2.getTag(R.id.customTag));
}
}
diff --git a/integration-tests/TestApp/app/src/androidTestApi7/java/android/databinding/testapp/multiconfig/LandscapeConfigTest.java b/integration-tests/TestApp/app/src/androidTestApi7/java/android/databinding/testapp/multiconfig/LandscapeConfigTest.java
index 965562f..ddda269 100644
--- a/integration-tests/TestApp/app/src/androidTestApi7/java/android/databinding/testapp/multiconfig/LandscapeConfigTest.java
+++ b/integration-tests/TestApp/app/src/androidTestApi7/java/android/databinding/testapp/multiconfig/LandscapeConfigTest.java
@@ -15,6 +15,7 @@
import android.databinding.ViewDataBinding;
import android.databinding.testapp.BaseLandDataBinderTest;
+import android.databinding.testapp.BR;
import android.databinding.testapp.R;
import android.databinding.testapp.databinding.BasicBindingBinding;
import android.databinding.testapp.databinding.ConditionalBindingBinding;
@@ -23,6 +24,7 @@
import android.databinding.testapp.vo.NotBindableVo;
import android.content.pm.ActivityInfo;
+import android.test.UiThreadTest;
import android.view.View;
import android.widget.TextView;
@@ -49,4 +51,13 @@
assertPublicField(ConditionalBindingBinding.class, "includedLayoutPort");
assertPublicField(ConditionalBindingBinding.class, "includedLayoutLand");
}
+
+ @UiThreadTest
+ public void testSetVariable() throws Throwable {
+ initBinder();
+ assertTrue(mBinder.setVariable(BR.objectInBoth, null));
+ assertTrue(mBinder.setVariable(BR.objectInDefault, null));
+ assertTrue(mBinder.setVariable(BR.objectInLand, null));
+ assertFalse(mBinder.setVariable(BR.obj, null));
+ }
}
diff --git a/integration-tests/TestApp/app/src/androidTestApi7/java/android/databinding/testapp/multiconfig/PortraitConfigTest.java b/integration-tests/TestApp/app/src/androidTestApi7/java/android/databinding/testapp/multiconfig/PortraitConfigTest.java
index b1d1469..6a259bb 100644
--- a/integration-tests/TestApp/app/src/androidTestApi7/java/android/databinding/testapp/multiconfig/PortraitConfigTest.java
+++ b/integration-tests/TestApp/app/src/androidTestApi7/java/android/databinding/testapp/multiconfig/PortraitConfigTest.java
@@ -15,6 +15,7 @@
import android.databinding.ViewDataBinding;
import android.databinding.testapp.BaseDataBinderTest;
+import android.databinding.testapp.BR;
import android.databinding.testapp.databinding.BasicBindingBinding;
import android.databinding.testapp.databinding.ConditionalBindingBinding;
import android.databinding.testapp.databinding.IncludedLayoutBinding;
@@ -22,6 +23,7 @@
import android.databinding.testapp.vo.NotBindableVo;
import android.content.pm.ActivityInfo;
+import android.test.UiThreadTest;
import android.view.View;
import android.widget.EditText;
import android.widget.TextView;
@@ -47,4 +49,13 @@
assertPublicField(ConditionalBindingBinding.class, "includedLayoutPort");
assertPublicField(ConditionalBindingBinding.class, "includedLayoutLand");
}
+
+ @UiThreadTest
+ public void testSetVariable() throws Throwable {
+ initBinder();
+ assertTrue(mBinder.setVariable(BR.objectInBoth, null));
+ assertTrue(mBinder.setVariable(BR.objectInDefault, null));
+ assertFalse(mBinder.setVariable(BR.obj, null));
+ assertFalse(mBinder.setVariable(BR.objectInLand, null));
+ }
}
diff --git a/integration-tests/TestApp/app/src/api14/res/layout/root_tag.xml b/integration-tests/TestApp/app/src/api14/res/layout/root_tag.xml
new file mode 100644
index 0000000..b7be6bf
--- /dev/null
+++ b/integration-tests/TestApp/app/src/api14/res/layout/root_tag.xml
@@ -0,0 +1,28 @@
+<?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.
+ -->
+<layout xmlns:android="http://schemas.android.com/apk/res/android">
+ <data>
+ <variable name="var1" type="int"/>
+ <variable name="var2" type="int"/>
+ <variable name="bool1" type="boolean"/>
+ <variable name="bool2" type="boolean"/>
+ </data>
+ <TextView
+ android:id="@+id/textView1"
+ android:tag="@{`foo`}"
+ android:orientation="vertical"
+ android:layout_width="match_parent"
+ android:layout_height="match_parent"
+ android:text="@{`hello world`}"/>
+</layout>
\ No newline at end of file
diff --git a/integration-tests/TestApp/app/src/main/java/android/databinding/testapp/adapter/GenericConverter.java b/integration-tests/TestApp/app/src/main/java/android/databinding/testapp/adapter/GenericConverter.java
new file mode 100644
index 0000000..a8aae55
--- /dev/null
+++ b/integration-tests/TestApp/app/src/main/java/android/databinding/testapp/adapter/GenericConverter.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.adapter;
+
+import android.databinding.BindingConversion;
+import java.util.ArrayList;
+import java.util.LinkedList;
+import java.util.List;
+
+public class GenericConverter {
+ @BindingConversion
+ public static <T> String convertArrayList(ArrayList<T> values) {
+ return convert(values);
+ }
+
+ @BindingConversion
+ public static String convertLinkedList(LinkedList<?> values) {
+ return convert(values);
+ }
+
+ private static <T> String convert(List<T> values) {
+ if (values == null) {
+ return "";
+ }
+ StringBuilder vals = new StringBuilder();
+ for (T val : values) {
+ if (vals.length() != 0) {
+ vals.append(' ');
+ }
+ vals.append(val);
+ }
+ return vals.toString();
+ }
+}
diff --git a/integration-tests/TestApp/app/src/main/java/android/databinding/testapp/adapter/MultiArgTestAdapter.java b/integration-tests/TestApp/app/src/main/java/android/databinding/testapp/adapter/MultiArgTestAdapter.java
index f83cd8d..062b884 100644
--- a/integration-tests/TestApp/app/src/main/java/android/databinding/testapp/adapter/MultiArgTestAdapter.java
+++ b/integration-tests/TestApp/app/src/main/java/android/databinding/testapp/adapter/MultiArgTestAdapter.java
@@ -21,8 +21,6 @@
import android.databinding.testapp.BR;
import android.widget.TextView;
-import org.apache.commons.lang3.StringUtils;
-
public class MultiArgTestAdapter {
public static String join(BaseMultiBindingClass... classes) {
diff --git a/integration-tests/TestApp/app/src/main/java/android/databinding/testapp/adapter/NameClashAdapter.java b/integration-tests/TestApp/app/src/main/java/android/databinding/testapp/adapter/NameClashAdapter.java
new file mode 100644
index 0000000..9435698
--- /dev/null
+++ b/integration-tests/TestApp/app/src/main/java/android/databinding/testapp/adapter/NameClashAdapter.java
@@ -0,0 +1,31 @@
+/*
+ * Copyright (C) 2015 The Android Open Source Project
+ *
+ * Licensed under the Apache License, Version 2.0 (the "License");
+ * you may not use this file except in compliance with the License.
+ * You may obtain a copy of the License at
+ *
+ * http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing, software
+ * distributed under the License is distributed on an "AS IS" BASIS,
+ * WITHOUT 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.adapter;
+
+import android.databinding.BindingAdapter;
+import android.view.View;
+
+public class NameClashAdapter {
+ @BindingAdapter("gabble-babble")
+ public void setGabbleBabble(View view, String value) {
+ }
+
+ public static class MyAdapter {
+ @BindingAdapter({"gabble-babble-flabble", "booble-beeble-bee"})
+ public void setGabbleBabbleFlabble(View view, String value, String value2) {
+ }
+ }
+}
diff --git a/integration-tests/TestApp/app/src/main/java/android/databinding/testapp/adapter/WeirdListeners.java b/integration-tests/TestApp/app/src/main/java/android/databinding/testapp/adapter/WeirdListeners.java
index 5f7bb5d..df20022 100644
--- a/integration-tests/TestApp/app/src/main/java/android/databinding/testapp/adapter/WeirdListeners.java
+++ b/integration-tests/TestApp/app/src/main/java/android/databinding/testapp/adapter/WeirdListeners.java
@@ -27,6 +27,17 @@
@BindingAdapter("android:onFoo2")
public static void setListener(View view, OnFoo2 onFoo) {}
+ @BindingAdapter("android:onBar1")
+ public static void setListener(View view, OnBar1 onBar) {}
+
+ @BindingAdapter("android:onBar2")
+ public static void setListener(View view, OnBar2 onBar) {}
+
+ @BindingAdapter({"runnable", "fooId", "barId"})
+ public static void setRunnable(View view, Runnable runnable, int foo, int bar) {
+ runnable.run();
+ }
+
@TargetApi(VERSION_CODES.ICE_CREAM_SANDWICH)
public static abstract class OnFoo {
public abstract void onFoo();
@@ -37,4 +48,14 @@
public interface OnFoo2 {
void onFoo();
}
+
+ @TargetApi(VERSION_CODES.ICE_CREAM_SANDWICH)
+ public interface OnBar1 {
+ void onBar();
+ }
+
+ @TargetApi(VERSION_CODES.GINGERBREAD)
+ public interface OnBar2 {
+ boolean onBar(View view);
+ }
}
diff --git a/integration-tests/TestApp/app/src/main/java/android/databinding/testapp/adapter2/NameClashAdapter.java b/integration-tests/TestApp/app/src/main/java/android/databinding/testapp/adapter2/NameClashAdapter.java
new file mode 100644
index 0000000..53eb77b
--- /dev/null
+++ b/integration-tests/TestApp/app/src/main/java/android/databinding/testapp/adapter2/NameClashAdapter.java
@@ -0,0 +1,31 @@
+/*
+ * Copyright (C) 2015 The Android Open Source Project
+ *
+ * Licensed under the Apache License, Version 2.0 (the "License");
+ * you may not use this file except in compliance with the License.
+ * You may obtain a copy of the License at
+ *
+ * http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing, software
+ * distributed under the License is distributed on an "AS IS" BASIS,
+ * WITHOUT 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.adapter2;
+
+import android.databinding.BindingAdapter;
+import android.view.View;
+
+public class NameClashAdapter {
+ @BindingAdapter("gooble-flooble")
+ public void setGoogleFlooble(View view, String value) {
+ }
+
+ public static class MyAdapter {
+ @BindingAdapter({"gooble-flooble-booble", "rumple-bumple-bum"})
+ public void setGoogleFloobleBooble(View view, String value, String value2) {
+ }
+ }
+}
diff --git a/integration-tests/TestApp/app/src/main/java/android/databinding/testapp/vo/AbsSpinnerBindingObject.java b/integration-tests/TestApp/app/src/main/java/android/databinding/testapp/vo/AbsSpinnerBindingObject.java
index 05d7ed7..61b8a36 100644
--- a/integration-tests/TestApp/app/src/main/java/android/databinding/testapp/vo/AbsSpinnerBindingObject.java
+++ b/integration-tests/TestApp/app/src/main/java/android/databinding/testapp/vo/AbsSpinnerBindingObject.java
@@ -16,6 +16,10 @@
package android.databinding.testapp.vo;
import android.databinding.Bindable;
+import android.databinding.ObservableArrayList;
+import android.databinding.ObservableList;
+
+import java.util.List;
public class AbsSpinnerBindingObject extends BindingAdapterBindingObject {
@Bindable
@@ -30,6 +34,13 @@
"world"
};
+ private ObservableList<String> mList = new ObservableArrayList<String>();
+
+ public AbsSpinnerBindingObject() {
+ mList.add("Hello");
+ mList.add("World");
+ }
+
public CharSequence[] getEntries() {
return mEntries;
}
@@ -38,4 +49,8 @@
mEntries = CHANGED_VALUES;
notifyChange();
}
+
+ public List<String> getList() {
+ return mList;
+ }
}
diff --git a/integration-tests/TestApp/app/src/main/java/android/databinding/testapp/vo/BasicObject.java b/integration-tests/TestApp/app/src/main/java/android/databinding/testapp/vo/BasicObject.java
index 450f7fb..5f332ef 100644
--- a/integration-tests/TestApp/app/src/main/java/android/databinding/testapp/vo/BasicObject.java
+++ b/integration-tests/TestApp/app/src/main/java/android/databinding/testapp/vo/BasicObject.java
@@ -42,4 +42,19 @@
this.mField2 = field2;
notifyPropertyChanged(BR.field1);
}
+
+ @Bindable
+ public boolean isThisNameDoesNotMatchAnythingElse1() {
+ // see: https://code.google.com/p/android/issues/detail?id=190207
+ return false;
+ }
+
+ @Bindable
+ public boolean getThisNameDoesNotMatchAnythingElse2() {
+ return false;
+ }
+
+ public String boolMethod(boolean value) {
+ return value ? "true" : "false";
+ }
}
diff --git a/developmentPlugins/bintrayPlugin/build.gradle b/integration-tests/TestApp/app/src/main/java/android/databinding/testapp/vo/BracketObject.java
similarity index 63%
copy from developmentPlugins/bintrayPlugin/build.gradle
copy to integration-tests/TestApp/app/src/main/java/android/databinding/testapp/vo/BracketObject.java
index 364a5bb..aa6187f 100644
--- a/developmentPlugins/bintrayPlugin/build.gradle
+++ b/integration-tests/TestApp/app/src/main/java/android/databinding/testapp/vo/BracketObject.java
@@ -13,22 +13,10 @@
* See the License for the specific language governing permissions and
* limitations under the License.
*/
-apply plugin: 'groovy'
+package android.databinding.testapp.vo;
-repositories {
- mavenCentral()
+import java.util.HashMap;
+
+public class BracketObject {
+ public HashMap<String, String> map = new HashMap<>();
}
-
-dependencies {
- compile gradleApi()
- testCompile group: 'junit', name: 'junit', version: '4.12'
-}
-
-uploadArchives {
- repositories {
- mavenDeployer {
- pom.artifactId = 'bintray'
- repository(url: "file://${config.extraPluginsRepoDir}")
- }
- }
-}
\ No newline at end of file
diff --git a/integration-tests/TestApp/app/src/main/java/android/databinding/testapp/vo/ConditionalVo.java b/integration-tests/TestApp/app/src/main/java/android/databinding/testapp/vo/ConditionalVo.java
new file mode 100644
index 0000000..22ecb05
--- /dev/null
+++ b/integration-tests/TestApp/app/src/main/java/android/databinding/testapp/vo/ConditionalVo.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.BaseObservable;
+import android.databinding.Bindable;
+import android.databinding.testapp.BR;
+import android.view.View;
+
+public class ConditionalVo extends BaseObservable {
+ private boolean useHello;
+ public String text = "Hello World";
+ public boolean wasClicked;
+
+ @Bindable
+ public boolean getUseHello() {
+ return useHello;
+ }
+
+ public void setUseHello(boolean useHello) {
+ this.useHello = useHello;
+ notifyPropertyChanged(BR.useHello);
+ }
+
+ public void clicked(View view) {
+ wasClicked = true;
+ }
+}
diff --git a/integration-tests/TestApp/app/src/main/java/android/databinding/testapp/vo/FindFieldBindingObject.java b/integration-tests/TestApp/app/src/main/java/android/databinding/testapp/vo/FindFieldBindingObject.java
index 32d7b0e..d0e0043 100644
--- a/integration-tests/TestApp/app/src/main/java/android/databinding/testapp/vo/FindFieldBindingObject.java
+++ b/integration-tests/TestApp/app/src/main/java/android/databinding/testapp/vo/FindFieldBindingObject.java
@@ -18,4 +18,8 @@
public class FindFieldBindingObject {
public String mPublicField;
+
+ public static class Inner<T> {
+ public static final String HELLO = "Hello";
+ }
}
diff --git a/integration-tests/TestApp/app/src/main/java/android/databinding/testapp/vo/ListenerBindingObject.java b/integration-tests/TestApp/app/src/main/java/android/databinding/testapp/vo/ListenerBindingObject.java
index cc0c42c..94a71a0 100644
--- a/integration-tests/TestApp/app/src/main/java/android/databinding/testapp/vo/ListenerBindingObject.java
+++ b/integration-tests/TestApp/app/src/main/java/android/databinding/testapp/vo/ListenerBindingObject.java
@@ -16,11 +16,11 @@
package android.databinding.testapp.vo;
import android.content.Context;
+import android.databinding.BaseObservable;
import android.databinding.ObservableBoolean;
import android.graphics.Outline;
import android.media.MediaPlayer;
import android.text.Editable;
-import android.view.ActionMode;
import android.view.ContextMenu;
import android.view.DragEvent;
import android.view.KeyEvent;
@@ -47,6 +47,7 @@
public static int lastClick = 0;
public boolean inflateCalled;
private final Context mContext;
+ public boolean wasRunnableRun;
public final ObservableBoolean clickable = new ObservableBoolean();
public final ObservableBoolean useOne = new ObservableBoolean();
@@ -247,7 +248,23 @@
lastClick = 4;
}
+ public void runnableRun() {
+ this.wasRunnableRun = true;
+ }
+
public void onFoo() {
}
+ public void onBar() {}
+
+ public boolean onBar(View view) {
+ return true;
+ }
+
+ public static class Inner extends BaseObservable {
+ public boolean clicked;
+ public void onClick(View view) {
+ clicked = true;
+ }
+ }
}
diff --git a/integration-tests/TestApp/app/src/main/res/color/text_colors.xml b/integration-tests/TestApp/app/src/main/res/color/text_colors.xml
new file mode 100644
index 0000000..0043a02
--- /dev/null
+++ b/integration-tests/TestApp/app/src/main/res/color/text_colors.xml
@@ -0,0 +1,12 @@
+<?xml version="1.0" encoding="utf-8"?>
+<selector xmlns:android="http://schemas.android.com/apk/res/android">
+ <item
+ android:color="?android:selectedWeekBackgroundColor"
+ android:state_pressed="false"
+ android:state_focused="false"
+ android:state_selected="false"
+ android:state_checkable="false"
+ android:state_checked="false"
+ android:state_enabled="false"
+ android:state_window_focused="false"/>
+</selector>
\ No newline at end of file
diff --git a/integration-tests/TestApp/app/src/main/res/drawable/circle.xml b/integration-tests/TestApp/app/src/main/res/drawable/circle.xml
new file mode 100644
index 0000000..e325ab3
--- /dev/null
+++ b/integration-tests/TestApp/app/src/main/res/drawable/circle.xml
@@ -0,0 +1,6 @@
+<?xml version="1.0" encoding="utf-8"?>
+<shape xmlns:android="http://schemas.android.com/apk/res/android"
+ android:shape="oval">
+ <solid
+ android:color="?android:selectedWeekBackgroundColor"/>
+</shape>
\ No newline at end of file
diff --git a/integration-tests/TestApp/app/src/main/res/layout-land/multi_res_layout.xml b/integration-tests/TestApp/app/src/main/res/layout-land/multi_res_layout.xml
index bb2f58d..2b47694 100644
--- a/integration-tests/TestApp/app/src/main/res/layout-land/multi_res_layout.xml
+++ b/integration-tests/TestApp/app/src/main/res/layout-land/multi_res_layout.xml
@@ -14,12 +14,9 @@
<layout xmlns:android="http://schemas.android.com/apk/res/android"
xmlns:bind="http://schemas.android.com/apk/res-auto">
-
-
-
-
<data>
<variable name="objectInLand" type="android.databinding.testapp.vo.NotBindableVo"/>
+ <variable name="objectInBoth" type="Object"/>
</data>
<LinearLayout
android:orientation="vertical"
diff --git a/integration-tests/TestApp/app/src/main/res/layout/abs_spinner_adapter_test.xml b/integration-tests/TestApp/app/src/main/res/layout/abs_spinner_adapter_test.xml
index e24dfa3..579d4fe 100644
--- a/integration-tests/TestApp/app/src/main/res/layout/abs_spinner_adapter_test.xml
+++ b/integration-tests/TestApp/app/src/main/res/layout/abs_spinner_adapter_test.xml
@@ -22,5 +22,8 @@
<Spinner android:layout_width="match_parent" android:layout_height="match_parent"
android:id="@+id/view"
android:entries="@{obj.entries}"/>
+ <Spinner android:layout_width="match_parent" android:layout_height="match_parent"
+ android:id="@+id/view2"
+ android:entries="@{obj.list}"/>
</LinearLayout>
</layout>
diff --git a/integration-tests/TestApp/app/src/main/res/layout/auto_context.xml b/integration-tests/TestApp/app/src/main/res/layout/auto_context.xml
new file mode 100644
index 0000000..7098061
--- /dev/null
+++ b/integration-tests/TestApp/app/src/main/res/layout/auto_context.xml
@@ -0,0 +1,28 @@
+<?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.
+ -->
+<layout xmlns:android="http://schemas.android.com/apk/res/android"
+ xmlns:app="http://schemas.android.com/apk/res-auto">
+ <data>
+ <import type="android.text.format.DateUtils"/>
+ </data>
+ <LinearLayout
+ android:orientation="vertical"
+ android:layout_width="match_parent"
+ android:layout_height="match_parent">
+ <TextView
+ android:id="@+id/textView1"
+ android:layout_width="wrap_content" android:layout_height="wrap_content"
+ android:text="@{DateUtils.formatDateTime(context, 0, 0)}"/>
+ </LinearLayout>
+</layout>
\ No newline at end of file
diff --git a/integration-tests/TestApp/app/src/main/res/layout/bracket_test.xml b/integration-tests/TestApp/app/src/main/res/layout/bracket_test.xml
index 141ddb4..e9a0e2f 100644
--- a/integration-tests/TestApp/app/src/main/res/layout/bracket_test.xml
+++ b/integration-tests/TestApp/app/src/main/res/layout/bracket_test.xml
@@ -19,6 +19,9 @@
<variable name="sparseIntArray" type="android.util.SparseIntArray"/>
<variable name="sparseLongArray" type="android.util.SparseLongArray"/>
<variable name="longSparseArray" type="android.util.LongSparseArray<String>"/>
+ <variable name="index" type="int"/>
+ <variable name="indexObj" type="Object"/>
+ <variable name="obj" type="android.databinding.testapp.vo.BracketObject"/>
</data>
<LinearLayout
android:layout_width="match_parent"
@@ -28,32 +31,46 @@
<TextView android:layout_width="wrap_content"
android:layout_height="wrap_content"
android:id="@+id/arrayText"
- android:text="@{array[0]}"/>
+ android:text="@{array[index]}"/>
+
+ <TextView android:layout_width="wrap_content"
+ android:layout_height="wrap_content"
+ android:id="@+id/indexObj"
+ android:text="@{array[indexObj]}"/>
<TextView android:layout_width="wrap_content"
android:layout_height="wrap_content"
android:id="@+id/sparseArrayText"
- android:text='@{sparseArray[0]}'/>
+ android:text='@{sparseArray[index]}'/>
+
+ <TextView android:layout_width="wrap_content"
+ android:layout_height="wrap_content"
+ android:id="@+id/sparseArrayTextObj"
+ android:text='@{sparseArray[indexObj]}'/>
<TextView android:layout_width="wrap_content"
android:layout_height="wrap_content"
android:id="@+id/sparseBooleanArrayText"
- android:text='@{"" + sparseBooleanArray[0]}'/>
+ android:text='@{"" + sparseBooleanArray[index]}'/>
<TextView android:layout_width="wrap_content"
android:layout_height="wrap_content"
android:id="@+id/sparseIntArrayText"
- android:text='@{"" + sparseIntArray[0]}'/>
+ android:text='@{"" + sparseIntArray[index]}'/>
<TextView android:layout_width="wrap_content"
android:layout_height="wrap_content"
android:id="@+id/sparseLongArrayText"
- android:text='@{"" + sparseLongArray[0]}'/>
+ android:text='@{"" + sparseLongArray[index]}'/>
<TextView android:layout_width="wrap_content"
android:layout_height="wrap_content"
android:id="@+id/longSparseArrayText"
- android:text='@{longSparseArray[0]}'/>
+ android:text='@{longSparseArray[index]}'/>
+ <TextView android:layout_width="wrap_content"
+ android:layout_height="wrap_content"
+ android:id="@+id/bracketMap"
+ android:text='@{obj.map["Hello"]}'/>
</LinearLayout>
</layout>
diff --git a/integration-tests/TestApp/app/src/main/res/layout/conditional_binding.xml b/integration-tests/TestApp/app/src/main/res/layout/conditional_binding.xml
index dabed89..675c37c 100644
--- a/integration-tests/TestApp/app/src/main/res/layout/conditional_binding.xml
+++ b/integration-tests/TestApp/app/src/main/res/layout/conditional_binding.xml
@@ -16,6 +16,7 @@
<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="obj4" type="android.databinding.testapp.vo.ConditionalVo"/>
<variable name="cond1" type="boolean"/>
<variable name="cond2" type="boolean"/>
</data>
@@ -28,5 +29,20 @@
android:text="@{cond1 ? cond2 ? obj1.stringValue : obj2.stringValue : obj3.stringValue}"
android:layout_width="wrap_content"
android:layout_height="wrap_content"/>
+ <TextView
+ android:id="@+id/textView2"
+ android:layout_width="wrap_content"
+ android:layout_height="wrap_content"
+ android:text="@{obj4.text}"/>
+ <TextView
+ android:id="@+id/textView1"
+ android:layout_width="wrap_content"
+ android:layout_height="wrap_content"
+ android:text="@{obj4.useHello ? obj4.text : `hello`}"/>
+ <View
+ android:id="@+id/view1"
+ android:layout_width="wrap_content"
+ android:layout_height="wrap_content"
+ android:onClick="@{cond1 ? obj4.clicked : null}"/>
</LinearLayout>
</layout>
\ No newline at end of file
diff --git a/integration-tests/TestApp/app/src/main/res/layout/const_expression_test.xml b/integration-tests/TestApp/app/src/main/res/layout/const_expression_test.xml
new file mode 100644
index 0000000..fa13e22
--- /dev/null
+++ b/integration-tests/TestApp/app/src/main/res/layout/const_expression_test.xml
@@ -0,0 +1,31 @@
+<?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.
+ -->
+<layout xmlns:android="http://schemas.android.com/apk/res/android">
+ <data>
+ <variable name="var1" type="int"/>
+ <variable name="var2" type="int"/>
+ </data>
+ <LinearLayout
+ android:orientation="vertical"
+ android:layout_width="match_parent"
+ android:layout_height="match_parent">
+ <TextView
+ android:id="@+id/textView"
+ android:text='@{true ? "" + var1 : "" + var2}'
+ android:layout_width="wrap_content"
+ android:layout_height="match_parent"/>
+ </LinearLayout>
+ <!--android:textIsSelectable="@{var1 > 1 ? true ? true : false : false}"-->
+
+</layout>
\ No newline at end of file
diff --git a/integration-tests/TestApp/app/src/main/res/layout/converters.xml b/integration-tests/TestApp/app/src/main/res/layout/converters.xml
new file mode 100644
index 0000000..f133819
--- /dev/null
+++ b/integration-tests/TestApp/app/src/main/res/layout/converters.xml
@@ -0,0 +1,33 @@
+<?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.
+ -->
+<layout xmlns:android="http://schemas.android.com/apk/res/android">
+ <data>
+ <variable name="list" type="java.util.ArrayList<String>"/>
+ <variable name="linked" type="java.util.LinkedList<String>"/>
+ </data>
+ <LinearLayout
+ android:orientation="vertical"
+ android:layout_width="match_parent"
+ android:layout_height="match_parent">
+
+ <TextView
+ android:id="@+id/textView1"
+ android:layout_width="wrap_content" android:layout_height="wrap_content"
+ android:text="@{list}"/>
+ <TextView
+ android:id="@+id/textView2"
+ android:layout_width="wrap_content" android:layout_height="wrap_content"
+ android:text="@{linked}"/>
+ </LinearLayout>
+</layout>
\ No newline at end of file
diff --git a/integration-tests/TestApp/app/src/main/res/layout/duplicate_context.xml b/integration-tests/TestApp/app/src/main/res/layout/duplicate_context.xml
new file mode 100644
index 0000000..716b80f
--- /dev/null
+++ b/integration-tests/TestApp/app/src/main/res/layout/duplicate_context.xml
@@ -0,0 +1,29 @@
+<?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.
+ -->
+<layout xmlns:android="http://schemas.android.com/apk/res/android"
+ xmlns:app="http://schemas.android.com/apk/res-auto">
+ <data>
+ <variable name="context" type="String"/>
+ </data>
+ <LinearLayout
+ android:orientation="vertical"
+ android:layout_width="match_parent"
+ android:layout_height="match_parent">
+ <TextView
+ android:id="@+id/textView1"
+ android:layout_width="wrap_content" android:layout_height="wrap_content"
+ android:text="@{context}"/>
+
+ </LinearLayout>
+</layout>
\ No newline at end of file
diff --git a/integration-tests/TestApp/app/src/main/res/layout/expression_test.xml b/integration-tests/TestApp/app/src/main/res/layout/expression_test.xml
index 063f771..5858b81 100644
--- a/integration-tests/TestApp/app/src/main/res/layout/expression_test.xml
+++ b/integration-tests/TestApp/app/src/main/res/layout/expression_test.xml
@@ -101,5 +101,20 @@
android:id="@+id/textView18"
android:layout_width="wrap_content" android:layout_height="wrap_content"
android:text="@{bool1 ? @string/app_name : bool2 ? @string/rain : null}"/>
+ <TextView
+ android:id="@+id/textView19"
+ android:tag="@{bool1 ? `foo` : `bar`}"
+ android:layout_width="wrap_content"
+ android:layout_height="match_parent"/>
+ <TextView
+ android:id="@+id/textView20"
+ android:text='@{@bool/alwaysTrue ? "" + var1 : "" + var2}'
+ android:layout_width="wrap_content"
+ android:layout_height="match_parent"/>
+ <TextView
+ android:id="@+id/textView21"
+ android:text='@{@bool/alwaysFalse ? "" + var1 : "" + var2}'
+ android:layout_width="wrap_content"
+ android:layout_height="match_parent"/>
</LinearLayout>
</layout>
\ No newline at end of file
diff --git a/integration-tests/TestApp/app/src/main/res/layout/find_field_test.xml b/integration-tests/TestApp/app/src/main/res/layout/find_field_test.xml
index f9a9f7c..7c3e2e0 100644
--- a/integration-tests/TestApp/app/src/main/res/layout/find_field_test.xml
+++ b/integration-tests/TestApp/app/src/main/res/layout/find_field_test.xml
@@ -13,6 +13,7 @@
-->
<layout xmlns:android="http://schemas.android.com/apk/res/android">
<data>
+ <import type="android.databinding.testapp.vo.FindFieldBindingObject.Inner"/>
<variable name="obj" type="android.databinding.testapp.vo.FindFieldBindingObject"/>
</data>
<LinearLayout
@@ -23,5 +24,10 @@
android:text="@{obj.mPublicField}"
android:layout_width="wrap_content"
android:layout_height="wrap_content" />
+ <TextView
+ android:id="@+id/textView2"
+ android:text="@{Inner.HELLO}"
+ android:layout_width="wrap_content"
+ android:layout_height="wrap_content" />
</LinearLayout>
</layout>
diff --git a/integration-tests/TestApp/app/src/main/res/layout/find_method_test.xml b/integration-tests/TestApp/app/src/main/res/layout/find_method_test.xml
index 3755b73..509517f 100644
--- a/integration-tests/TestApp/app/src/main/res/layout/find_method_test.xml
+++ b/integration-tests/TestApp/app/src/main/res/layout/find_method_test.xml
@@ -11,7 +11,8 @@
~ See the License for the specific language governing permissions and
~ limitations under the License.
-->
-<layout xmlns:android="http://schemas.android.com/apk/res/android">
+<layout xmlns:android="http://schemas.android.com/apk/res/android"
+ xmlns:app="http://schemas.android.com/apk/res-auto">
<data>
<variable name="obj" type="android.databinding.testapp.vo.FindMethodBindingObject"/>
<import type="android.databinding.testapp.vo.FindMethodBindingObject.Bar"/>
@@ -134,5 +135,9 @@
android:id="@+id/textView26"
android:layout_width="wrap_content" android:layout_height="wrap_content"
android:text="@{obj.observableClass.x}"/>
+ <TextView
+ android:id="@+id/textView27"
+ android:layout_width="wrap_content" android:layout_height="wrap_content"
+ app:tag="@{1}"/>
</LinearLayout>
</layout>
\ No newline at end of file
diff --git a/integration-tests/TestApp/app/src/main/res/layout/fragment_main.xml b/integration-tests/TestApp/app/src/main/res/layout/fragment_main.xml
new file mode 100644
index 0000000..e2fbf7c
--- /dev/null
+++ b/integration-tests/TestApp/app/src/main/res/layout/fragment_main.xml
@@ -0,0 +1,9 @@
+<?xml version="1.0" encoding="utf-8"?>
+<layout xmlns:android="http://schemas.android.com/apk/res/android">
+ <fragment
+ android:id="@+id/fragment"
+ android:name="android.databinding.testapp.TestFragment"
+ android:layout_height="match_parent"
+ android:layout_width="match_parent"
+ />
+</layout>
\ No newline at end of file
diff --git a/integration-tests/TestApp/app/src/main/res/layout/image_view_adapter_test.xml b/integration-tests/TestApp/app/src/main/res/layout/image_view_adapter_test.xml
index b459dda..fb79249 100644
--- a/integration-tests/TestApp/app/src/main/res/layout/image_view_adapter_test.xml
+++ b/integration-tests/TestApp/app/src/main/res/layout/image_view_adapter_test.xml
@@ -38,5 +38,10 @@
android:id="@+id/view3"
android:src="@{uriString}"
/>
+ <ImageView
+ android:layout_width="match_parent" android:layout_height="match_parent"
+ android:id="@+id/view4"
+ android:src="@{obj == null ? @drawable/ic_launcher : null}"
+ />
</LinearLayout>
</layout>
\ No newline at end of file
diff --git a/integration-tests/TestApp/app/src/main/res/layout/include_no_variables.xml b/integration-tests/TestApp/app/src/main/res/layout/include_no_variables.xml
new file mode 100644
index 0000000..f0db2a3
--- /dev/null
+++ b/integration-tests/TestApp/app/src/main/res/layout/include_no_variables.xml
@@ -0,0 +1,7 @@
+<?xml version="1.0" encoding="utf-8"?>
+<layout xmlns:android="http://schemas.android.com/apk/res/android">
+ <FrameLayout android:layout_width="match_parent" android:layout_height="match_parent">
+ <include android:id="@+id/included" layout="@layout/no_variables"/>
+ <include layout="@layout/no_variables"/>
+ </FrameLayout>
+</layout>
\ No newline at end of file
diff --git a/integration-tests/TestApp/app/src/main/res/layout/layout_with_include.xml b/integration-tests/TestApp/app/src/main/res/layout/layout_with_include.xml
index 292db4f..06156f6 100644
--- a/integration-tests/TestApp/app/src/main/res/layout/layout_with_include.xml
+++ b/integration-tests/TestApp/app/src/main/res/layout/layout_with_include.xml
@@ -49,6 +49,12 @@
android:id="@+id/thirdMerge"
bind:innerObject="@{outerObject}"
bind:innerValue="@{`third ` + outerObject.intValue}"/>
+
+ <FrameLayout android:layout_width="wrap_content"
+ android:layout_height="wrap_content">
+ <!-- an included layout with just an id -->
+ <include android:id="@+id/includedPlainLayout" layout="@layout/plain_layout"/>
+ </FrameLayout>
</LinearLayout>
</FrameLayout>
</layout>
\ No newline at end of file
diff --git a/integration-tests/TestApp/app/src/main/res/layout/listeners.xml b/integration-tests/TestApp/app/src/main/res/layout/listeners.xml
index 2922177..00a26ff 100644
--- a/integration-tests/TestApp/app/src/main/res/layout/listeners.xml
+++ b/integration-tests/TestApp/app/src/main/res/layout/listeners.xml
@@ -12,9 +12,11 @@
~ limitations under the License.
-->
-<layout xmlns:android="http://schemas.android.com/apk/res/android">
+<layout xmlns:android="http://schemas.android.com/apk/res/android"
+ xmlns:app="http://schemas.android.com/apk/res-auto">
<data>
<variable name="obj" type="android.databinding.testapp.vo.ListenerBindingObject"/>
+ <variable name="obj2" type="android.databinding.testapp.vo.ListenerBindingObject.Inner"/>
<import type="android.databinding.testapp.vo.ListenerBindingObject"/>
</data>
<LinearLayout
@@ -317,5 +319,23 @@
android:onFoo="@{obj.onFoo}"
android:onFoo2="@{obj.onFoo}"
/>
+ <View android:id="@+id/listener2"
+ android:layout_width="10dp"
+ android:layout_height="10dp"
+ android:onBar1="@{obj.onBar}"
+ android:onBar2="@{obj.onBar}"
+ />
+ <View android:id="@+id/click6"
+ android:layout_width="10dp"
+ android:layout_height="10dp"
+ android:onClick="@{obj2.onClick}"
+ />
+ <View android:id="@+id/click7"
+ android:layout_width="10dp"
+ android:layout_height="10dp"
+ app:fooId="@{1}"
+ app:barId="@{2}"
+ app:runnable="@{obj.runnableRun}"
+ />
</LinearLayout>
</layout>
\ No newline at end of file
diff --git a/integration-tests/TestApp/app/src/main/res/layout/multi_res_layout.xml b/integration-tests/TestApp/app/src/main/res/layout/multi_res_layout.xml
index 41031a7..0a80250 100644
--- a/integration-tests/TestApp/app/src/main/res/layout/multi_res_layout.xml
+++ b/integration-tests/TestApp/app/src/main/res/layout/multi_res_layout.xml
@@ -16,6 +16,7 @@
xmlns:bind="http://schemas.android.com/apk/res-auto">
<data>
<variable name="objectInDefault" type="android.databinding.testapp.vo.NotBindableVo"/>
+ <variable name="objectInBoth" type="Object"/>
</data>
<LinearLayout
android:orientation="vertical"
diff --git a/integration-tests/TestApp/app/src/main/res/layout/name_mapping_test.xml b/integration-tests/TestApp/app/src/main/res/layout/name_mapping_test.xml
new file mode 100644
index 0000000..1ed651b
--- /dev/null
+++ b/integration-tests/TestApp/app/src/main/res/layout/name_mapping_test.xml
@@ -0,0 +1,24 @@
+<?xml version="1.0" encoding="utf-8"?>
+<layout>
+ <data>
+ <variable name="obj" type="android.databinding.testapp.vo.BasicObject"/>
+ </data>
+ <LinearLayout xmlns:android="http://schemas.android.com/apk/res/android"
+ android:orientation="vertical"
+ android:layout_width="match_parent"
+ android:layout_height="match_parent">
+ <TextView android:layout_width="wrap_content" android:layout_height="wrap_content"
+ android:id="@+id/textView"
+ android:enabled="@{obj.getThisNameDoesNotMatchAnythingElse2}"
+ android:focusable="@{obj.isThisNameDoesNotMatchAnythingElse1}"/>
+ <TextView android:layout_width="wrap_content" android:layout_height="wrap_content"
+ android:id="@+id/textView2"
+ android:enabled="@{obj.thisNameDoesNotMatchAnythingElse2}"
+ android:focusable="@{obj.thisNameDoesNotMatchAnythingElse1}"/>
+ <TextView android:layout_width="wrap_content" android:layout_height="wrap_content"
+ android:id="@+id/textView3"
+ android:enabled="@{obj.getThisNameDoesNotMatchAnythingElse2()}"
+ android:focusable="@{obj.isThisNameDoesNotMatchAnythingElse1()}"/>
+
+ </LinearLayout>
+</layout>
\ No newline at end of file
diff --git a/integration-tests/TestApp/app/src/main/res/layout/text_view_adapter_test.xml b/integration-tests/TestApp/app/src/main/res/layout/text_view_adapter_test.xml
index a37031b..43b11da 100644
--- a/integration-tests/TestApp/app/src/main/res/layout/text_view_adapter_test.xml
+++ b/integration-tests/TestApp/app/src/main/res/layout/text_view_adapter_test.xml
@@ -80,6 +80,13 @@
android:id="@+id/textInputMethod"
android:inputMethod="@{obj.inputMethod}"
/>
-
+ <TextView android:layout_width="match_parent" android:layout_height="match_parent"
+ android:id="@+id/textWithTheme"
+ android:textColor="@{@colorStateList/text_colors}"
+ />
+ <TextView android:layout_width="match_parent" android:layout_height="match_parent"
+ android:id="@+id/textWithColor"
+ android:textColor="@{@android:color/holo_blue_bright}"
+ />
</LinearLayout>
</layout>
\ No newline at end of file
diff --git a/integration-tests/TestApp/app/src/main/res/layout/unnecessary_calculation.xml b/integration-tests/TestApp/app/src/main/res/layout/unnecessary_calculation.xml
new file mode 100644
index 0000000..0410189
--- /dev/null
+++ b/integration-tests/TestApp/app/src/main/res/layout/unnecessary_calculation.xml
@@ -0,0 +1,39 @@
+<?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.
+ -->
+
+<layout xmlns:android="http://schemas.android.com/apk/res/android">
+ <data>
+ <variable name="obja" type="android.databinding.testapp.vo.BasicObject"/>
+ <variable name="objb" type="android.databinding.testapp.vo.BasicObject"/>
+ <variable name="objc" type="android.databinding.testapp.vo.BasicObject"/>
+ <variable name="a" type="boolean"/>
+ <variable name="b" type="boolean"/>
+ <variable name="c" type="boolean"/>
+ </data>
+ <LinearLayout
+ android:orientation="vertical"
+ android:layout_width="match_parent"
+ android:layout_height="match_parent">
+ <TextView
+ android:id="@+id/textView"
+ android:text="@{obja.boolMethod(a)}"
+ android:layout_width="wrap_content"
+ android:layout_height="wrap_content"/>
+ <android.databinding.testapp.view.MyTextView
+ android:id="@+id/textView2"
+ android:text="@{a ? objb.boolMethod(b) : objc.boolMethod(c)}"
+ android:layout_width="wrap_content"
+ android:layout_height="wrap_content"/>
+ </LinearLayout>
+</layout>
\ No newline at end of file
diff --git a/integration-tests/TestApp/app/src/main/res/layout/view_adapter_test.xml b/integration-tests/TestApp/app/src/main/res/layout/view_adapter_test.xml
index a3d40ff..ce3b895 100644
--- a/integration-tests/TestApp/app/src/main/res/layout/view_adapter_test.xml
+++ b/integration-tests/TestApp/app/src/main/res/layout/view_adapter_test.xml
@@ -28,6 +28,12 @@
android:padding="@{obj.padding}"
/>
<View
+ android:id="@+id/paddingFloat"
+ android:layout_width="10dp"
+ android:layout_height="10dp"
+ android:padding="@{@dimen/testPadding}"
+ />
+ <View
android:id="@+id/paddingStartEnd"
android:layout_width="10dp"
android:layout_height="10dp"
@@ -35,6 +41,15 @@
android:paddingStart="@{obj.paddingStart}"
/>
<View
+ android:id="@+id/paddingStartEndFloat"
+ android:layout_width="10dp"
+ android:layout_height="10dp"
+ android:paddingTop="@{@dimen/testPadding}"
+ android:paddingBottom="@{@dimen/testPadding}"
+ android:paddingEnd="@{@dimen/testPadding}"
+ android:paddingStart="@{@dimen/testPadding}"
+ />
+ <View
android:id="@+id/paddingTopBottom"
android:layout_width="10dp"
android:layout_height="10dp"
@@ -49,6 +64,15 @@
android:paddingRight="@{obj.paddingRight}"
/>
<View
+ android:id="@+id/paddingLeftRightFloat"
+ android:layout_width="10dp"
+ android:layout_height="10dp"
+ android:paddingTop="@{@dimen/testPadding}"
+ android:paddingBottom="@{@dimen/testPadding}"
+ android:paddingLeft="@{@dimen/testPadding}"
+ android:paddingRight="@{@dimen/testPadding}"
+ />
+ <View
android:id="@+id/backgroundTint"
android:backgroundTint="@{obj.backgroundTint}"
android:layout_width="10dp"
@@ -86,5 +110,16 @@
android:transformPivotY="@{obj.transformPivotY}"
android:layout_width="10dp"
android:layout_height="10dp"/>
+
+ <View
+ android:id="@+id/backgroundDrawable"
+ android:layout_width="10dp"
+ android:layout_height="10dp"
+ android:background="@{@drawable/ic_launcher}"/>
+ <View
+ android:id="@+id/backgroundWithTheme"
+ android:layout_width="10dp"
+ android:layout_height="10dp"
+ android:background="@{@drawable/circle}"/>
</LinearLayout>
</layout>
\ No newline at end of file
diff --git a/integration-tests/TestApp/app/src/main/res/layout/view_with_tag.xml b/integration-tests/TestApp/app/src/main/res/layout/view_with_tag.xml
index a710390..6672c99 100644
--- a/integration-tests/TestApp/app/src/main/res/layout/view_with_tag.xml
+++ b/integration-tests/TestApp/app/src/main/res/layout/view_with_tag.xml
@@ -25,6 +25,8 @@
<TextView
android:text="@{str}"
android:layout_width="wrap_content"
- android:layout_height="wrap_content" />
+ android:layout_height="wrap_content" >
+ <tag android:id="@+id/customTag" android:value="Hello"/>
+ </TextView>
</LinearLayout>
</layout>
diff --git a/integration-tests/TestApp/app/src/main/res/values/booleans.xml b/integration-tests/TestApp/app/src/main/res/values/booleans.xml
new file mode 100644
index 0000000..5cf5a37
--- /dev/null
+++ b/integration-tests/TestApp/app/src/main/res/values/booleans.xml
@@ -0,0 +1,5 @@
+<?xml version="1.0" encoding="utf-8"?>
+<resources>
+ <bool name="alwaysTrue">true</bool>
+ <bool name="alwaysFalse">false</bool>
+</resources>
\ No newline at end of file
diff --git a/integration-tests/TestApp/app/src/main/res/values/dimens.xml b/integration-tests/TestApp/app/src/main/res/values/dimens.xml
new file mode 100644
index 0000000..dbcada0
--- /dev/null
+++ b/integration-tests/TestApp/app/src/main/res/values/dimens.xml
@@ -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.
+ -->
+
+<resources>
+ <dimen name="testPadding">10dp</dimen>
+</resources>
diff --git a/integration-tests/TestApp/build.gradle b/integration-tests/TestApp/build.gradle
index c15f7f5..35f8e95 100644
--- a/integration-tests/TestApp/build.gradle
+++ b/integration-tests/TestApp/build.gradle
@@ -1,9 +1,8 @@
buildscript {
- ext.rootFolder = new File(project.projectDir, "../..")
+ ext.dataBindingRootFolder = new File(project.projectDir, "../..")
apply from: "${project.projectDir}/../../propLoader.gradle"
ext.addRepos(repositories)
dependencies {
- classpath "com.android.tools.build:gradle:${config.androidPluginVersion}"
- classpath "com.android.databinding:dataBinder:${config.version}"
+ classpath "com.android.tools.build:gradle:${dataBindingConfig.androidPluginVersion}"
}
}
diff --git a/integration-tests/TestApp/gradle/wrapper/gradle-wrapper.properties b/integration-tests/TestApp/gradle/wrapper/gradle-wrapper.properties
index 0c71e76..d570517 100644
--- a/integration-tests/TestApp/gradle/wrapper/gradle-wrapper.properties
+++ b/integration-tests/TestApp/gradle/wrapper/gradle-wrapper.properties
@@ -3,4 +3,4 @@
distributionPath=wrapper/dists
zipStoreBase=GRADLE_USER_HOME
zipStorePath=wrapper/dists
-distributionUrl=https\://services.gradle.org/distributions/gradle-2.2.1-all.zip
+distributionUrl=https\://services.gradle.org/distributions/gradle-2.10-all.zip
diff --git a/internal-prebuilts/README.md b/internal-prebuilts/README.md
new file mode 100644
index 0000000..92116c7
--- /dev/null
+++ b/internal-prebuilts/README.md
@@ -0,0 +1,6 @@
+This maven repository includes prebuilts of data binding libs that
+a) depend on android gradle plugin
+b) used druing development of data binding itself.
+
+This repository is designed to only be referenced by the Android Gradle Plugin (platform/tools).
+To access data binding prebuilds for your own app, use /platform/prebuilt/ projects.
\ No newline at end of file
diff --git a/internal-prebuilts/com/android/databinding/adapters/1.0-rc3/adapters-1.0-rc3-javadoc.jar b/internal-prebuilts/com/android/databinding/adapters/1.0-rc3/adapters-1.0-rc3-javadoc.jar
new file mode 100644
index 0000000..3c0acac
--- /dev/null
+++ b/internal-prebuilts/com/android/databinding/adapters/1.0-rc3/adapters-1.0-rc3-javadoc.jar
Binary files differ
diff --git a/internal-prebuilts/com/android/databinding/adapters/1.0-rc3/adapters-1.0-rc3-javadoc.jar.md5 b/internal-prebuilts/com/android/databinding/adapters/1.0-rc3/adapters-1.0-rc3-javadoc.jar.md5
new file mode 100644
index 0000000..01e0832
--- /dev/null
+++ b/internal-prebuilts/com/android/databinding/adapters/1.0-rc3/adapters-1.0-rc3-javadoc.jar.md5
@@ -0,0 +1 @@
+feca24e4477fba87b8593eb845cd60df
\ No newline at end of file
diff --git a/internal-prebuilts/com/android/databinding/adapters/1.0-rc3/adapters-1.0-rc3-javadoc.jar.sha1 b/internal-prebuilts/com/android/databinding/adapters/1.0-rc3/adapters-1.0-rc3-javadoc.jar.sha1
new file mode 100644
index 0000000..e02f829
--- /dev/null
+++ b/internal-prebuilts/com/android/databinding/adapters/1.0-rc3/adapters-1.0-rc3-javadoc.jar.sha1
@@ -0,0 +1 @@
+73c27a8c2ceb3283e0ff24caab4491ff10eff03d
\ No newline at end of file
diff --git a/internal-prebuilts/com/android/databinding/adapters/1.0-rc3/adapters-1.0-rc3-sources.jar b/internal-prebuilts/com/android/databinding/adapters/1.0-rc3/adapters-1.0-rc3-sources.jar
new file mode 100644
index 0000000..97ed60c
--- /dev/null
+++ b/internal-prebuilts/com/android/databinding/adapters/1.0-rc3/adapters-1.0-rc3-sources.jar
Binary files differ
diff --git a/internal-prebuilts/com/android/databinding/adapters/1.0-rc3/adapters-1.0-rc3-sources.jar.md5 b/internal-prebuilts/com/android/databinding/adapters/1.0-rc3/adapters-1.0-rc3-sources.jar.md5
new file mode 100644
index 0000000..7c9174a
--- /dev/null
+++ b/internal-prebuilts/com/android/databinding/adapters/1.0-rc3/adapters-1.0-rc3-sources.jar.md5
@@ -0,0 +1 @@
+1bbcf17edb9febc5ce467432eec6c451
\ No newline at end of file
diff --git a/internal-prebuilts/com/android/databinding/adapters/1.0-rc3/adapters-1.0-rc3-sources.jar.sha1 b/internal-prebuilts/com/android/databinding/adapters/1.0-rc3/adapters-1.0-rc3-sources.jar.sha1
new file mode 100644
index 0000000..b3e1249
--- /dev/null
+++ b/internal-prebuilts/com/android/databinding/adapters/1.0-rc3/adapters-1.0-rc3-sources.jar.sha1
@@ -0,0 +1 @@
+3dbcbe676b3b81d1b9085ced28a279bba30da2eb
\ No newline at end of file
diff --git a/internal-prebuilts/com/android/databinding/adapters/1.0-rc3/adapters-1.0-rc3.aar b/internal-prebuilts/com/android/databinding/adapters/1.0-rc3/adapters-1.0-rc3.aar
new file mode 100644
index 0000000..f646a94
--- /dev/null
+++ b/internal-prebuilts/com/android/databinding/adapters/1.0-rc3/adapters-1.0-rc3.aar
Binary files differ
diff --git a/internal-prebuilts/com/android/databinding/adapters/1.0-rc3/adapters-1.0-rc3.aar.md5 b/internal-prebuilts/com/android/databinding/adapters/1.0-rc3/adapters-1.0-rc3.aar.md5
new file mode 100644
index 0000000..dab61c0
--- /dev/null
+++ b/internal-prebuilts/com/android/databinding/adapters/1.0-rc3/adapters-1.0-rc3.aar.md5
@@ -0,0 +1 @@
+e2c580d1670caa594f617c8d1ea9b41c
\ No newline at end of file
diff --git a/internal-prebuilts/com/android/databinding/adapters/1.0-rc3/adapters-1.0-rc3.aar.sha1 b/internal-prebuilts/com/android/databinding/adapters/1.0-rc3/adapters-1.0-rc3.aar.sha1
new file mode 100644
index 0000000..38d6efc
--- /dev/null
+++ b/internal-prebuilts/com/android/databinding/adapters/1.0-rc3/adapters-1.0-rc3.aar.sha1
@@ -0,0 +1 @@
+cda5ac229c41ab2000a394eaa9e4dbf33adc8658
\ No newline at end of file
diff --git a/internal-prebuilts/com/android/databinding/adapters/1.0-rc3/adapters-1.0-rc3.pom b/internal-prebuilts/com/android/databinding/adapters/1.0-rc3/adapters-1.0-rc3.pom
new file mode 100644
index 0000000..9b99982
--- /dev/null
+++ b/internal-prebuilts/com/android/databinding/adapters/1.0-rc3/adapters-1.0-rc3.pom
@@ -0,0 +1,24 @@
+<?xml version="1.0" encoding="UTF-8"?>
+<project xsi:schemaLocation="http://maven.apache.org/POM/4.0.0 http://maven.apache.org/xsd/maven-4.0.0.xsd" xmlns="http://maven.apache.org/POM/4.0.0"
+ xmlns:xsi="http://www.w3.org/2001/XMLSchema-instance">
+ <modelVersion>4.0.0</modelVersion>
+ <groupId>com.android.databinding</groupId>
+ <artifactId>adapters</artifactId>
+ <version>1.0-rc3</version>
+ <packaging>aar</packaging>
+ <licenses>
+ <license>
+ <name>The Apache Software License, Version 2.0</name>
+ <url>http://www.apache.org/licenses/LICENSE-2.0.txt</url>
+ <distribution>repo</distribution>
+ </license>
+ </licenses>
+ <dependencies>
+ <dependency>
+ <groupId>com.android.databinding</groupId>
+ <artifactId>library</artifactId>
+ <version>1.0-rc3</version>
+ <scope>compile</scope>
+ </dependency>
+ </dependencies>
+</project>
diff --git a/internal-prebuilts/com/android/databinding/adapters/1.0-rc3/adapters-1.0-rc3.pom.md5 b/internal-prebuilts/com/android/databinding/adapters/1.0-rc3/adapters-1.0-rc3.pom.md5
new file mode 100644
index 0000000..2ef600c
--- /dev/null
+++ b/internal-prebuilts/com/android/databinding/adapters/1.0-rc3/adapters-1.0-rc3.pom.md5
@@ -0,0 +1 @@
+86843dc92dafc8aa8816088108d6175d
\ No newline at end of file
diff --git a/internal-prebuilts/com/android/databinding/adapters/1.0-rc3/adapters-1.0-rc3.pom.sha1 b/internal-prebuilts/com/android/databinding/adapters/1.0-rc3/adapters-1.0-rc3.pom.sha1
new file mode 100644
index 0000000..375c72d
--- /dev/null
+++ b/internal-prebuilts/com/android/databinding/adapters/1.0-rc3/adapters-1.0-rc3.pom.sha1
@@ -0,0 +1 @@
+22547691cf3cc9ca63b6e872a04b111964674193
\ No newline at end of file
diff --git a/internal-prebuilts/com/android/databinding/adapters/1.0-rc5/adapters-1.0-rc5-javadoc.jar b/internal-prebuilts/com/android/databinding/adapters/1.0-rc5/adapters-1.0-rc5-javadoc.jar
new file mode 100644
index 0000000..0a4baeb
--- /dev/null
+++ b/internal-prebuilts/com/android/databinding/adapters/1.0-rc5/adapters-1.0-rc5-javadoc.jar
Binary files differ
diff --git a/internal-prebuilts/com/android/databinding/adapters/1.0-rc5/adapters-1.0-rc5-javadoc.jar.md5 b/internal-prebuilts/com/android/databinding/adapters/1.0-rc5/adapters-1.0-rc5-javadoc.jar.md5
new file mode 100644
index 0000000..c61e62c
--- /dev/null
+++ b/internal-prebuilts/com/android/databinding/adapters/1.0-rc5/adapters-1.0-rc5-javadoc.jar.md5
@@ -0,0 +1 @@
+3653511ec6a902c0cb8cb1c35e83d8bf
\ No newline at end of file
diff --git a/internal-prebuilts/com/android/databinding/adapters/1.0-rc5/adapters-1.0-rc5-javadoc.jar.sha1 b/internal-prebuilts/com/android/databinding/adapters/1.0-rc5/adapters-1.0-rc5-javadoc.jar.sha1
new file mode 100644
index 0000000..5c8cbca
--- /dev/null
+++ b/internal-prebuilts/com/android/databinding/adapters/1.0-rc5/adapters-1.0-rc5-javadoc.jar.sha1
@@ -0,0 +1 @@
+9e4a7cc9ec5c09320c93d52db381a21413a88c7b
\ No newline at end of file
diff --git a/internal-prebuilts/com/android/databinding/adapters/1.0-rc5/adapters-1.0-rc5-sources.jar b/internal-prebuilts/com/android/databinding/adapters/1.0-rc5/adapters-1.0-rc5-sources.jar
new file mode 100644
index 0000000..8fda1f2
--- /dev/null
+++ b/internal-prebuilts/com/android/databinding/adapters/1.0-rc5/adapters-1.0-rc5-sources.jar
Binary files differ
diff --git a/internal-prebuilts/com/android/databinding/adapters/1.0-rc5/adapters-1.0-rc5-sources.jar.md5 b/internal-prebuilts/com/android/databinding/adapters/1.0-rc5/adapters-1.0-rc5-sources.jar.md5
new file mode 100644
index 0000000..9c6cdef
--- /dev/null
+++ b/internal-prebuilts/com/android/databinding/adapters/1.0-rc5/adapters-1.0-rc5-sources.jar.md5
@@ -0,0 +1 @@
+886d28a28d1507215a5b52de8aafe501
\ No newline at end of file
diff --git a/internal-prebuilts/com/android/databinding/adapters/1.0-rc5/adapters-1.0-rc5-sources.jar.sha1 b/internal-prebuilts/com/android/databinding/adapters/1.0-rc5/adapters-1.0-rc5-sources.jar.sha1
new file mode 100644
index 0000000..9595681
--- /dev/null
+++ b/internal-prebuilts/com/android/databinding/adapters/1.0-rc5/adapters-1.0-rc5-sources.jar.sha1
@@ -0,0 +1 @@
+8cddf09f2b4ce33b8acefbc8395a23cd09220608
\ No newline at end of file
diff --git a/internal-prebuilts/com/android/databinding/adapters/1.0-rc5/adapters-1.0-rc5.aar b/internal-prebuilts/com/android/databinding/adapters/1.0-rc5/adapters-1.0-rc5.aar
new file mode 100644
index 0000000..072d0c8
--- /dev/null
+++ b/internal-prebuilts/com/android/databinding/adapters/1.0-rc5/adapters-1.0-rc5.aar
Binary files differ
diff --git a/internal-prebuilts/com/android/databinding/adapters/1.0-rc5/adapters-1.0-rc5.aar.md5 b/internal-prebuilts/com/android/databinding/adapters/1.0-rc5/adapters-1.0-rc5.aar.md5
new file mode 100644
index 0000000..ebe5ac3
--- /dev/null
+++ b/internal-prebuilts/com/android/databinding/adapters/1.0-rc5/adapters-1.0-rc5.aar.md5
@@ -0,0 +1 @@
+15dfee32a3045fb217e8ebd14d840ffb
\ No newline at end of file
diff --git a/internal-prebuilts/com/android/databinding/adapters/1.0-rc5/adapters-1.0-rc5.aar.sha1 b/internal-prebuilts/com/android/databinding/adapters/1.0-rc5/adapters-1.0-rc5.aar.sha1
new file mode 100644
index 0000000..3ef5c33
--- /dev/null
+++ b/internal-prebuilts/com/android/databinding/adapters/1.0-rc5/adapters-1.0-rc5.aar.sha1
@@ -0,0 +1 @@
+01ac2f4916e93f32049bbc564217f51fabed2955
\ No newline at end of file
diff --git a/internal-prebuilts/com/android/databinding/adapters/1.0-rc5/adapters-1.0-rc5.pom b/internal-prebuilts/com/android/databinding/adapters/1.0-rc5/adapters-1.0-rc5.pom
new file mode 100644
index 0000000..f7824b1
--- /dev/null
+++ b/internal-prebuilts/com/android/databinding/adapters/1.0-rc5/adapters-1.0-rc5.pom
@@ -0,0 +1,30 @@
+<?xml version="1.0" encoding="UTF-8"?>
+<project xsi:schemaLocation="http://maven.apache.org/POM/4.0.0 http://maven.apache.org/xsd/maven-4.0.0.xsd" xmlns="http://maven.apache.org/POM/4.0.0"
+ xmlns:xsi="http://www.w3.org/2001/XMLSchema-instance">
+ <modelVersion>4.0.0</modelVersion>
+ <groupId>com.android.databinding</groupId>
+ <artifactId>adapters</artifactId>
+ <version>1.0-rc5</version>
+ <packaging>aar</packaging>
+ <licenses>
+ <license>
+ <name>The Apache Software License, Version 2.0</name>
+ <url>http://www.apache.org/licenses/LICENSE-2.0.txt</url>
+ <distribution>repo</distribution>
+ </license>
+ </licenses>
+ <dependencies>
+ <dependency>
+ <groupId>com.android.databinding</groupId>
+ <artifactId>library</artifactId>
+ <version>1.0-rc5</version>
+ <scope>compile</scope>
+ </dependency>
+ <dependency>
+ <groupId>com.android.databinding</groupId>
+ <artifactId>baseLibrary</artifactId>
+ <version>1.0-rc5</version>
+ <scope>compile</scope>
+ </dependency>
+ </dependencies>
+</project>
diff --git a/internal-prebuilts/com/android/databinding/adapters/1.0-rc5/adapters-1.0-rc5.pom.md5 b/internal-prebuilts/com/android/databinding/adapters/1.0-rc5/adapters-1.0-rc5.pom.md5
new file mode 100644
index 0000000..7f5e52e
--- /dev/null
+++ b/internal-prebuilts/com/android/databinding/adapters/1.0-rc5/adapters-1.0-rc5.pom.md5
@@ -0,0 +1 @@
+9550c15097a003ffd03006215e07d17c
\ No newline at end of file
diff --git a/internal-prebuilts/com/android/databinding/adapters/1.0-rc5/adapters-1.0-rc5.pom.sha1 b/internal-prebuilts/com/android/databinding/adapters/1.0-rc5/adapters-1.0-rc5.pom.sha1
new file mode 100644
index 0000000..7c06238
--- /dev/null
+++ b/internal-prebuilts/com/android/databinding/adapters/1.0-rc5/adapters-1.0-rc5.pom.sha1
@@ -0,0 +1 @@
+084728a93397ecc99af002f2c5614b4a64510cfb
\ No newline at end of file
diff --git a/internal-prebuilts/com/android/databinding/adapters/1.1/adapters-1.1-javadoc.jar b/internal-prebuilts/com/android/databinding/adapters/1.1/adapters-1.1-javadoc.jar
new file mode 100644
index 0000000..741e5b6
--- /dev/null
+++ b/internal-prebuilts/com/android/databinding/adapters/1.1/adapters-1.1-javadoc.jar
Binary files differ
diff --git a/internal-prebuilts/com/android/databinding/adapters/1.1/adapters-1.1-javadoc.jar.md5 b/internal-prebuilts/com/android/databinding/adapters/1.1/adapters-1.1-javadoc.jar.md5
new file mode 100644
index 0000000..c4ad48c
--- /dev/null
+++ b/internal-prebuilts/com/android/databinding/adapters/1.1/adapters-1.1-javadoc.jar.md5
@@ -0,0 +1 @@
+3102e4c3a8b533ecadf7a79f8cc0e6ed
\ No newline at end of file
diff --git a/internal-prebuilts/com/android/databinding/adapters/1.1/adapters-1.1-javadoc.jar.sha1 b/internal-prebuilts/com/android/databinding/adapters/1.1/adapters-1.1-javadoc.jar.sha1
new file mode 100644
index 0000000..a8506d5
--- /dev/null
+++ b/internal-prebuilts/com/android/databinding/adapters/1.1/adapters-1.1-javadoc.jar.sha1
@@ -0,0 +1 @@
+96b2b1883d2ea46bfcc65c1e6addfda97814825f
\ No newline at end of file
diff --git a/internal-prebuilts/com/android/databinding/adapters/1.1/adapters-1.1-sources.jar b/internal-prebuilts/com/android/databinding/adapters/1.1/adapters-1.1-sources.jar
new file mode 100644
index 0000000..a65eed3
--- /dev/null
+++ b/internal-prebuilts/com/android/databinding/adapters/1.1/adapters-1.1-sources.jar
Binary files differ
diff --git a/internal-prebuilts/com/android/databinding/adapters/1.1/adapters-1.1-sources.jar.md5 b/internal-prebuilts/com/android/databinding/adapters/1.1/adapters-1.1-sources.jar.md5
new file mode 100644
index 0000000..a1b3e16
--- /dev/null
+++ b/internal-prebuilts/com/android/databinding/adapters/1.1/adapters-1.1-sources.jar.md5
@@ -0,0 +1 @@
+d9447e86bde083ce29e475269f408094
\ No newline at end of file
diff --git a/internal-prebuilts/com/android/databinding/adapters/1.1/adapters-1.1-sources.jar.sha1 b/internal-prebuilts/com/android/databinding/adapters/1.1/adapters-1.1-sources.jar.sha1
new file mode 100644
index 0000000..4e30a01
--- /dev/null
+++ b/internal-prebuilts/com/android/databinding/adapters/1.1/adapters-1.1-sources.jar.sha1
@@ -0,0 +1 @@
+271f7d5792857458ee31eaafa2f00ab75ee68a27
\ No newline at end of file
diff --git a/internal-prebuilts/com/android/databinding/adapters/1.1/adapters-1.1.aar b/internal-prebuilts/com/android/databinding/adapters/1.1/adapters-1.1.aar
new file mode 100644
index 0000000..91ffc8e
--- /dev/null
+++ b/internal-prebuilts/com/android/databinding/adapters/1.1/adapters-1.1.aar
Binary files differ
diff --git a/internal-prebuilts/com/android/databinding/adapters/1.1/adapters-1.1.aar.md5 b/internal-prebuilts/com/android/databinding/adapters/1.1/adapters-1.1.aar.md5
new file mode 100644
index 0000000..ed95514
--- /dev/null
+++ b/internal-prebuilts/com/android/databinding/adapters/1.1/adapters-1.1.aar.md5
@@ -0,0 +1 @@
+63c9e012af57aacb15d1b998711077c0
\ No newline at end of file
diff --git a/internal-prebuilts/com/android/databinding/adapters/1.1/adapters-1.1.aar.sha1 b/internal-prebuilts/com/android/databinding/adapters/1.1/adapters-1.1.aar.sha1
new file mode 100644
index 0000000..07ea678
--- /dev/null
+++ b/internal-prebuilts/com/android/databinding/adapters/1.1/adapters-1.1.aar.sha1
@@ -0,0 +1 @@
+110c163a806a7317374e98d9a8ab460e91c0d3d3
\ No newline at end of file
diff --git a/internal-prebuilts/com/android/databinding/adapters/1.1/adapters-1.1.pom b/internal-prebuilts/com/android/databinding/adapters/1.1/adapters-1.1.pom
new file mode 100644
index 0000000..c22b386
--- /dev/null
+++ b/internal-prebuilts/com/android/databinding/adapters/1.1/adapters-1.1.pom
@@ -0,0 +1,30 @@
+<?xml version="1.0" encoding="UTF-8"?>
+<project xsi:schemaLocation="http://maven.apache.org/POM/4.0.0 http://maven.apache.org/xsd/maven-4.0.0.xsd" xmlns="http://maven.apache.org/POM/4.0.0"
+ xmlns:xsi="http://www.w3.org/2001/XMLSchema-instance">
+ <modelVersion>4.0.0</modelVersion>
+ <groupId>com.android.databinding</groupId>
+ <artifactId>adapters</artifactId>
+ <version>1.1</version>
+ <packaging>aar</packaging>
+ <licenses>
+ <license>
+ <name>The Apache Software License, Version 2.0</name>
+ <url>http://www.apache.org/licenses/LICENSE-2.0.txt</url>
+ <distribution>repo</distribution>
+ </license>
+ </licenses>
+ <dependencies>
+ <dependency>
+ <groupId>com.android.databinding</groupId>
+ <artifactId>library</artifactId>
+ <version>1.1</version>
+ <scope>compile</scope>
+ </dependency>
+ <dependency>
+ <groupId>com.android.databinding</groupId>
+ <artifactId>baseLibrary</artifactId>
+ <version>1.1</version>
+ <scope>compile</scope>
+ </dependency>
+ </dependencies>
+</project>
diff --git a/internal-prebuilts/com/android/databinding/adapters/1.1/adapters-1.1.pom.md5 b/internal-prebuilts/com/android/databinding/adapters/1.1/adapters-1.1.pom.md5
new file mode 100644
index 0000000..d928121
--- /dev/null
+++ b/internal-prebuilts/com/android/databinding/adapters/1.1/adapters-1.1.pom.md5
@@ -0,0 +1 @@
+7f884655cb412a87b69247d382edcf2e
\ No newline at end of file
diff --git a/internal-prebuilts/com/android/databinding/adapters/1.1/adapters-1.1.pom.sha1 b/internal-prebuilts/com/android/databinding/adapters/1.1/adapters-1.1.pom.sha1
new file mode 100644
index 0000000..eb40518
--- /dev/null
+++ b/internal-prebuilts/com/android/databinding/adapters/1.1/adapters-1.1.pom.sha1
@@ -0,0 +1 @@
+bd7626a61823c5e1b5ea49a6315cac64c016c220
\ No newline at end of file
diff --git a/internal-prebuilts/com/android/databinding/adapters/maven-metadata.xml b/internal-prebuilts/com/android/databinding/adapters/maven-metadata.xml
new file mode 100644
index 0000000..da35a08
--- /dev/null
+++ b/internal-prebuilts/com/android/databinding/adapters/maven-metadata.xml
@@ -0,0 +1,13 @@
+<?xml version="1.0" encoding="UTF-8"?>
+<metadata>
+ <groupId>com.android.databinding</groupId>
+ <artifactId>adapters</artifactId>
+ <versioning>
+ <release>1.1</release>
+ <versions>
+ <version>1.0-rc5</version>
+ <version>1.1</version>
+ </versions>
+ <lastUpdated>20160114222429</lastUpdated>
+ </versioning>
+</metadata>
diff --git a/internal-prebuilts/com/android/databinding/adapters/maven-metadata.xml.md5 b/internal-prebuilts/com/android/databinding/adapters/maven-metadata.xml.md5
new file mode 100644
index 0000000..cd35679
--- /dev/null
+++ b/internal-prebuilts/com/android/databinding/adapters/maven-metadata.xml.md5
@@ -0,0 +1 @@
+08b9a4a0ba5bfce3cf77ae92007003e5
\ No newline at end of file
diff --git a/internal-prebuilts/com/android/databinding/adapters/maven-metadata.xml.sha1 b/internal-prebuilts/com/android/databinding/adapters/maven-metadata.xml.sha1
new file mode 100644
index 0000000..b604175
--- /dev/null
+++ b/internal-prebuilts/com/android/databinding/adapters/maven-metadata.xml.sha1
@@ -0,0 +1 @@
+98de00cc52ce9a2a7b71d15e67fe22926c88d528
\ No newline at end of file
diff --git a/internal-prebuilts/com/android/databinding/baseLibrary/1.0-rc3/baseLibrary-1.0-rc3-javadoc.jar b/internal-prebuilts/com/android/databinding/baseLibrary/1.0-rc3/baseLibrary-1.0-rc3-javadoc.jar
new file mode 100644
index 0000000..c218838
--- /dev/null
+++ b/internal-prebuilts/com/android/databinding/baseLibrary/1.0-rc3/baseLibrary-1.0-rc3-javadoc.jar
Binary files differ
diff --git a/internal-prebuilts/com/android/databinding/baseLibrary/1.0-rc3/baseLibrary-1.0-rc3-sources.jar b/internal-prebuilts/com/android/databinding/baseLibrary/1.0-rc3/baseLibrary-1.0-rc3-sources.jar
new file mode 100644
index 0000000..402b6f3
--- /dev/null
+++ b/internal-prebuilts/com/android/databinding/baseLibrary/1.0-rc3/baseLibrary-1.0-rc3-sources.jar
Binary files differ
diff --git a/internal-prebuilts/com/android/databinding/baseLibrary/1.0-rc3/baseLibrary-1.0-rc3-sources.jar.md5 b/internal-prebuilts/com/android/databinding/baseLibrary/1.0-rc3/baseLibrary-1.0-rc3-sources.jar.md5
new file mode 100644
index 0000000..fd611e0
--- /dev/null
+++ b/internal-prebuilts/com/android/databinding/baseLibrary/1.0-rc3/baseLibrary-1.0-rc3-sources.jar.md5
@@ -0,0 +1 @@
+06eaa31170fbda8df4bfabbd9e597dbb
\ No newline at end of file
diff --git a/internal-prebuilts/com/android/databinding/baseLibrary/1.0-rc3/baseLibrary-1.0-rc3.jar b/internal-prebuilts/com/android/databinding/baseLibrary/1.0-rc3/baseLibrary-1.0-rc3.jar
new file mode 100644
index 0000000..f2ba523
--- /dev/null
+++ b/internal-prebuilts/com/android/databinding/baseLibrary/1.0-rc3/baseLibrary-1.0-rc3.jar
Binary files differ
diff --git a/internal-prebuilts/com/android/databinding/baseLibrary/1.0-rc3/baseLibrary-1.0-rc3.jar.md5 b/internal-prebuilts/com/android/databinding/baseLibrary/1.0-rc3/baseLibrary-1.0-rc3.jar.md5
new file mode 100644
index 0000000..8cfaa39
--- /dev/null
+++ b/internal-prebuilts/com/android/databinding/baseLibrary/1.0-rc3/baseLibrary-1.0-rc3.jar.md5
@@ -0,0 +1 @@
+e4d9cb379ec08bd5605f50ae46f4931a
\ No newline at end of file
diff --git a/plugins-repo/com/android/databinding/bintray/1.0/bintray-1.0.pom b/internal-prebuilts/com/android/databinding/baseLibrary/1.0-rc3/baseLibrary-1.0-rc3.pom
similarity index 64%
rename from plugins-repo/com/android/databinding/bintray/1.0/bintray-1.0.pom
rename to internal-prebuilts/com/android/databinding/baseLibrary/1.0-rc3/baseLibrary-1.0-rc3.pom
index 7b2ffc5..a7cb4c7 100644
--- a/plugins-repo/com/android/databinding/bintray/1.0/bintray-1.0.pom
+++ b/internal-prebuilts/com/android/databinding/baseLibrary/1.0-rc3/baseLibrary-1.0-rc3.pom
@@ -3,8 +3,15 @@
xmlns:xsi="http://www.w3.org/2001/XMLSchema-instance">
<modelVersion>4.0.0</modelVersion>
<groupId>com.android.databinding</groupId>
- <artifactId>bintray</artifactId>
- <version>1.0</version>
+ <artifactId>baseLibrary</artifactId>
+ <version>1.0-rc3</version>
+ <licenses>
+ <license>
+ <name>The Apache Software License, Version 2.0</name>
+ <url>http://www.apache.org/licenses/LICENSE-2.0.txt</url>
+ <distribution>repo</distribution>
+ </license>
+ </licenses>
<dependencies>
<dependency>
<groupId>junit</groupId>
diff --git a/internal-prebuilts/com/android/databinding/baseLibrary/1.0-rc3/baseLibrary-1.0-rc3.pom.md5 b/internal-prebuilts/com/android/databinding/baseLibrary/1.0-rc3/baseLibrary-1.0-rc3.pom.md5
new file mode 100644
index 0000000..0865ba5
--- /dev/null
+++ b/internal-prebuilts/com/android/databinding/baseLibrary/1.0-rc3/baseLibrary-1.0-rc3.pom.md5
@@ -0,0 +1 @@
+b27ce565d94a8fa1510cf091bd392cea
\ No newline at end of file
diff --git a/internal-prebuilts/com/android/databinding/library/1.0-rc3/library-1.0-rc3-javadoc.jar b/internal-prebuilts/com/android/databinding/library/1.0-rc3/library-1.0-rc3-javadoc.jar
new file mode 100644
index 0000000..8691f34
--- /dev/null
+++ b/internal-prebuilts/com/android/databinding/library/1.0-rc3/library-1.0-rc3-javadoc.jar
Binary files differ
diff --git a/internal-prebuilts/com/android/databinding/library/1.0-rc3/library-1.0-rc3-javadoc.jar.md5 b/internal-prebuilts/com/android/databinding/library/1.0-rc3/library-1.0-rc3-javadoc.jar.md5
new file mode 100644
index 0000000..5570186
--- /dev/null
+++ b/internal-prebuilts/com/android/databinding/library/1.0-rc3/library-1.0-rc3-javadoc.jar.md5
@@ -0,0 +1 @@
+7f9c5798e927f862c9e3b297a15ba841
\ No newline at end of file
diff --git a/internal-prebuilts/com/android/databinding/library/1.0-rc3/library-1.0-rc3-javadoc.jar.sha1 b/internal-prebuilts/com/android/databinding/library/1.0-rc3/library-1.0-rc3-javadoc.jar.sha1
new file mode 100644
index 0000000..222e7f2
--- /dev/null
+++ b/internal-prebuilts/com/android/databinding/library/1.0-rc3/library-1.0-rc3-javadoc.jar.sha1
@@ -0,0 +1 @@
+7599391632f87f2b33c0c392b6e1593ef1196221
\ No newline at end of file
diff --git a/internal-prebuilts/com/android/databinding/library/1.0-rc3/library-1.0-rc3-sources.jar b/internal-prebuilts/com/android/databinding/library/1.0-rc3/library-1.0-rc3-sources.jar
new file mode 100644
index 0000000..652ad8d
--- /dev/null
+++ b/internal-prebuilts/com/android/databinding/library/1.0-rc3/library-1.0-rc3-sources.jar
Binary files differ
diff --git a/internal-prebuilts/com/android/databinding/library/1.0-rc3/library-1.0-rc3-sources.jar.md5 b/internal-prebuilts/com/android/databinding/library/1.0-rc3/library-1.0-rc3-sources.jar.md5
new file mode 100644
index 0000000..f6b98ed
--- /dev/null
+++ b/internal-prebuilts/com/android/databinding/library/1.0-rc3/library-1.0-rc3-sources.jar.md5
@@ -0,0 +1 @@
+7ed58210198f3afb13226a42f3d97b80
\ No newline at end of file
diff --git a/internal-prebuilts/com/android/databinding/library/1.0-rc3/library-1.0-rc3-sources.jar.sha1 b/internal-prebuilts/com/android/databinding/library/1.0-rc3/library-1.0-rc3-sources.jar.sha1
new file mode 100644
index 0000000..06663fb
--- /dev/null
+++ b/internal-prebuilts/com/android/databinding/library/1.0-rc3/library-1.0-rc3-sources.jar.sha1
@@ -0,0 +1 @@
+a25430b7adff8cd8f6eb5d4ba1066fbe92099b81
\ No newline at end of file
diff --git a/internal-prebuilts/com/android/databinding/library/1.0-rc3/library-1.0-rc3.aar b/internal-prebuilts/com/android/databinding/library/1.0-rc3/library-1.0-rc3.aar
new file mode 100644
index 0000000..439fce5
--- /dev/null
+++ b/internal-prebuilts/com/android/databinding/library/1.0-rc3/library-1.0-rc3.aar
Binary files differ
diff --git a/internal-prebuilts/com/android/databinding/library/1.0-rc3/library-1.0-rc3.aar.md5 b/internal-prebuilts/com/android/databinding/library/1.0-rc3/library-1.0-rc3.aar.md5
new file mode 100644
index 0000000..d454d0f
--- /dev/null
+++ b/internal-prebuilts/com/android/databinding/library/1.0-rc3/library-1.0-rc3.aar.md5
@@ -0,0 +1 @@
+3ca01d790d7efee212362a168a74b6c7
\ No newline at end of file
diff --git a/internal-prebuilts/com/android/databinding/library/1.0-rc3/library-1.0-rc3.aar.sha1 b/internal-prebuilts/com/android/databinding/library/1.0-rc3/library-1.0-rc3.aar.sha1
new file mode 100644
index 0000000..af0648b
--- /dev/null
+++ b/internal-prebuilts/com/android/databinding/library/1.0-rc3/library-1.0-rc3.aar.sha1
@@ -0,0 +1 @@
+33a2cacd38ea6e7ea750a50b1be3e5ecddc07fd2
\ No newline at end of file
diff --git a/internal-prebuilts/com/android/databinding/library/1.0-rc3/library-1.0-rc3.pom b/internal-prebuilts/com/android/databinding/library/1.0-rc3/library-1.0-rc3.pom
new file mode 100644
index 0000000..d836832
--- /dev/null
+++ b/internal-prebuilts/com/android/databinding/library/1.0-rc3/library-1.0-rc3.pom
@@ -0,0 +1,30 @@
+<?xml version="1.0" encoding="UTF-8"?>
+<project xsi:schemaLocation="http://maven.apache.org/POM/4.0.0 http://maven.apache.org/xsd/maven-4.0.0.xsd" xmlns="http://maven.apache.org/POM/4.0.0"
+ xmlns:xsi="http://www.w3.org/2001/XMLSchema-instance">
+ <modelVersion>4.0.0</modelVersion>
+ <groupId>com.android.databinding</groupId>
+ <artifactId>library</artifactId>
+ <version>1.0-rc3</version>
+ <packaging>aar</packaging>
+ <licenses>
+ <license>
+ <name>The Apache Software License, Version 2.0</name>
+ <url>http://www.apache.org/licenses/LICENSE-2.0.txt</url>
+ <distribution>repo</distribution>
+ </license>
+ </licenses>
+ <dependencies>
+ <dependency>
+ <groupId>com.android.databinding</groupId>
+ <artifactId>baseLibrary</artifactId>
+ <version>1.0-rc3</version>
+ <scope>compile</scope>
+ </dependency>
+ <dependency>
+ <groupId>com.android.support</groupId>
+ <artifactId>support-v4</artifactId>
+ <version>21.0.3</version>
+ <scope>compile</scope>
+ </dependency>
+ </dependencies>
+</project>
diff --git a/internal-prebuilts/com/android/databinding/library/1.0-rc3/library-1.0-rc3.pom.md5 b/internal-prebuilts/com/android/databinding/library/1.0-rc3/library-1.0-rc3.pom.md5
new file mode 100644
index 0000000..f5d608b
--- /dev/null
+++ b/internal-prebuilts/com/android/databinding/library/1.0-rc3/library-1.0-rc3.pom.md5
@@ -0,0 +1 @@
+ef14709e30f81059e4e466e3cbda2c05
\ No newline at end of file
diff --git a/internal-prebuilts/com/android/databinding/library/1.0-rc3/library-1.0-rc3.pom.sha1 b/internal-prebuilts/com/android/databinding/library/1.0-rc3/library-1.0-rc3.pom.sha1
new file mode 100644
index 0000000..c97a628
--- /dev/null
+++ b/internal-prebuilts/com/android/databinding/library/1.0-rc3/library-1.0-rc3.pom.sha1
@@ -0,0 +1 @@
+e7aea023ae8188c8130bdf68e39a3a0fe3b392b1
\ No newline at end of file
diff --git a/internal-prebuilts/com/android/databinding/library/1.0-rc5/library-1.0-rc5-javadoc.jar b/internal-prebuilts/com/android/databinding/library/1.0-rc5/library-1.0-rc5-javadoc.jar
new file mode 100644
index 0000000..2496be0
--- /dev/null
+++ b/internal-prebuilts/com/android/databinding/library/1.0-rc5/library-1.0-rc5-javadoc.jar
Binary files differ
diff --git a/internal-prebuilts/com/android/databinding/library/1.0-rc5/library-1.0-rc5-javadoc.jar.md5 b/internal-prebuilts/com/android/databinding/library/1.0-rc5/library-1.0-rc5-javadoc.jar.md5
new file mode 100644
index 0000000..029c0b1
--- /dev/null
+++ b/internal-prebuilts/com/android/databinding/library/1.0-rc5/library-1.0-rc5-javadoc.jar.md5
@@ -0,0 +1 @@
+55b0c81b3729025054081437f34155ec
\ No newline at end of file
diff --git a/internal-prebuilts/com/android/databinding/library/1.0-rc5/library-1.0-rc5-javadoc.jar.sha1 b/internal-prebuilts/com/android/databinding/library/1.0-rc5/library-1.0-rc5-javadoc.jar.sha1
new file mode 100644
index 0000000..b6ac5a8
--- /dev/null
+++ b/internal-prebuilts/com/android/databinding/library/1.0-rc5/library-1.0-rc5-javadoc.jar.sha1
@@ -0,0 +1 @@
+34c8d79bd66d99f81666593c35ae7cdb036b1a03
\ No newline at end of file
diff --git a/internal-prebuilts/com/android/databinding/library/1.0-rc5/library-1.0-rc5-sources.jar b/internal-prebuilts/com/android/databinding/library/1.0-rc5/library-1.0-rc5-sources.jar
new file mode 100644
index 0000000..b83ae6f
--- /dev/null
+++ b/internal-prebuilts/com/android/databinding/library/1.0-rc5/library-1.0-rc5-sources.jar
Binary files differ
diff --git a/internal-prebuilts/com/android/databinding/library/1.0-rc5/library-1.0-rc5-sources.jar.md5 b/internal-prebuilts/com/android/databinding/library/1.0-rc5/library-1.0-rc5-sources.jar.md5
new file mode 100644
index 0000000..9cb4087
--- /dev/null
+++ b/internal-prebuilts/com/android/databinding/library/1.0-rc5/library-1.0-rc5-sources.jar.md5
@@ -0,0 +1 @@
+0f225a5c73a58cf525994b1a183e75ad
\ No newline at end of file
diff --git a/internal-prebuilts/com/android/databinding/library/1.0-rc5/library-1.0-rc5-sources.jar.sha1 b/internal-prebuilts/com/android/databinding/library/1.0-rc5/library-1.0-rc5-sources.jar.sha1
new file mode 100644
index 0000000..436c4c7
--- /dev/null
+++ b/internal-prebuilts/com/android/databinding/library/1.0-rc5/library-1.0-rc5-sources.jar.sha1
@@ -0,0 +1 @@
+b0e6dfbe9daf46fe0503957b81da6e888fdfcf30
\ No newline at end of file
diff --git a/internal-prebuilts/com/android/databinding/library/1.0-rc5/library-1.0-rc5.aar b/internal-prebuilts/com/android/databinding/library/1.0-rc5/library-1.0-rc5.aar
new file mode 100644
index 0000000..1bc5ca8
--- /dev/null
+++ b/internal-prebuilts/com/android/databinding/library/1.0-rc5/library-1.0-rc5.aar
Binary files differ
diff --git a/internal-prebuilts/com/android/databinding/library/1.0-rc5/library-1.0-rc5.aar.md5 b/internal-prebuilts/com/android/databinding/library/1.0-rc5/library-1.0-rc5.aar.md5
new file mode 100644
index 0000000..7db41ee
--- /dev/null
+++ b/internal-prebuilts/com/android/databinding/library/1.0-rc5/library-1.0-rc5.aar.md5
@@ -0,0 +1 @@
+a2166f6f9663d36ee0414f85f229f765
\ No newline at end of file
diff --git a/internal-prebuilts/com/android/databinding/library/1.0-rc5/library-1.0-rc5.aar.sha1 b/internal-prebuilts/com/android/databinding/library/1.0-rc5/library-1.0-rc5.aar.sha1
new file mode 100644
index 0000000..4aced33
--- /dev/null
+++ b/internal-prebuilts/com/android/databinding/library/1.0-rc5/library-1.0-rc5.aar.sha1
@@ -0,0 +1 @@
+aba6c5f9ad349a476c59826b53c1a2deb1af3add
\ No newline at end of file
diff --git a/internal-prebuilts/com/android/databinding/library/1.0-rc5/library-1.0-rc5.pom b/internal-prebuilts/com/android/databinding/library/1.0-rc5/library-1.0-rc5.pom
new file mode 100644
index 0000000..c2e5066
--- /dev/null
+++ b/internal-prebuilts/com/android/databinding/library/1.0-rc5/library-1.0-rc5.pom
@@ -0,0 +1,30 @@
+<?xml version="1.0" encoding="UTF-8"?>
+<project xsi:schemaLocation="http://maven.apache.org/POM/4.0.0 http://maven.apache.org/xsd/maven-4.0.0.xsd" xmlns="http://maven.apache.org/POM/4.0.0"
+ xmlns:xsi="http://www.w3.org/2001/XMLSchema-instance">
+ <modelVersion>4.0.0</modelVersion>
+ <groupId>com.android.databinding</groupId>
+ <artifactId>library</artifactId>
+ <version>1.0-rc5</version>
+ <packaging>aar</packaging>
+ <licenses>
+ <license>
+ <name>The Apache Software License, Version 2.0</name>
+ <url>http://www.apache.org/licenses/LICENSE-2.0.txt</url>
+ <distribution>repo</distribution>
+ </license>
+ </licenses>
+ <dependencies>
+ <dependency>
+ <groupId>com.android.support</groupId>
+ <artifactId>support-v4</artifactId>
+ <version>21.0.3</version>
+ <scope>compile</scope>
+ </dependency>
+ <dependency>
+ <groupId>com.android.databinding</groupId>
+ <artifactId>baseLibrary</artifactId>
+ <version>1.0-rc5</version>
+ <scope>compile</scope>
+ </dependency>
+ </dependencies>
+</project>
diff --git a/internal-prebuilts/com/android/databinding/library/1.0-rc5/library-1.0-rc5.pom.md5 b/internal-prebuilts/com/android/databinding/library/1.0-rc5/library-1.0-rc5.pom.md5
new file mode 100644
index 0000000..67798ba
--- /dev/null
+++ b/internal-prebuilts/com/android/databinding/library/1.0-rc5/library-1.0-rc5.pom.md5
@@ -0,0 +1 @@
+f2a81f3066450c88c406125052d0b4b7
\ No newline at end of file
diff --git a/internal-prebuilts/com/android/databinding/library/1.0-rc5/library-1.0-rc5.pom.sha1 b/internal-prebuilts/com/android/databinding/library/1.0-rc5/library-1.0-rc5.pom.sha1
new file mode 100644
index 0000000..fdd031f
--- /dev/null
+++ b/internal-prebuilts/com/android/databinding/library/1.0-rc5/library-1.0-rc5.pom.sha1
@@ -0,0 +1 @@
+328129fafbd49107574154efdefd115efe26c60b
\ No newline at end of file
diff --git a/internal-prebuilts/com/android/databinding/library/1.1/library-1.1-javadoc.jar b/internal-prebuilts/com/android/databinding/library/1.1/library-1.1-javadoc.jar
new file mode 100644
index 0000000..fa82708
--- /dev/null
+++ b/internal-prebuilts/com/android/databinding/library/1.1/library-1.1-javadoc.jar
Binary files differ
diff --git a/internal-prebuilts/com/android/databinding/library/1.1/library-1.1-javadoc.jar.md5 b/internal-prebuilts/com/android/databinding/library/1.1/library-1.1-javadoc.jar.md5
new file mode 100644
index 0000000..ad758d9
--- /dev/null
+++ b/internal-prebuilts/com/android/databinding/library/1.1/library-1.1-javadoc.jar.md5
@@ -0,0 +1 @@
+d7e559f25a20f061c62cc4dbbfb6a36c
\ No newline at end of file
diff --git a/internal-prebuilts/com/android/databinding/library/1.1/library-1.1-javadoc.jar.sha1 b/internal-prebuilts/com/android/databinding/library/1.1/library-1.1-javadoc.jar.sha1
new file mode 100644
index 0000000..54e287c
--- /dev/null
+++ b/internal-prebuilts/com/android/databinding/library/1.1/library-1.1-javadoc.jar.sha1
@@ -0,0 +1 @@
+339a3b20fd98775796dfb6c248cc7ddbf526f635
\ No newline at end of file
diff --git a/internal-prebuilts/com/android/databinding/library/1.1/library-1.1-sources.jar b/internal-prebuilts/com/android/databinding/library/1.1/library-1.1-sources.jar
new file mode 100644
index 0000000..d8bd208
--- /dev/null
+++ b/internal-prebuilts/com/android/databinding/library/1.1/library-1.1-sources.jar
Binary files differ
diff --git a/internal-prebuilts/com/android/databinding/library/1.1/library-1.1-sources.jar.md5 b/internal-prebuilts/com/android/databinding/library/1.1/library-1.1-sources.jar.md5
new file mode 100644
index 0000000..964df96
--- /dev/null
+++ b/internal-prebuilts/com/android/databinding/library/1.1/library-1.1-sources.jar.md5
@@ -0,0 +1 @@
+ca88a210328f9c3655c3524421874a97
\ No newline at end of file
diff --git a/internal-prebuilts/com/android/databinding/library/1.1/library-1.1-sources.jar.sha1 b/internal-prebuilts/com/android/databinding/library/1.1/library-1.1-sources.jar.sha1
new file mode 100644
index 0000000..e640354
--- /dev/null
+++ b/internal-prebuilts/com/android/databinding/library/1.1/library-1.1-sources.jar.sha1
@@ -0,0 +1 @@
+12e3f88b119fbaccf46fe28b6c4f35adbe60daf3
\ No newline at end of file
diff --git a/internal-prebuilts/com/android/databinding/library/1.1/library-1.1.aar b/internal-prebuilts/com/android/databinding/library/1.1/library-1.1.aar
new file mode 100644
index 0000000..d25e0d1
--- /dev/null
+++ b/internal-prebuilts/com/android/databinding/library/1.1/library-1.1.aar
Binary files differ
diff --git a/internal-prebuilts/com/android/databinding/library/1.1/library-1.1.aar.md5 b/internal-prebuilts/com/android/databinding/library/1.1/library-1.1.aar.md5
new file mode 100644
index 0000000..97b2c02
--- /dev/null
+++ b/internal-prebuilts/com/android/databinding/library/1.1/library-1.1.aar.md5
@@ -0,0 +1 @@
+786980fe30ef7a7015ec9d9e4f340ecb
\ No newline at end of file
diff --git a/internal-prebuilts/com/android/databinding/library/1.1/library-1.1.aar.sha1 b/internal-prebuilts/com/android/databinding/library/1.1/library-1.1.aar.sha1
new file mode 100644
index 0000000..a2e0c86
--- /dev/null
+++ b/internal-prebuilts/com/android/databinding/library/1.1/library-1.1.aar.sha1
@@ -0,0 +1 @@
+6d305d8c838bbb1e16584a3053097699b5353d11
\ No newline at end of file
diff --git a/internal-prebuilts/com/android/databinding/library/1.1/library-1.1.pom b/internal-prebuilts/com/android/databinding/library/1.1/library-1.1.pom
new file mode 100644
index 0000000..bcb1d9b
--- /dev/null
+++ b/internal-prebuilts/com/android/databinding/library/1.1/library-1.1.pom
@@ -0,0 +1,30 @@
+<?xml version="1.0" encoding="UTF-8"?>
+<project xsi:schemaLocation="http://maven.apache.org/POM/4.0.0 http://maven.apache.org/xsd/maven-4.0.0.xsd" xmlns="http://maven.apache.org/POM/4.0.0"
+ xmlns:xsi="http://www.w3.org/2001/XMLSchema-instance">
+ <modelVersion>4.0.0</modelVersion>
+ <groupId>com.android.databinding</groupId>
+ <artifactId>library</artifactId>
+ <version>1.1</version>
+ <packaging>aar</packaging>
+ <licenses>
+ <license>
+ <name>The Apache Software License, Version 2.0</name>
+ <url>http://www.apache.org/licenses/LICENSE-2.0.txt</url>
+ <distribution>repo</distribution>
+ </license>
+ </licenses>
+ <dependencies>
+ <dependency>
+ <groupId>com.android.support</groupId>
+ <artifactId>support-v4</artifactId>
+ <version>21.0.3</version>
+ <scope>compile</scope>
+ </dependency>
+ <dependency>
+ <groupId>com.android.databinding</groupId>
+ <artifactId>baseLibrary</artifactId>
+ <version>1.1</version>
+ <scope>compile</scope>
+ </dependency>
+ </dependencies>
+</project>
diff --git a/internal-prebuilts/com/android/databinding/library/1.1/library-1.1.pom.md5 b/internal-prebuilts/com/android/databinding/library/1.1/library-1.1.pom.md5
new file mode 100644
index 0000000..4559ce5
--- /dev/null
+++ b/internal-prebuilts/com/android/databinding/library/1.1/library-1.1.pom.md5
@@ -0,0 +1 @@
+8177aaf76c6da0bc8f881a78327f0355
\ No newline at end of file
diff --git a/internal-prebuilts/com/android/databinding/library/1.1/library-1.1.pom.sha1 b/internal-prebuilts/com/android/databinding/library/1.1/library-1.1.pom.sha1
new file mode 100644
index 0000000..b2c05a2
--- /dev/null
+++ b/internal-prebuilts/com/android/databinding/library/1.1/library-1.1.pom.sha1
@@ -0,0 +1 @@
+5a4b0208f2a1722c23341d0d163d4c459c60ae3a
\ No newline at end of file
diff --git a/internal-prebuilts/com/android/databinding/library/maven-metadata.xml b/internal-prebuilts/com/android/databinding/library/maven-metadata.xml
new file mode 100644
index 0000000..ddcb12e
--- /dev/null
+++ b/internal-prebuilts/com/android/databinding/library/maven-metadata.xml
@@ -0,0 +1,13 @@
+<?xml version="1.0" encoding="UTF-8"?>
+<metadata>
+ <groupId>com.android.databinding</groupId>
+ <artifactId>library</artifactId>
+ <versioning>
+ <release>1.1</release>
+ <versions>
+ <version>1.0-rc5</version>
+ <version>1.1</version>
+ </versions>
+ <lastUpdated>20160114222429</lastUpdated>
+ </versioning>
+</metadata>
diff --git a/internal-prebuilts/com/android/databinding/library/maven-metadata.xml.md5 b/internal-prebuilts/com/android/databinding/library/maven-metadata.xml.md5
new file mode 100644
index 0000000..04ab5a4
--- /dev/null
+++ b/internal-prebuilts/com/android/databinding/library/maven-metadata.xml.md5
@@ -0,0 +1 @@
+614659348ea658cd97eda5b27231b223
\ No newline at end of file
diff --git a/internal-prebuilts/com/android/databinding/library/maven-metadata.xml.sha1 b/internal-prebuilts/com/android/databinding/library/maven-metadata.xml.sha1
new file mode 100644
index 0000000..e87a0a4
--- /dev/null
+++ b/internal-prebuilts/com/android/databinding/library/maven-metadata.xml.sha1
@@ -0,0 +1 @@
+0fecc097f9c2a8973c7169112cea421b48b87cb0
\ No newline at end of file
diff --git a/internal-prebuilts/com/android/databinding/localizemaven/1.1/localizemaven-1.1.jar b/internal-prebuilts/com/android/databinding/localizemaven/1.1/localizemaven-1.1.jar
new file mode 100644
index 0000000..e003a04
--- /dev/null
+++ b/internal-prebuilts/com/android/databinding/localizemaven/1.1/localizemaven-1.1.jar
Binary files differ
diff --git a/internal-prebuilts/com/android/databinding/localizemaven/1.1/localizemaven-1.1.jar.md5 b/internal-prebuilts/com/android/databinding/localizemaven/1.1/localizemaven-1.1.jar.md5
new file mode 100644
index 0000000..d93d95d
--- /dev/null
+++ b/internal-prebuilts/com/android/databinding/localizemaven/1.1/localizemaven-1.1.jar.md5
@@ -0,0 +1 @@
+4b81f88150cd2be1b1f6d886100af1c2
\ No newline at end of file
diff --git a/internal-prebuilts/com/android/databinding/localizemaven/1.1/localizemaven-1.1.jar.sha1 b/internal-prebuilts/com/android/databinding/localizemaven/1.1/localizemaven-1.1.jar.sha1
new file mode 100644
index 0000000..77d5422
--- /dev/null
+++ b/internal-prebuilts/com/android/databinding/localizemaven/1.1/localizemaven-1.1.jar.sha1
@@ -0,0 +1 @@
+6e19c0ca82802428346c624673e66479089c6814
\ No newline at end of file
diff --git a/plugins-repo/com/android/databinding/localizemaven/1.0/localizemaven-1.0.pom b/internal-prebuilts/com/android/databinding/localizemaven/1.1/localizemaven-1.1.pom
similarity index 97%
rename from plugins-repo/com/android/databinding/localizemaven/1.0/localizemaven-1.0.pom
rename to internal-prebuilts/com/android/databinding/localizemaven/1.1/localizemaven-1.1.pom
index e9cc223..08a876c 100644
--- a/plugins-repo/com/android/databinding/localizemaven/1.0/localizemaven-1.0.pom
+++ b/internal-prebuilts/com/android/databinding/localizemaven/1.1/localizemaven-1.1.pom
@@ -4,7 +4,7 @@
<modelVersion>4.0.0</modelVersion>
<groupId>com.android.databinding</groupId>
<artifactId>localizemaven</artifactId>
- <version>1.0</version>
+ <version>1.1</version>
<dependencies>
<dependency>
<groupId>org.eclipse.aether</groupId>
diff --git a/internal-prebuilts/com/android/databinding/localizemaven/1.1/localizemaven-1.1.pom.md5 b/internal-prebuilts/com/android/databinding/localizemaven/1.1/localizemaven-1.1.pom.md5
new file mode 100644
index 0000000..20a4982
--- /dev/null
+++ b/internal-prebuilts/com/android/databinding/localizemaven/1.1/localizemaven-1.1.pom.md5
@@ -0,0 +1 @@
+1e4d10cf9c8ffc6040e22c7e76f0aa2a
\ No newline at end of file
diff --git a/internal-prebuilts/com/android/databinding/localizemaven/1.1/localizemaven-1.1.pom.sha1 b/internal-prebuilts/com/android/databinding/localizemaven/1.1/localizemaven-1.1.pom.sha1
new file mode 100644
index 0000000..6705f12
--- /dev/null
+++ b/internal-prebuilts/com/android/databinding/localizemaven/1.1/localizemaven-1.1.pom.sha1
@@ -0,0 +1 @@
+89065de70856a47fcf5e51f9b8a5529644952303
\ No newline at end of file
diff --git a/plugins-repo/com/android/databinding/localizemaven/maven-metadata.xml b/internal-prebuilts/com/android/databinding/localizemaven/maven-metadata.xml
similarity index 67%
rename from plugins-repo/com/android/databinding/localizemaven/maven-metadata.xml
rename to internal-prebuilts/com/android/databinding/localizemaven/maven-metadata.xml
index 6761fe8..4f263e9 100644
--- a/plugins-repo/com/android/databinding/localizemaven/maven-metadata.xml
+++ b/internal-prebuilts/com/android/databinding/localizemaven/maven-metadata.xml
@@ -2,11 +2,11 @@
<metadata>
<groupId>com.android.databinding</groupId>
<artifactId>localizemaven</artifactId>
- <version>1.0</version>
+ <version>1.1</version>
<versioning>
<versions>
- <version>1.0</version>
+ <version>1.1</version>
</versions>
- <lastUpdated>20150717200705</lastUpdated>
+ <lastUpdated>20151104222707</lastUpdated>
</versioning>
</metadata>
diff --git a/internal-prebuilts/com/android/databinding/localizemaven/maven-metadata.xml.md5 b/internal-prebuilts/com/android/databinding/localizemaven/maven-metadata.xml.md5
new file mode 100644
index 0000000..accb9a0
--- /dev/null
+++ b/internal-prebuilts/com/android/databinding/localizemaven/maven-metadata.xml.md5
@@ -0,0 +1 @@
+71903e14023ca5e2adb851cf065715e7
\ No newline at end of file
diff --git a/internal-prebuilts/com/android/databinding/localizemaven/maven-metadata.xml.sha1 b/internal-prebuilts/com/android/databinding/localizemaven/maven-metadata.xml.sha1
new file mode 100644
index 0000000..c2964b1
--- /dev/null
+++ b/internal-prebuilts/com/android/databinding/localizemaven/maven-metadata.xml.sha1
@@ -0,0 +1 @@
+f30fef09adcbc55cf74ad44c7607accc884d87f9
\ No newline at end of file
diff --git a/library/build.gradle b/library/build.gradle
deleted file mode 100644
index 937a383..0000000
--- a/library/build.gradle
+++ /dev/null
@@ -1,127 +0,0 @@
-/*
- * 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 {
- 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'
-
-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'
- }
-}
-
-configurations {
- jarArchives
-}
-
-
-dependencies {
- compile 'com.android.support:support-v4:21.0.3'
- compile project(':baseLibrary')
-}
-
-//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.
- }
- def suffix = name.capitalize()
-
- def javadocTask = project.tasks.create(name: "javadoc${suffix}", type: Javadoc) {
- source variant.javaCompile.source + project(":baseLibrary").tasks['compileJava'].source
- classpath = files(variant.javaCompile.classpath.files) + files(
- "${android.sdkDirectory}/platforms/${android.compileSdkVersion}/android.jar")
- }
-
- def javadocJarTask = project.tasks.create(name: "javadocJar${suffix}", type: Jar) {
- classifier = 'javadoc'
- from 'build/docs/javadoc'
- }
- javadocJarTask.dependsOn javadocTask
-
- def sourcesJarTask = project.tasks.create(name: "sourceJar${suffix}", type: Jar) {
- classifier = 'sources'
- from android.sourceSets.main.java.srcDirs + project(":baseLibrary").sourceSets.main.java.srcDirs
- }
-
- artifacts.add('archives', javadocJarTask);
- artifacts.add('archives', sourcesJarTask);
-}
-uploadArchives {
- repositories {
- mavenDeployer {
- pom.artifactId = 'library'
- pom.project {
- licenses {
- license {
- name config.licenseName
- url config.licenseUrl
- distribution config.licenseDistribution
- }
- }
- }
- }
- }
-}
-
-
-afterEvaluate {
- tasks['packageReleaseJar'].exclude('android/databinding/DataBinderMapper.*')
- tasks['packageReleaseJar'].exclude('android/databinding/DataBindingComponent.*')
- tasks['packageDebugJar'].exclude('android/databinding/DataBinderMapper.*')
- tasks['packageDebugJar'].exclude('android/databinding/DataBindingComponent.*')
-}
-
-task prebuildAar(type : Copy) {
- dependsOn uploadArchives
- from "$buildDir/outputs/aar/library-release.aar"
- into config.prebuildFolder
- rename { String fileName ->
- "databinding-library.aar"
- }
-}
\ No newline at end of file
diff --git a/plugins-repo/com/android/databinding/bintray/1.0/bintray-1.0.jar b/plugins-repo/com/android/databinding/bintray/1.0/bintray-1.0.jar
deleted file mode 100644
index 87bd3b5..0000000
--- a/plugins-repo/com/android/databinding/bintray/1.0/bintray-1.0.jar
+++ /dev/null
Binary files differ
diff --git a/plugins-repo/com/android/databinding/bintray/1.0/bintray-1.0.jar.md5 b/plugins-repo/com/android/databinding/bintray/1.0/bintray-1.0.jar.md5
deleted file mode 100644
index 5240a5e..0000000
--- a/plugins-repo/com/android/databinding/bintray/1.0/bintray-1.0.jar.md5
+++ /dev/null
@@ -1 +0,0 @@
-7587c61bfa00ad541010853edda211f8
\ No newline at end of file
diff --git a/plugins-repo/com/android/databinding/bintray/1.0/bintray-1.0.jar.sha1 b/plugins-repo/com/android/databinding/bintray/1.0/bintray-1.0.jar.sha1
deleted file mode 100644
index 3533a16..0000000
--- a/plugins-repo/com/android/databinding/bintray/1.0/bintray-1.0.jar.sha1
+++ /dev/null
@@ -1 +0,0 @@
-b23e75f6609abbd735115394773b64d7d715cfc4
\ No newline at end of file
diff --git a/plugins-repo/com/android/databinding/bintray/1.0/bintray-1.0.pom.md5 b/plugins-repo/com/android/databinding/bintray/1.0/bintray-1.0.pom.md5
deleted file mode 100644
index 06b668b..0000000
--- a/plugins-repo/com/android/databinding/bintray/1.0/bintray-1.0.pom.md5
+++ /dev/null
@@ -1 +0,0 @@
-3dc6ce6e2b0f405e0e005bfd61824683
\ No newline at end of file
diff --git a/plugins-repo/com/android/databinding/bintray/1.0/bintray-1.0.pom.sha1 b/plugins-repo/com/android/databinding/bintray/1.0/bintray-1.0.pom.sha1
deleted file mode 100644
index c071f7c..0000000
--- a/plugins-repo/com/android/databinding/bintray/1.0/bintray-1.0.pom.sha1
+++ /dev/null
@@ -1 +0,0 @@
-05fa26e81f15809f9e231099f05fc62d0c4a536b
\ No newline at end of file
diff --git a/plugins-repo/com/android/databinding/bintray/maven-metadata.xml b/plugins-repo/com/android/databinding/bintray/maven-metadata.xml
deleted file mode 100644
index 1a3143d..0000000
--- a/plugins-repo/com/android/databinding/bintray/maven-metadata.xml
+++ /dev/null
@@ -1,12 +0,0 @@
-<?xml version="1.0" encoding="UTF-8"?>
-<metadata>
- <groupId>com.android.databinding</groupId>
- <artifactId>bintray</artifactId>
- <version>1.0</version>
- <versioning>
- <versions>
- <version>1.0</version>
- </versions>
- <lastUpdated>20150717200702</lastUpdated>
- </versioning>
-</metadata>
diff --git a/plugins-repo/com/android/databinding/bintray/maven-metadata.xml.md5 b/plugins-repo/com/android/databinding/bintray/maven-metadata.xml.md5
deleted file mode 100644
index bb48478..0000000
--- a/plugins-repo/com/android/databinding/bintray/maven-metadata.xml.md5
+++ /dev/null
@@ -1 +0,0 @@
-d5d20c728a5dafb939297c828fbb2375
\ No newline at end of file
diff --git a/plugins-repo/com/android/databinding/bintray/maven-metadata.xml.sha1 b/plugins-repo/com/android/databinding/bintray/maven-metadata.xml.sha1
deleted file mode 100644
index 8b18d9e..0000000
--- a/plugins-repo/com/android/databinding/bintray/maven-metadata.xml.sha1
+++ /dev/null
@@ -1 +0,0 @@
-73eccf19ca9942f3df94ac2bc204d9648949ebdb
\ No newline at end of file
diff --git a/plugins-repo/com/android/databinding/localizemaven/1.0/localizemaven-1.0.jar b/plugins-repo/com/android/databinding/localizemaven/1.0/localizemaven-1.0.jar
deleted file mode 100644
index f768a31..0000000
--- a/plugins-repo/com/android/databinding/localizemaven/1.0/localizemaven-1.0.jar
+++ /dev/null
Binary files differ
diff --git a/plugins-repo/com/android/databinding/localizemaven/1.0/localizemaven-1.0.jar.md5 b/plugins-repo/com/android/databinding/localizemaven/1.0/localizemaven-1.0.jar.md5
deleted file mode 100644
index 563dd69..0000000
--- a/plugins-repo/com/android/databinding/localizemaven/1.0/localizemaven-1.0.jar.md5
+++ /dev/null
@@ -1 +0,0 @@
-37be36f09bf80160e6dc45be75e1122f
\ No newline at end of file
diff --git a/plugins-repo/com/android/databinding/localizemaven/1.0/localizemaven-1.0.jar.sha1 b/plugins-repo/com/android/databinding/localizemaven/1.0/localizemaven-1.0.jar.sha1
deleted file mode 100644
index 2cff637..0000000
--- a/plugins-repo/com/android/databinding/localizemaven/1.0/localizemaven-1.0.jar.sha1
+++ /dev/null
@@ -1 +0,0 @@
-d19dc6dac73676f13b4ef90e22302586aa41bcaa
\ No newline at end of file
diff --git a/plugins-repo/com/android/databinding/localizemaven/1.0/localizemaven-1.0.pom.md5 b/plugins-repo/com/android/databinding/localizemaven/1.0/localizemaven-1.0.pom.md5
deleted file mode 100644
index 21c3a27..0000000
--- a/plugins-repo/com/android/databinding/localizemaven/1.0/localizemaven-1.0.pom.md5
+++ /dev/null
@@ -1 +0,0 @@
-f7cd22c6b15280987bb23c4871f32d31
\ No newline at end of file
diff --git a/plugins-repo/com/android/databinding/localizemaven/1.0/localizemaven-1.0.pom.sha1 b/plugins-repo/com/android/databinding/localizemaven/1.0/localizemaven-1.0.pom.sha1
deleted file mode 100644
index f0713d2..0000000
--- a/plugins-repo/com/android/databinding/localizemaven/1.0/localizemaven-1.0.pom.sha1
+++ /dev/null
@@ -1 +0,0 @@
-2bae0e1507a55b8d3ad7f484c56a25896b490cc0
\ No newline at end of file
diff --git a/plugins-repo/com/android/databinding/localizemaven/maven-metadata.xml.md5 b/plugins-repo/com/android/databinding/localizemaven/maven-metadata.xml.md5
deleted file mode 100644
index 8c6accf..0000000
--- a/plugins-repo/com/android/databinding/localizemaven/maven-metadata.xml.md5
+++ /dev/null
@@ -1 +0,0 @@
-3ceff32cb0a00c72558b6d9f20cbae1e
\ No newline at end of file
diff --git a/plugins-repo/com/android/databinding/localizemaven/maven-metadata.xml.sha1 b/plugins-repo/com/android/databinding/localizemaven/maven-metadata.xml.sha1
deleted file mode 100644
index abac4ba..0000000
--- a/plugins-repo/com/android/databinding/localizemaven/maven-metadata.xml.sha1
+++ /dev/null
@@ -1 +0,0 @@
-727c5086bf5cb873142fa24454cb0c0ba9732d88
\ No newline at end of file
diff --git a/proguard.cfg b/proguard.cfg
new file mode 100644
index 0000000..e56368b
--- /dev/null
+++ b/proguard.cfg
@@ -0,0 +1,28 @@
+-keep class android.databinding.** { *; }
+
+-keepnames class * implements java.io.Serializable
+
+-keepclassmembers class * implements java.io.Serializable {
+ static final long serialVersionUID;
+ private static final java.io.ObjectStreamField[] serialPersistentFields;
+ !static !transient <fields>;
+ private void writeObject(java.io.ObjectOutputStream);
+ private void readObject(java.io.ObjectInputStream);
+ java.lang.Object writeReplace();
+ java.lang.Object readResolve();
+}
+
+-keepattributes *Annotation*
+-keepattributes javax.xml.bind.annotation.*
+-keepattributes javax.annotation.processing.*
+
+-keepclassmembers class * extends java.lang.Enum { *; }
+
+-keepclasseswithmembernames class android.**
+
+-keepclasseswithmembernames interface android.**
+
+-dontobfuscate
+-libraryjars <java.home>/lib/rt.jar
+-libraryjars <java.home>/lib/jce.jar
+-dontwarn
\ No newline at end of file
diff --git a/propLoader.gradle b/propLoader.gradle
index f8730ae..310d313 100644
--- a/propLoader.gradle
+++ b/propLoader.gradle
@@ -1,71 +1,80 @@
/**
* Helper build script that reads data binding variables and sets up the projects.
*/
-def root = ext.rootFolder
+def root = ext.dataBindingRootFolder
Properties databindingProperties = new Properties()
databindingProperties.load(new FileInputStream("${root}/databinding.properties"))
-def repoBase = databindingProperties.mavenRepoAbsolutePath == "." ? root : databindingProperties.mavenRepoAbsolutePath
-databindingProperties.mavenRepoDir = "${repoBase}/${databindingProperties.mavenRepoName}"
-databindingProperties.extraPluginsRepoDir = "${root}/${databindingProperties.extraPluginsRepoName}"
+// load android gradle plugin's version file
+apply from: "${root}/../buildSrc/base/version.gradle"
+
+databindingProperties.version = ext.buildVersion
+databindingProperties.compileSdkVersion = Integer.parseInt(databindingProperties.compileSdkVersionStr)
+def repoBase = databindingProperties.mavenRepoAbsolutePath == "." ? root : databindingProperties.mavenRepoAbsolutePath
+databindingProperties.androidGradlePluginRepoDir = "${root}/../../${databindingProperties.androidGradlePluginOutRepo}"
+databindingProperties.mavenRepoDir = "${databindingProperties.androidGradlePluginRepoDir}"
+databindingProperties.internalPrebuiltsRepoDir = "${root}/${databindingProperties.internalPrebuiltsRepoName}"
+databindingProperties.runProguard = project.hasProperty('runProguard') && project.getProperty('runProguard').equals("true")
+
+databindingProperties.inReleaseBuild = project.ext.hasProperty('release') && project.ext.release
+// load version from gradle build file
+apply from: "$root/../buildSrc/base/version.gradle"
+databindingProperties.androidPluginVersion=ext.buildVersion
+databindingProperties.isIndependent = rootProject.name.equals("data-binding")
databindingProperties.eapOutDir = "${root}/${databindingProperties.eapOutFolderName}"
databindingProperties.prebuildFolder = "${root}/${databindingProperties.prebuildFolderName}" +
"/${databindingProperties.releaseVersion}"
-ext.config = databindingProperties
-ext.config.externalPrebuiltsBase = "${root}/../../prebuilts"
+ext.dataBindingConfig = databindingProperties
+ext.dataBindingConfig.externalPrebuiltsBase = "${root}/../../prebuilts"
databindingProperties.megaRepoDir = "${databindingProperties.externalPrebuiltsBase}/tools/common/m2/repository"
-println "local maven repo is ${ext.config.mavenRepoDir}."
-println "local pre-build folder is ${ext.config.prebuildFolder}."
-println "mega-repo folder is ${ext.config.megaRepoDir}."
+new File(ext.dataBindingConfig.mavenRepoDir).mkdir()
+new File(ext.dataBindingConfig.prebuildFolder).mkdir()
-new File(ext.config.mavenRepoDir).mkdir()
-new File(ext.config.prebuildFolder).mkdir()
-
-def addRemoteRepos = !project.hasProperty('addRemoteRepos') || project.getProperty('addRemoteRepos').equals("true")
-ext.config.addRemoteRepos = addRemoteRepos
-def config = ext.config
-def localRepositories = ["${config.extraPluginsRepoDir}",
- "${config.megaRepoDir}",
- "${config.mavenRepoDir}",
- "${config.externalPrebuiltsBase}/maven_repo/android",
- "${config.externalPrebuiltsBase}/gradle-plugin",
- "${config.externalPrebuiltsBase}/tools/common/m2/repository"]
-ext.config.localRepositories = localRepositories
+def addRemoteRepos = project.hasProperty('addRemoteRepos') && project.getProperty('addRemoteRepos').equals("true")
+ext.dataBindingConfig.addRemoteRepos = addRemoteRepos
+ext.dataBindingConfig.sharedSupportRepoDir = "${ext.dataBindingConfig.externalPrebuiltsBase}/maven_repo/android"
+ext.dataBindingConfig.forGradlePlugin = project.hasProperty('forGradlePlugin') && project.getProperty('forGradlePlugin').equals("true")
+def dataBindingConfig = ext.dataBindingConfig
+def localRepositories = ["${dataBindingConfig.internalPrebuiltsRepoDir}",
+ "${dataBindingConfig.megaRepoDir}",
+ "${dataBindingConfig.mavenRepoDir}",
+ "${dataBindingConfig.externalPrebuiltsBase}/tools/common/offline-m2",
+ dataBindingConfig.sharedSupportRepoDir,
+ "${dataBindingConfig.externalPrebuiltsBase}/gradle-plugin",
+ "${dataBindingConfig.externalPrebuiltsBase}/tools/common/m2/repository",
+ "${databindingProperties.androidGradlePluginRepoDir}"]
+ext.dataBindingConfig.localRepositories = localRepositories
def addRepos(RepositoryHandler handler) {
- config.localRepositories.each { repo ->
+ dataBindingConfig.localRepositories.each { repo ->
handler.maven {
url repo
}
}
- handler.jcenter()
- handler.mavenCentral()
+ if (dataBindingConfig.addRemoteRepos) {
+ handler.jcenter()
+ handler.mavenCentral()
+ }
}
ext.addRepos = this.&addRepos
subprojects {
buildscript {
- config.localRepositories.each { repo ->
- repositories.maven {
- url repo
- }
- }
- repositories {
- if (config.addRemoteRepos) {
- jcenter()
- mavenCentral()
- }
- }
+ addRepos(repositories)
}
repositories {
- config.localRepositories.each { repo ->
- repositories.maven {
- url repo
- }
- }
- if (config.addRemoteRepos) {
- jcenter()
- mavenCentral()
- }
+ addRepos(repositories)
}
}
+
+def enablePublishing(p, addJavadoc) {
+ if (!ext.dataBindingConfig.isIndependent) {
+ p.apply from: "$dataBindingRootFolder/../buildSrc/base/publish.gradle"
+ p.apply from: "$dataBindingRootFolder/../buildSrc/base/bintray.gradle"
+ if (addJavadoc) {
+ p.apply from: "$dataBindingRootFolder/../buildSrc/base/javadoc.gradle"
+ }
+ rootProject.project(":base:gradle-core").publishLocal.dependsOn p.publishLocal
+ }
+}
+ext.enablePublishing = this.&enablePublishing
\ No newline at end of file
diff --git a/samples/BindingDemo/app/build.gradle b/samples/BindingDemo/app/build.gradle
index 937ec65..651e33e 100644
--- a/samples/BindingDemo/app/build.gradle
+++ b/samples/BindingDemo/app/build.gradle
@@ -15,11 +15,10 @@
*/
apply plugin: 'com.android.application'
-apply plugin: 'com.android.databinding'
android {
- compileSdkVersion 22
- buildToolsVersion "22.0.1"
+ compileSdkVersion dataBindingConfig.compileSdkVersion
+ buildToolsVersion dataBindingConfig.buildToolsVersion
defaultConfig {
applicationId "com.android.bindingdemo"
@@ -28,6 +27,9 @@
versionCode 1
versionName "1.0"
}
+ dataBinding {
+ enabled = true
+ }
compileOptions {
sourceCompatibility JavaVersion.VERSION_1_7
targetCompatibility JavaVersion.VERSION_1_7
diff --git a/samples/BindingDemo/app/src/main/java/com/android/example/bindingdemo/MainActivity.java b/samples/BindingDemo/app/src/main/java/com/android/example/bindingdemo/MainActivity.java
index 93dc4c8..68c76c9 100644
--- a/samples/BindingDemo/app/src/main/java/com/android/example/bindingdemo/MainActivity.java
+++ b/samples/BindingDemo/app/src/main/java/com/android/example/bindingdemo/MainActivity.java
@@ -69,47 +69,34 @@
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());
+ public void onSave(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() {
+ public void onUnselect (View v) {
+ setSelected(null);
+ }
- @Override
- public void onClick(View v) {
- setSelected(null);
+ public void onDelete(View v) {
+ if (selected == null) {
+ return;
}
- };
-
- @Bindable
- public View.OnClickListener onDelete = new View.OnClickListener() {
- @Override
- public void onClick(View v) {
- if (selected == null) {
- return;
- }
- if (selected.getGroup() == User.KITTEN) {
- 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.KITTEN);
- robotAdapter.remove(selected);
- }
+ if (selected.getGroup() == User.KITTEN) {
+ 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.KITTEN);
+ robotAdapter.remove(selected);
}
- };
+ }
@Override
diff --git a/samples/BindingDemo/app/src/main/res/layout/list_item.xml b/samples/BindingDemo/app/src/main/res/layout/list_item.xml
index d58221f..26b937f 100644
--- a/samples/BindingDemo/app/src/main/res/layout/list_item.xml
+++ b/samples/BindingDemo/app/src/main/res/layout/list_item.xml
@@ -14,7 +14,9 @@
See the License for the specific language governing permissions and
limitations under the License.
-->
-
+<!--DISCLAIMER: This is a demo layout showing various calculations you can make in binding
+expressions. It is usually a good practice to keep these expressions simple and use a ViewModel
+if they are getting complex.-->
<layout xmlns:card_view="http://schemas.android.com/apk/res-auto"
xmlns:bind="http://schemas.android.com/apk/res-auto"
xmlns:android="http://schemas.android.com/apk/res/android"
diff --git a/samples/BindingDemo/app/src/main/res/layout/main_activity.xml b/samples/BindingDemo/app/src/main/res/layout/main_activity.xml
index 1898918..c16a94f 100644
--- a/samples/BindingDemo/app/src/main/res/layout/main_activity.xml
+++ b/samples/BindingDemo/app/src/main/res/layout/main_activity.xml
@@ -14,6 +14,9 @@
limitations under the License.
-->
+<!--DISCLAIMER: This is a demo layout showing various calculations you can make in binding
+expressions. It is usually a good practice to keep these expressions simple and use a ViewModel
+if they are getting complex.-->
<layout 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">
@@ -38,7 +41,7 @@
android:id="@+id/activityRoot"
tools:activity=".MainActivity"
android:clickable="true"
- android:onClickListener="@{activity.onUnselect}">
+ android:onClick="@{activity.onUnselect}">
<TextView
android:id="@+id/title"
android:layout_width="wrap_content"
@@ -104,7 +107,7 @@
android:text="@{activity.selected.lastName}" />
<Button
android:id="@+id/edit_button"
- bind:onClickListener="@{activity.onSave}"
+ android:onClick="@{activity.onSave}"
android:text='@{"Save changes to " + activity.selected.name}'
android:layout_width="wrap_content"
android:layout_height="wrap_content"
@@ -114,7 +117,7 @@
<Button
android:id="@+id/delete_button"
- bind:onClickListener="@{activity.onDelete}"
+ android:onClick="@{activity.onDelete}"
android:text="@{activity.getString(activity.selected.group == User.KITTEN ? Strings.became_robot : Strings.became_kitten, activity.selected.name)}"
android:layout_width="wrap_content"
android:layout_height="wrap_content"
diff --git a/samples/BindingDemo/build.gradle b/samples/BindingDemo/build.gradle
index 3a8758a..46aa275 100644
--- a/samples/BindingDemo/build.gradle
+++ b/samples/BindingDemo/build.gradle
@@ -14,32 +14,12 @@
* limitations under the License.
*/
-// Top-level build file where you can add configuration options common to all sub-projects/modules.
+// Top-level build file where you can add dataBindingConfiguration options common to all sub-projects/modules.
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 "$projectDir/../../maven-repo"
- }
- mavenCentral()
- }
+ ext.dataBindingRootFolder = new File(project.projectDir, "../..")
+ apply from: "${project.projectDir}/../../propLoader.gradle"
+ ext.addRepos(repositories)
dependencies {
- classpath "com.android.tools.build:gradle:${config.androidPluginVersion}"
- classpath "com.android.databinding:dataBinder:${config.version}"
- }
-}
-
-allprojects {
- repositories {
- jcenter()
- maven {
- url "$projectDir/../../../maven-repo"
- }
- mavenCentral()
+ classpath "com.android.tools.build:gradle:${dataBindingConfig.androidPluginVersion}"
}
}
diff --git a/samples/BindingDemo/gradle/wrapper/gradle-wrapper.properties b/samples/BindingDemo/gradle/wrapper/gradle-wrapper.properties
index f02c72a..de9d515 100644
--- a/samples/BindingDemo/gradle/wrapper/gradle-wrapper.properties
+++ b/samples/BindingDemo/gradle/wrapper/gradle-wrapper.properties
@@ -3,4 +3,4 @@
distributionPath=wrapper/dists
zipStoreBase=GRADLE_USER_HOME
zipStorePath=wrapper/dists
-distributionUrl=https\://services.gradle.org/distributions/gradle-2.2.1-all.zip
+distributionUrl=https\://services.gradle.org/distributions/gradle-2.10-all.zip
diff --git a/settings.gradle b/settings.gradle
index 0d4e57c..24b44d3 100644
--- a/settings.gradle
+++ b/settings.gradle
@@ -1,7 +1,9 @@
-include ':baseLibrary'
-include ':library'
-include ':compiler'
-include ':gradlePlugin'
-include 'compilationTests'
-include 'compilerCommon'
+include ':dataBinding:baseLibrary'
+project(':dataBinding:baseLibrary').projectDir = new File("baseLibrary")
+include ':dataBinding:compiler'
+project(':dataBinding:compiler').projectDir = new File("compiler")
+include ':dataBinding:compilationTests'
+project(':dataBinding:compilationTests').projectDir = new File("compilationTests")
+include ':dataBinding:compilerCommon'
+project(':dataBinding:compilerCommon').projectDir = new File("compilerCommon")
diff --git a/supportBundle.gradle b/supportBundle.gradle
new file mode 100644
index 0000000..94e20ad
--- /dev/null
+++ b/supportBundle.gradle
@@ -0,0 +1,133 @@
+buildscript {
+ ext.addRepos(repositories)
+ dependencies {
+ classpath 'com.google.guava:guava:18.0'
+ }
+}
+import com.google.common.io.Files
+import com.google.common.base.Charsets
+import com.google.common.hash.HashCode
+import com.google.common.hash.HashFunction
+import com.google.common.hash.Hashing
+import java.nio.charset.Charset
+
+
+ext.extraVersion = 22
+ext.supportRepoOut = ''
+ext.buildToolsVersion = '22.1.0'
+ext.buildNumber = Integer.toString(ext.extraVersion)
+
+/*
+ * With the build server you are given two env variables.
+ * The OUT_DIR is a temporary directory you can use to put things during the build.
+ * The DIST_DIR is where you want to save things from the build.
+ *
+ * The build server will copy the contents of DIST_DIR to somewhere and make it available.
+ */
+if (System.env.DIST_DIR != null && System.env.OUT_DIR != null) {
+ buildDir = new File(System.env.OUT_DIR + '/gradle/frameworks/support/build').getCanonicalFile()
+ project.ext.distDir = new File(System.env.DIST_DIR).getCanonicalFile()
+
+ // the build server does not pass the build number so we infer it from the last folder of the dist path.
+ ext.buildNumber = project.ext.distDir.getName()
+} else {
+ buildDir = file('../../out/host/gradle/frameworks/support/build')
+ project.ext.distDir = file('../../out/dist')
+}
+project.ext.distDir.mkdirs()
+ext.supportRepoOut = new File(buildDir, 'support_repo')
+
+// Main task called by the build server.
+task(createArchive) << {
+}
+createArchive.doLast {
+ println "support repo out ${project.ext.supportRepoOut}"
+ println "dist dir ${project.ext.distDir}"
+
+}
+
+// upload anchor for subprojects to upload their artifacts
+// to the local repo.
+task(mainUpload) << {
+}
+
+// repository creation task
+task createRepository(type: Zip, dependsOn: mainUpload) {
+ from project.ext.supportRepoOut
+ destinationDir project.ext.distDir
+ into 'm2repository'
+ baseName = String.format("sdk-repo-linux-m2repository-%s", project.ext.buildNumber)
+}
+createArchive.dependsOn createRepository
+// prepare repository with older versions
+task unzipRepo(type: Copy) {
+ from "${dataBindingConfig.externalPrebuiltsBase}/maven_repo/android"
+ into project.ext.supportRepoOut
+}
+
+unzipRepo.doFirst {
+ project.ext.supportRepoOut.deleteDir()
+ project.ext.supportRepoOut.mkdirs()
+}
+
+// anchor for prepare repo. This is post unzip + sourceProp.
+task(prepareRepo) << {
+}
+
+task(createXml) << {
+ def repoArchive = createRepository.archivePath
+ def repoArchiveName = createRepository.archiveName
+ def size = repoArchive.length()
+ def sha1 = getSha1(repoArchive)
+
+ def xml =
+ "<sdk:sdk-addon xmlns:xsi=\"http://www.w3.org/2001/XMLSchema-instance\" xmlns:sdk=\"http://schemas.android.com/sdk/android/addon/6\">\n\
+ <sdk:extra>\n\
+ <sdk:revision>\n\
+ <sdk:major>${project.ext.extraVersion}</sdk:major>\n\
+ </sdk:revision>\n\
+ <sdk:vendor-display>Android</sdk:vendor-display>\n\
+ <sdk:vendor-id>android</sdk:vendor-id>\n\
+ <sdk:name-display>Local Maven repository for Support Libraries</sdk:name-display>\n\
+ <sdk:path>m2repository</sdk:path>\n\
+ <sdk:archives>\n\
+ <sdk:archive>\n\
+ <sdk:size>${size}</sdk:size>\n\
+ <sdk:checksum type=\"sha1\">${sha1}</sdk:checksum>\n\
+ <sdk:url>${repoArchiveName}</sdk:url>\n\
+ </sdk:archive>\n\
+ </sdk:archives>\n\
+ </sdk:extra>\n\
+</sdk:sdk-addon>"
+
+ Files.write(xml, new File(project.ext.distDir, 'repo-extras.xml'), Charsets.UTF_8)
+}
+createArchive.dependsOn createXml
+
+task(createSourceProp) << {
+ def sourceProp =
+ "Extra.VendorDisplay=Android\n\
+Extra.Path=m2repository\n\
+Archive.Arch=ANY\n\
+Extra.NameDisplay=Android Support Repository\n\
+Archive.Os=ANY\n\
+Pkg.Revision=${project.ext.extraVersion}.0.0\n\
+Extra.VendorId=android"
+ Files.write(sourceProp, new File(project.ext.supportRepoOut, 'source.properties'), Charsets.UTF_8)
+}
+createSourceProp.dependsOn unzipRepo
+prepareRepo.dependsOn createSourceProp
+
+
+def getSha1(File inputFile) {
+ HashFunction hashFunction = Hashing.sha1()
+ HashCode hashCode = hashFunction.hashString(inputFile.getAbsolutePath(), Charset.forName("UTF-8"))
+ return hashCode.toString()
+}
+
+task(bundleSupportLib) << {
+
+}
+createRepository.dependsOn prepareRepo
+createRepository.dependsOn unzipRepo
+createRepository.dependsOn bundleSupportLib