Support for layout files in multiple resource folders
Multiple layout files with the same name now share a common interface. They also share all variables no matter where it is defined. If a variable is NOT used in one of the layout files, its implementation does not create a field BUT STILL creates the setter (to implement the base interface). If the same view id is used for two different types of views, return type in the interface is android.view.View. If it is an include, the return value is IViewDataBinder. Change-Id: Ie3cc2bb8ec5ea48b71337e314ec588a050d714df
This commit is contained in:
@@ -10,15 +10,14 @@
|
||||
* See the License for the specific language governing permissions and
|
||||
* limitations under the License.
|
||||
*/
|
||||
|
||||
buildscript {
|
||||
repositories {
|
||||
jcenter()
|
||||
mavenLocal()
|
||||
}
|
||||
dependencies {
|
||||
classpath 'com.android.tools.build:gradle:1.0.0'
|
||||
classpath 'com.android.databinding:dataBinder:0.3-SNAPSHOT'
|
||||
classpath "com.android.tools.build:gradle:$androidPluginVersion"
|
||||
classpath "com.android.databinding:dataBinder:$version"
|
||||
}
|
||||
}
|
||||
apply plugin: 'com.android.application'
|
||||
|
||||
@@ -16,6 +16,7 @@ package com.android.databinding.testapp;
|
||||
import com.android.databinding.library.DataBinder;
|
||||
import com.android.databinding.library.IViewDataBinder;
|
||||
|
||||
import android.content.pm.ActivityInfo;
|
||||
import android.os.Looper;
|
||||
import android.test.ActivityInstrumentationTestCase2;
|
||||
|
||||
@@ -23,17 +24,24 @@ public class BaseDataBinderTest<T extends IViewDataBinder>
|
||||
extends ActivityInstrumentationTestCase2<TestActivity> {
|
||||
private Class<T> mBinderClass;
|
||||
private int mLayoutId;
|
||||
private int mOrientation;
|
||||
protected T mBinder;
|
||||
|
||||
public BaseDataBinderTest(final Class<T> binderClass, final int layoutId) {
|
||||
this(binderClass, layoutId, ActivityInfo.SCREEN_ORIENTATION_PORTRAIT);
|
||||
}
|
||||
|
||||
public BaseDataBinderTest(final Class<T> binderClass, final int layoutId, final int orientation) {
|
||||
super(TestActivity.class);
|
||||
mBinderClass = binderClass;
|
||||
mLayoutId = layoutId;
|
||||
mOrientation = orientation;
|
||||
}
|
||||
|
||||
@Override
|
||||
protected void setUp() throws Exception {
|
||||
super.setUp();
|
||||
getActivity().setRequestedOrientation(mOrientation);
|
||||
createBinder();
|
||||
}
|
||||
|
||||
@@ -47,6 +55,7 @@ public class BaseDataBinderTest<T extends IViewDataBinder>
|
||||
@Override
|
||||
public void run() {
|
||||
mBinder = DataBinder.createBinder(mBinderClass, getActivity(), mLayoutId, null);
|
||||
getActivity().setContentView(mBinder.getRoot());
|
||||
}
|
||||
});
|
||||
if (!isMainThread()) {
|
||||
@@ -54,4 +63,22 @@ public class BaseDataBinderTest<T extends IViewDataBinder>
|
||||
}
|
||||
assertNotNull(mBinder);
|
||||
}
|
||||
|
||||
protected void assertMethod(Class<?> klass, String methodName) throws NoSuchMethodException {
|
||||
assertEquals(klass, mBinder.getClass().getDeclaredMethod(methodName).getReturnType());
|
||||
}
|
||||
|
||||
protected void assertField(Class<?> klass, String fieldName) throws NoSuchFieldException {
|
||||
assertEquals(klass, mBinder.getClass().getDeclaredField(fieldName).getType());
|
||||
}
|
||||
|
||||
protected void assertNoField(String fieldName) {
|
||||
Exception[] ex = new Exception[1];
|
||||
try {
|
||||
mBinder.getClass().getDeclaredField(fieldName);
|
||||
} catch (NoSuchFieldException e) {
|
||||
ex[0] = e;
|
||||
}
|
||||
assertNotNull(ex[0]);
|
||||
}
|
||||
}
|
||||
|
||||
@@ -0,0 +1,25 @@
|
||||
/*
|
||||
* Copyright (C) 2015 The Android Open Source Project
|
||||
* Licensed under the Apache License, Version 2.0 (the "License");
|
||||
* you may not use this file except in compliance with the License.
|
||||
* You may obtain a copy of the License at
|
||||
* http://www.apache.org/licenses/LICENSE-2.0
|
||||
* Unless required by applicable law or agreed to in writing, software
|
||||
* distributed under the License is distributed on an "AS IS" BASIS,
|
||||
* WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
|
||||
* See the License for the specific language governing permissions and
|
||||
* limitations under the License.
|
||||
*/
|
||||
|
||||
package com.android.databinding.testapp;
|
||||
|
||||
import com.android.databinding.library.IViewDataBinder;
|
||||
|
||||
import android.content.pm.ActivityInfo;
|
||||
|
||||
public class BaseLandDataBinderTest<T extends IViewDataBinder> extends BaseDataBinderTest<T> {
|
||||
|
||||
public BaseLandDataBinderTest(Class<T> binderClass, int layoutId) {
|
||||
super(binderClass, layoutId, ActivityInfo.SCREEN_ORIENTATION_LANDSCAPE);
|
||||
}
|
||||
}
|
||||
@@ -0,0 +1,18 @@
|
||||
/*
|
||||
* Copyright (C) 2015 The Android Open Source Project
|
||||
* Licensed under the Apache License, Version 2.0 (the "License");
|
||||
* you may not use this file except in compliance with the License.
|
||||
* You may obtain a copy of the License at
|
||||
* http://www.apache.org/licenses/LICENSE-2.0
|
||||
* Unless required by applicable law or agreed to in writing, software
|
||||
* distributed under the License is distributed on an "AS IS" BASIS,
|
||||
* WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
|
||||
* See the License for the specific language governing permissions and
|
||||
* limitations under the License.
|
||||
*/
|
||||
|
||||
package com.android.databinding.testapp;
|
||||
|
||||
public class LandDataBinderTest {
|
||||
|
||||
}
|
||||
@@ -20,6 +20,7 @@ import com.android.databinding.testapp.vo.ViewBindingObject;
|
||||
|
||||
import android.content.res.ColorStateList;
|
||||
import android.os.Build;
|
||||
import android.test.UiThreadTest;
|
||||
import android.view.View;
|
||||
|
||||
public class ViewBindingAdapterTest extends BaseDataBinderTest<ViewAdapterTestBinder> {
|
||||
@@ -33,8 +34,18 @@ public class ViewBindingAdapterTest extends BaseDataBinderTest<ViewAdapterTestBi
|
||||
@Override
|
||||
protected void setUp() throws Exception {
|
||||
super.setUp();
|
||||
mBinder.setViewBinding(mViewBindingObject);
|
||||
mBinder.rebindDirty();
|
||||
try {
|
||||
runTestOnUiThread(new Runnable() {
|
||||
@Override
|
||||
public void run() {
|
||||
mBinder.setViewBinding(mViewBindingObject);
|
||||
mBinder.rebindDirty();
|
||||
}
|
||||
});
|
||||
} catch (Throwable throwable) {
|
||||
throwable.printStackTrace();
|
||||
}
|
||||
|
||||
}
|
||||
|
||||
private void changeValues() throws Throwable {
|
||||
|
||||
@@ -0,0 +1,60 @@
|
||||
/*
|
||||
* Copyright (C) 2015 The Android Open Source Project
|
||||
* Licensed under the Apache License, Version 2.0 (the "License");
|
||||
* you may not use this file except in compliance with the License.
|
||||
* You may obtain a copy of the License at
|
||||
* http://www.apache.org/licenses/LICENSE-2.0
|
||||
* Unless required by applicable law or agreed to in writing, software
|
||||
* distributed under the License is distributed on an "AS IS" BASIS,
|
||||
* WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
|
||||
* See the License for the specific language governing permissions and
|
||||
* limitations under the License.
|
||||
*/
|
||||
|
||||
package com.android.databinding.testapp.multiconfig;
|
||||
|
||||
import com.android.databinding.library.IViewDataBinder;
|
||||
import com.android.databinding.testapp.BaseLandDataBinderTest;
|
||||
import com.android.databinding.testapp.R;
|
||||
import com.android.databinding.testapp.generated.BasicBindingBinder;
|
||||
import com.android.databinding.testapp.generated.ConditionalBindingBinder;
|
||||
import com.android.databinding.testapp.generated.IncludedLayoutBinder;
|
||||
import com.android.databinding.testapp.generated.MultiResLayoutBinder;
|
||||
import com.android.databinding.testapp.vo.NotBindableVo;
|
||||
|
||||
import android.view.View;
|
||||
import android.widget.TextView;
|
||||
|
||||
public class LandscapeConfigTest extends BaseLandDataBinderTest<MultiResLayoutBinder> {
|
||||
|
||||
public LandscapeConfigTest() {
|
||||
super(MultiResLayoutBinder.class, R.layout.multi_res_layout);
|
||||
}
|
||||
|
||||
public void testSharedViewIdAndVariableInheritance()
|
||||
throws InterruptedException, NoSuchMethodException, NoSuchFieldException {
|
||||
assertEquals("MultiResLayoutBinderLandImpl", mBinder.getClass().getSimpleName());
|
||||
assertMethod(TextView.class, "getObjectInLandTextView");
|
||||
assertMethod(TextView.class, "getObjectInDefaultTextView");
|
||||
assertMethod(View.class, "getObjectInDefaultTextView2");
|
||||
|
||||
assertField(TextView.class, "mObjectInLandTextView");
|
||||
assertField(TextView.class, "mObjectInDefaultTextView");
|
||||
assertField(TextView.class, "mObjectInDefaultTextView2");
|
||||
|
||||
assertField(NotBindableVo.class, "mObjectInLand");
|
||||
assertField(NotBindableVo.class, "mObjectInDefault");
|
||||
|
||||
// includes
|
||||
assertMethod(IViewDataBinder.class, "getIncludedLayoutConflict");
|
||||
assertMethod(BasicBindingBinder.class, "getIncludedLayoutShared");
|
||||
assertMethod(ConditionalBindingBinder.class, "getIncludedLayoutPort");
|
||||
assertMethod(ConditionalBindingBinder.class, "getIncludedLayoutLand");
|
||||
|
||||
assertField(IncludedLayoutBinder.class, "mIncludedLayoutConflict");
|
||||
assertField(BasicBindingBinder.class, "mIncludedLayoutShared");
|
||||
assertField(ConditionalBindingBinder.class, "mIncludedLayoutLand");
|
||||
|
||||
assertNoField("mIncludedLayoutPort");
|
||||
}
|
||||
}
|
||||
@@ -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 com.android.databinding.testapp.multiconfig;
|
||||
|
||||
import com.android.databinding.library.IViewDataBinder;
|
||||
import com.android.databinding.testapp.BaseDataBinderTest;
|
||||
import com.android.databinding.testapp.R;
|
||||
import com.android.databinding.testapp.generated.BasicBindingBinder;
|
||||
import com.android.databinding.testapp.generated.ConditionalBindingBinder;
|
||||
import com.android.databinding.testapp.generated.IncludedLayoutBinder;
|
||||
import com.android.databinding.testapp.generated.MultiResLayoutBinder;
|
||||
import com.android.databinding.testapp.vo.NotBindableVo;
|
||||
|
||||
import android.content.pm.ActivityInfo;
|
||||
import android.view.View;
|
||||
import android.widget.EditText;
|
||||
import android.widget.TextView;
|
||||
|
||||
public class PortraitConfigTest extends BaseDataBinderTest<MultiResLayoutBinder> {
|
||||
public PortraitConfigTest() {
|
||||
super(MultiResLayoutBinder.class, R.layout.multi_res_layout, ActivityInfo.SCREEN_ORIENTATION_PORTRAIT);
|
||||
}
|
||||
|
||||
public void testSharedViewIdAndVariableInheritance()
|
||||
throws InterruptedException, NoSuchMethodException, NoSuchFieldException {
|
||||
assertEquals("MultiResLayoutBinderImpl", mBinder.getClass().getSimpleName());
|
||||
assertEquals("MultiResLayoutBinderImpl", mBinder.getClass().getSimpleName());
|
||||
assertMethod(TextView.class, "getObjectInLandTextView");
|
||||
assertMethod(TextView.class, "getObjectInDefaultTextView");
|
||||
assertMethod(View.class, "getObjectInDefaultTextView2");
|
||||
|
||||
assertNoField("mObjectInLandTextView");
|
||||
assertField(TextView.class, "mObjectInDefaultTextView");
|
||||
assertField(EditText.class, "mObjectInDefaultTextView2");
|
||||
|
||||
assertNoField("mObjectInLand");
|
||||
assertField(NotBindableVo.class, "mObjectInDefault");
|
||||
|
||||
|
||||
// includes
|
||||
assertMethod(IViewDataBinder.class, "getIncludedLayoutConflict");
|
||||
assertMethod(BasicBindingBinder.class, "getIncludedLayoutShared");
|
||||
assertMethod(ConditionalBindingBinder.class, "getIncludedLayoutPort");
|
||||
assertMethod(ConditionalBindingBinder.class, "getIncludedLayoutLand");
|
||||
|
||||
assertField(BasicBindingBinder.class, "mIncludedLayoutConflict");
|
||||
assertField(BasicBindingBinder.class, "mIncludedLayoutShared");
|
||||
assertField(ConditionalBindingBinder.class, "mIncludedLayoutPort");
|
||||
|
||||
assertNoField("mIncludedLayoutLand");
|
||||
}
|
||||
}
|
||||
@@ -18,7 +18,8 @@
|
||||
android:label="@string/app_name"
|
||||
android:icon="@drawable/ic_launcher"
|
||||
>
|
||||
<activity android:name=".TestActivity"/>
|
||||
<activity android:name=".TestActivity"
|
||||
android:screenOrientation="portrait"/>
|
||||
</application>
|
||||
|
||||
</manifest>
|
||||
|
||||
@@ -0,0 +1,41 @@
|
||||
<?xml version="1.0" encoding="utf-8"?>
|
||||
<!--
|
||||
~ Copyright (C) 2015 The Android Open Source Project
|
||||
~ Licensed under the Apache License, Version 2.0 (the "License");
|
||||
~ you may not use this file except in compliance with the License.
|
||||
~ You may obtain a copy of the License at
|
||||
~ http://www.apache.org/licenses/LICENSE-2.0
|
||||
~ Unless required by applicable law or agreed to in writing, software
|
||||
~ distributed under the License is distributed on an "AS IS" BASIS,
|
||||
~ WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
|
||||
~ See the License for the specific language governing permissions and
|
||||
~ limitations under the License.
|
||||
-->
|
||||
|
||||
<LinearLayout xmlns:android="http://schemas.android.com/apk/res/android"
|
||||
xmlns:bind="http://schemas.android.com/apk/res-auto"
|
||||
android:orientation="vertical"
|
||||
android:layout_width="match_parent"
|
||||
android:layout_height="match_parent">
|
||||
<variable name="objectInLand" type="com.android.databinding.testapp.vo.NotBindableVo"/>
|
||||
<TextView android:layout_width="wrap_content" android:layout_height="wrap_content"
|
||||
android:id="@+id/objectInLandTextView"
|
||||
android:text="{objectInLand.stringValue}"/>
|
||||
<TextView android:layout_width="wrap_content" android:layout_height="wrap_content"
|
||||
android:id="@+id/objectInDefaultTextView"
|
||||
android:text="{objectInDefault.stringValue}"/>
|
||||
<TextView android:layout_width="wrap_content" android:layout_height="wrap_content"
|
||||
android:id="@+id/objectInDefaultTextView2"
|
||||
android:text="{objectInDefault.stringValue}"/>
|
||||
|
||||
<include layout="@layout/included_layout" android:id="@+id/includedLayoutConflict"
|
||||
bind:innerObject="{objectInLand}"
|
||||
bind:innerValue="{`modified ` + objectInLand.intValue}"
|
||||
/>
|
||||
<include layout="@layout/basic_binding" android:id="@+id/includedLayoutShared"
|
||||
bind:a="{objectInDefault.stringValue}"
|
||||
/>
|
||||
<include layout="@layout/conditional_binding" android:id="@+id/includedLayoutLand"
|
||||
bind:obj2="{objectInDefault}"
|
||||
/>
|
||||
</LinearLayout>
|
||||
@@ -0,0 +1,38 @@
|
||||
<?xml version="1.0" encoding="utf-8"?>
|
||||
<!--
|
||||
~ Copyright (C) 2015 The Android Open Source Project
|
||||
~ Licensed under the Apache License, Version 2.0 (the "License");
|
||||
~ you may not use this file except in compliance with the License.
|
||||
~ You may obtain a copy of the License at
|
||||
~ http://www.apache.org/licenses/LICENSE-2.0
|
||||
~ Unless required by applicable law or agreed to in writing, software
|
||||
~ distributed under the License is distributed on an "AS IS" BASIS,
|
||||
~ WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
|
||||
~ See the License for the specific language governing permissions and
|
||||
~ limitations under the License.
|
||||
-->
|
||||
|
||||
<LinearLayout xmlns:android="http://schemas.android.com/apk/res/android"
|
||||
xmlns:bind="http://schemas.android.com/apk/res-auto"
|
||||
android:orientation="vertical"
|
||||
android:layout_width="match_parent"
|
||||
android:layout_height="match_parent">
|
||||
<variable name="objectInDefault" type="com.android.databinding.testapp.vo.NotBindableVo"/>
|
||||
<TextView android:layout_width="wrap_content" android:layout_height="wrap_content"
|
||||
android:id="@+id/objectInDefaultTextView"
|
||||
android:text="{objectInDefault.stringValue}"/>
|
||||
<EditText android:layout_width="wrap_content" android:layout_height="wrap_content"
|
||||
android:id="@+id/objectInDefaultTextView2"
|
||||
android:text="{objectInDefault.stringValue}"/>
|
||||
|
||||
<include layout="@layout/basic_binding" android:id="@+id/includedLayoutConflict"
|
||||
bind:a="{objectInDefault.stringValue}"
|
||||
/>
|
||||
<include layout="@layout/basic_binding" android:id="@+id/includedLayoutShared"
|
||||
bind:a="{objectInDefault.stringValue}"
|
||||
/>
|
||||
<include layout="@layout/conditional_binding" android:id="@+id/includedLayoutPort"
|
||||
bind:cond1="{objectInDefault == null}"
|
||||
/>
|
||||
|
||||
</LinearLayout>
|
||||
@@ -1,62 +1,9 @@
|
||||
/*
|
||||
* Copyright (C) 2014 The Android Open Source Project
|
||||
*
|
||||
* Licensed under the Apache License, Version 2.0 (the "License");
|
||||
* you may not use this file except in compliance with the License.
|
||||
* You may obtain a copy of the License at
|
||||
*
|
||||
* http://www.apache.org/licenses/LICENSE-2.0
|
||||
*
|
||||
* Unless required by applicable law or agreed to in writing, software
|
||||
* distributed under the License is distributed on an "AS IS" BASIS,
|
||||
* WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
|
||||
* See the License for the specific language governing permissions and
|
||||
* limitations under the License.
|
||||
*/
|
||||
|
||||
apply plugin: 'java'
|
||||
apply plugin: 'maven'
|
||||
apply plugin: 'application'
|
||||
|
||||
sourceCompatibility = 1.7
|
||||
mainClassName = "org.antlr.v4.Tool"
|
||||
|
||||
buildscript {
|
||||
repositories {
|
||||
mavenLocal()
|
||||
mavenCentral()
|
||||
}
|
||||
}
|
||||
|
||||
repositories {
|
||||
mavenCentral()
|
||||
}
|
||||
|
||||
sourceSets {
|
||||
main {
|
||||
java {
|
||||
srcDir 'src/main/java'
|
||||
}
|
||||
}
|
||||
test {
|
||||
java {
|
||||
srcDir 'src/test/java'
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
dependencies {
|
||||
compile 'com.tunnelvisionlabs:antlr4:4.4'
|
||||
testCompile 'junit:junit:4.11'
|
||||
}
|
||||
|
||||
uploadArchives {
|
||||
repositories {
|
||||
mavenDeployer {
|
||||
repository(url: mavenLocal().url)
|
||||
pom.version = '0.3-SNAPSHOT'
|
||||
pom.artifactId = 'baseLibrary'
|
||||
pom.groupId='com.android.databinding'
|
||||
}
|
||||
}
|
||||
}
|
||||
testCompile group: 'junit', name: 'junit', version: '4.11'
|
||||
}
|
||||
@@ -1,6 +1,7 @@
|
||||
ext.kotlinVersion = '0.10.195'
|
||||
ext.releaseVersion = "0.3"
|
||||
ext.snapshotVersion = "0.3-SNAPSHOT"
|
||||
ext.androidPluginVersion = "1.0.0"
|
||||
|
||||
subprojects {
|
||||
group = 'com.android.databinding'
|
||||
|
||||
@@ -25,26 +25,39 @@ import java.util.ArrayList;
|
||||
import java.util.List;
|
||||
|
||||
public class BindingTarget {
|
||||
Node mNode;
|
||||
String mId;
|
||||
String mViewClass;
|
||||
List<Binding> mBindings = new ArrayList<>();
|
||||
ExprModel mModel;
|
||||
Class mResolvedClass;
|
||||
String mIncludedLayout;
|
||||
// if this target presents itself in multiple layout files with different view types,
|
||||
// it receives an interface type and should use it in the getter instead.
|
||||
String mInterfaceType;
|
||||
// if this target is inherited from a common layout interface, used is false so that we don't
|
||||
// create find view by id etc for it.
|
||||
boolean mUsed;
|
||||
|
||||
public BindingTarget(Node node, String id, String viewClass) {
|
||||
mNode = node;
|
||||
public BindingTarget(String id, String viewClass, boolean used) {
|
||||
mId = id;
|
||||
mViewClass = viewClass;
|
||||
mUsed = used;
|
||||
}
|
||||
|
||||
public boolean isUsed() {
|
||||
return mUsed;
|
||||
}
|
||||
|
||||
public void addBinding(String name, Expr expr) {
|
||||
mBindings.add(new Binding(this, name, expr));
|
||||
}
|
||||
|
||||
public Node getNode() {
|
||||
return mNode;
|
||||
public void setInterfaceType(String interfaceType) {
|
||||
mInterfaceType = interfaceType;
|
||||
}
|
||||
|
||||
public String getInterfaceType() {
|
||||
return mInterfaceType == null ? mViewClass : mInterfaceType;
|
||||
}
|
||||
|
||||
public String getId() {
|
||||
|
||||
@@ -31,6 +31,7 @@ import org.xml.sax.SAXException;
|
||||
import java.io.File;
|
||||
import java.io.IOException;
|
||||
import java.util.ArrayList;
|
||||
import java.util.HashMap;
|
||||
import java.util.List;
|
||||
import java.util.Objects;
|
||||
|
||||
@@ -53,7 +54,7 @@ public class DataBinder {
|
||||
private static final String XPATH_IMPORT_DEFINITIONS = "//import";
|
||||
final String LAYOUT_PREFIX = "@layout/";
|
||||
|
||||
List<LayoutBinder> mLayoutBinders = new ArrayList<>();
|
||||
HashMap<String, List<LayoutBinder>> mLayoutBinders = new HashMap<>();
|
||||
|
||||
public LayoutBinder parseXml(File xml, String pkg)
|
||||
throws ParserConfigurationException, IOException, SAXException,
|
||||
@@ -120,7 +121,7 @@ public class DataBinder {
|
||||
fullClassName = getFullViewClassName(nodeName);
|
||||
}
|
||||
final BindingTarget bindingTarget = layoutBinder
|
||||
.createBindingTarget(parent, id.getNodeValue(), fullClassName);
|
||||
.createBindingTarget(id.getNodeValue(), fullClassName, true);
|
||||
bindingTarget.setIncludedLayout(layoutName);
|
||||
int attrCount = attributes.getLength();
|
||||
for (int i = 0; i < attrCount; i ++) {
|
||||
@@ -137,7 +138,10 @@ public class DataBinder {
|
||||
}
|
||||
|
||||
if (!layoutBinder.isEmpty()) {
|
||||
mLayoutBinders.add(layoutBinder);
|
||||
if (!mLayoutBinders.containsKey(xml.getName())) {
|
||||
mLayoutBinders.put(xml.getName(), new ArrayList<LayoutBinder>());
|
||||
}
|
||||
mLayoutBinders.get(xml.getName()).add(layoutBinder);
|
||||
}
|
||||
return layoutBinder;
|
||||
}
|
||||
@@ -178,7 +182,7 @@ public class DataBinder {
|
||||
return viewName;
|
||||
}
|
||||
|
||||
public List<LayoutBinder> getLayoutBinders() {
|
||||
public HashMap<String, List<LayoutBinder>> getLayoutBinders() {
|
||||
return mLayoutBinders;
|
||||
}
|
||||
}
|
||||
|
||||
@@ -17,7 +17,11 @@
|
||||
package com.android.databinding;
|
||||
|
||||
import com.google.common.base.Preconditions;
|
||||
import com.google.common.collect.Iterables;
|
||||
|
||||
import com.android.databinding.expr.Dependency;
|
||||
import com.android.databinding.util.Log;
|
||||
import com.android.databinding.util.ParserHelper;
|
||||
import com.android.databinding.writer.LayoutBinderWriter;
|
||||
import com.android.databinding.expr.Expr;
|
||||
import com.android.databinding.expr.ExprModel;
|
||||
@@ -27,8 +31,13 @@ import com.android.databinding.util.L;
|
||||
|
||||
import org.w3c.dom.Node;
|
||||
|
||||
import java.io.File;
|
||||
import java.util.ArrayList;
|
||||
import java.util.HashMap;
|
||||
import java.util.HashSet;
|
||||
import java.util.List;
|
||||
import java.util.Map;
|
||||
import java.util.Set;
|
||||
|
||||
/**
|
||||
* Keeps all information about the bindings per layout file
|
||||
@@ -45,9 +54,16 @@ public class LayoutBinder {
|
||||
private String mProjectPackage;
|
||||
private String mBaseClassName;
|
||||
private String mLayoutname;
|
||||
private File mFile;
|
||||
private int id;
|
||||
private final HashMap<String, String> mUserDefinedVariables = new HashMap<>();
|
||||
private final HashMap<String, String> mUserDefinedImports = new HashMap<>();
|
||||
|
||||
private LayoutBinderWriter mWriter;
|
||||
|
||||
// layout has different definitions in different configurations
|
||||
private boolean mHasVariations = false;
|
||||
|
||||
public LayoutBinder(Node root) {
|
||||
mRoot = root;
|
||||
mExprModel = new ExprModel();
|
||||
@@ -55,22 +71,55 @@ public class LayoutBinder {
|
||||
mBindingTargets = new ArrayList<>();
|
||||
}
|
||||
|
||||
public void resolveWhichExpressionsAreUsed() {
|
||||
List<Expr> used = new ArrayList<>();
|
||||
for (BindingTarget target : mBindingTargets) {
|
||||
for (Binding binding : target.getBindings()) {
|
||||
binding.getExpr().setIsUsed(true);
|
||||
used.add(binding.getExpr());
|
||||
}
|
||||
}
|
||||
while (!used.isEmpty()) {
|
||||
Expr e = used.remove(used.size() - 1);
|
||||
for (Dependency dep : e.getDependencies()) {
|
||||
if (!dep.getOther().isUsed()) {
|
||||
used.add(dep.getOther());
|
||||
dep.getOther().setIsUsed(true);
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
public IdentifierExpr addVariable(String name, String type) {
|
||||
Preconditions.checkState(!mUserDefinedVariables.containsKey(name),
|
||||
"%s has already been defined as %s", name, type);
|
||||
final IdentifierExpr id = mExprModel.identifier(name);
|
||||
id.setUserDefinedType(type);
|
||||
id.enableDirectInvalidation();
|
||||
mUserDefinedVariables.put(name, type);
|
||||
return id;
|
||||
}
|
||||
|
||||
public StaticIdentifierExpr addImport(String alias, String type) {
|
||||
Preconditions.checkState(!mUserDefinedImports.containsKey(alias),
|
||||
"%s has already been defined as %s", alias, type);
|
||||
final StaticIdentifierExpr id = mExprModel.staticIdentifier(alias);
|
||||
L.d("adding import %s as %s klass: %s", type, alias, id.getClass().getSimpleName());
|
||||
id.setUserDefinedType(type);
|
||||
mUserDefinedImports.put(alias, type);
|
||||
return id;
|
||||
}
|
||||
|
||||
public BindingTarget createBindingTarget(Node parent, String nodeValue, String viewClassName) {
|
||||
final BindingTarget target = new BindingTarget(parent, nodeValue, viewClassName);
|
||||
public HashMap<String, String> getUserDefinedVariables() {
|
||||
return mUserDefinedVariables;
|
||||
}
|
||||
|
||||
public HashMap<String, String> getUserDefinedImports() {
|
||||
return mUserDefinedImports;
|
||||
}
|
||||
|
||||
public BindingTarget createBindingTarget(String nodeValue, String viewClassName, boolean used) {
|
||||
final BindingTarget target = new BindingTarget(nodeValue, viewClassName, used);
|
||||
mBindingTargets.add(target);
|
||||
target.setModel(mExprModel);
|
||||
return target;
|
||||
@@ -147,10 +196,48 @@ public class LayoutBinder {
|
||||
}
|
||||
|
||||
public String getClassName() {
|
||||
return mBaseClassName + "Impl";
|
||||
final String suffix;
|
||||
if (hasVariations()) {
|
||||
// append configuration specifiers.
|
||||
final String parentFileName = mFile.getParentFile().getName();
|
||||
L.d("parent file for %s is %s", mFile.getName(), parentFileName);
|
||||
if ("layout".equals(parentFileName)) {
|
||||
suffix = "";
|
||||
} else {
|
||||
suffix = ParserHelper.INSTANCE$.toClassName(parentFileName.substring("layout-".length()));
|
||||
}
|
||||
} else {
|
||||
suffix = "";
|
||||
}
|
||||
return mBaseClassName + suffix + "Impl";
|
||||
|
||||
}
|
||||
|
||||
public String getInterfaceName() {
|
||||
return mBaseClassName;
|
||||
}
|
||||
|
||||
public int getId() {
|
||||
return id;
|
||||
}
|
||||
|
||||
public void setId(int id) {
|
||||
this.id = id;
|
||||
}
|
||||
|
||||
public File getFile() {
|
||||
return mFile;
|
||||
}
|
||||
|
||||
public void setFile(File file) {
|
||||
mFile = file;
|
||||
}
|
||||
|
||||
public boolean hasVariations() {
|
||||
return mHasVariations;
|
||||
}
|
||||
|
||||
public void setHasVariations(boolean hasVariations) {
|
||||
mHasVariations = hasVariations;
|
||||
}
|
||||
}
|
||||
|
||||
@@ -87,6 +87,7 @@ abstract public class Expr {
|
||||
* Used by generators when this expression is resolved.
|
||||
*/
|
||||
private boolean mRead;
|
||||
private boolean mIsUsed = false;
|
||||
|
||||
Expr(Iterable<Expr> children) {
|
||||
for (Expr expr : children) {
|
||||
@@ -555,6 +556,14 @@ abstract public class Expr {
|
||||
return false;
|
||||
}
|
||||
|
||||
public void setIsUsed(boolean isUsed) {
|
||||
mIsUsed = isUsed;
|
||||
}
|
||||
|
||||
public boolean isUsed() {
|
||||
return mIsUsed;
|
||||
}
|
||||
|
||||
static class Node {
|
||||
|
||||
BitSet mBitSet = new BitSet();
|
||||
|
||||
@@ -52,6 +52,7 @@ import com.android.databinding.DataBinder
|
||||
import com.android.databinding.writer.DataBinderWriter
|
||||
import com.android.databinding.ClassAnalyzer
|
||||
import com.android.databinding.util.ParserHelper
|
||||
import com.google.common.base.Preconditions
|
||||
|
||||
|
||||
public class KLayoutParser(val appPkg : String, val resourceFolders : kotlin.Iterable<File>,
|
||||
@@ -97,26 +98,109 @@ public class KLayoutParser(val appPkg : String, val resourceFolders : kotlin.Ite
|
||||
findXmlFiles(it, xmlFiles)
|
||||
}
|
||||
}
|
||||
//viewBinderRenderers.clear()
|
||||
var layoutId = 0
|
||||
for (xml in xmlFiles) {
|
||||
log("xmlFile $xml")
|
||||
val layoutBinder = parseAndStripXml(xml, "$appPkg.generated")
|
||||
val layoutBinder = parseAndStripXml(xml, "$appPkg.generated", layoutId.toString())
|
||||
if (layoutBinder == null) {
|
||||
log("no bindings in $xml, skipping")
|
||||
continue
|
||||
}
|
||||
layoutBinder.setId(layoutId)
|
||||
layoutBinder.setProjectPackage("$appPkg");
|
||||
layoutBinder.setPackage("$appPkg.generated")
|
||||
layoutBinder.setBaseClassName("${ParserHelper.toClassName(xml.name)}Binder")
|
||||
layoutBinder.setLayoutname(toLayoutId(xml.name))
|
||||
|
||||
layoutBinder.setFile(xml)
|
||||
layoutId ++
|
||||
}
|
||||
validateMultiResFiles()
|
||||
dbr = DataBinderWriter("com.android.databinding.library", appPkg,
|
||||
"GeneratedDataBinderRenderer", jDataBinder.getLayoutBinders())
|
||||
}
|
||||
|
||||
/**
|
||||
* checks which layout fields are defined in multiple places. Ensures their
|
||||
* variables do not conflict
|
||||
*/
|
||||
public fun validateMultiResFiles() {
|
||||
jDataBinder.getLayoutBinders()
|
||||
.filter { it.getValue().size() > 1 }
|
||||
.forEach { layout ->
|
||||
// validate all ids are in correct view types
|
||||
// and all variables have the same name
|
||||
val variableTypes = hashMapOf<String, String>()
|
||||
val importTypes = hashMapOf<String, String>()
|
||||
|
||||
layout.getValue().forEach {
|
||||
it.setHasVariations(true)
|
||||
it.getUserDefinedVariables().forEach {
|
||||
Preconditions.checkState(variableTypes.getOrPut(it.getKey(), {it.getValue()}).equals(it.getValue()),
|
||||
"inconsistent variable types for %s for layout %s", it.getKey(), layout.key)
|
||||
}
|
||||
it.getUserDefinedImports().forEach {
|
||||
Preconditions.checkState(importTypes.getOrPut(it.getKey(), {it.getValue()}).equals(it.getValue()),
|
||||
"inconsistent import types for %s for layout %s", it.getKey(), layout.key)
|
||||
}
|
||||
}
|
||||
// now add missing ones to each to ensure they can be referenced
|
||||
Log.d { "checking for missing variables in ${layout.getKey()}" }
|
||||
layout.getValue().forEach { binder ->
|
||||
// TODO need to remove unused variables while generating the code.
|
||||
variableTypes.filterNot { binder.getUserDefinedVariables().containsKey(it.key) }
|
||||
.forEach {
|
||||
binder.addVariable(it.key, it.value)
|
||||
Log.d {"adding missing variable ${it.key} / ${it.value} to binder ${binder.getId()}"}
|
||||
}
|
||||
importTypes.filterNot { binder.getUserDefinedImports().containsKey(it.key) }
|
||||
.forEach {
|
||||
binder.addImport(it.key, it.value)
|
||||
Log.d {"adding missing import ${it.key} / ${it.value} to binder ${binder.getId()}"}
|
||||
}
|
||||
}
|
||||
val includeBindingIds = hashSetOf<String>()
|
||||
val viewBindingIds = hashSetOf<String>()
|
||||
val viewTypes = hashMapOf<String, String>()
|
||||
layout.getValue().forEach {
|
||||
it.getBindingTargets().forEach {
|
||||
if (it.isBinder()) {
|
||||
Preconditions.checkState(!viewBindingIds.contains(it.mViewClass),
|
||||
"""Cannot use the same id for a View and an include tag. Error
|
||||
for layout file ${layout.key}""")
|
||||
includeBindingIds.add(it.mViewClass)
|
||||
} else {
|
||||
Preconditions.checkState(!includeBindingIds.contains(it.mViewClass),
|
||||
"""Cannot use the same id for a View and an include tag. Error
|
||||
for layout file ${layout.key}""")
|
||||
viewBindingIds.add(it.mViewClass)
|
||||
}
|
||||
if (it.mViewClass != viewTypes.getOrPut(it.getId(), {it.mViewClass})) {
|
||||
if (it.isBinder()) {
|
||||
viewTypes.put(it.getId(), "com.android.databinding.library.IViewDataBinder")
|
||||
} else {
|
||||
viewTypes.put(it.getId(), "android.view.View")
|
||||
}
|
||||
|
||||
}
|
||||
}
|
||||
}
|
||||
layout.getValue().forEach { binder ->
|
||||
viewTypes.forEach { common ->
|
||||
val target = binder.getBindingTargets().firstOrNull { it.mId == common.key }
|
||||
if (target == null) {
|
||||
// undefined, just define
|
||||
binder.createBindingTarget(common.key, common.value, false)
|
||||
} else {
|
||||
// set type
|
||||
target.setInterfaceType(common.value)
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
public fun generatedCode() : Boolean {
|
||||
return jDataBinder.getLayoutBinders().isNotEmpty()
|
||||
return jDataBinder.getLayoutBinders().size() > 0
|
||||
}
|
||||
|
||||
public fun writeAttrFile() {
|
||||
@@ -127,7 +211,7 @@ public class KLayoutParser(val appPkg : String, val resourceFolders : kotlin.Ite
|
||||
public fun writeDbrFile() : Unit = writeDbrFile(dbrOutputDir)
|
||||
public fun writeDbrFile(dir : File) : Unit {
|
||||
dir.mkdirs()
|
||||
if (dbr.layoutBinders.isNotEmpty()) {
|
||||
if (dbr.layoutBinders.size() > 0) {
|
||||
writeToFile(File(dir, "${dbr.className}.java"), dbr.write())
|
||||
}
|
||||
}
|
||||
@@ -137,7 +221,7 @@ public class KLayoutParser(val appPkg : String, val resourceFolders : kotlin.Ite
|
||||
public fun writeViewBinderInterfaces(dir : File) : Unit {
|
||||
dir.mkdirs()
|
||||
jDataBinder.getLayoutBinders().forEach {
|
||||
writeToFile(File(dir, "${it.getInterfaceName()}.java"), it.writeViewBinderInterface())
|
||||
writeToFile(File(dir, "${it.value.first!!.getInterfaceName()}.java"), it.value.first!!.writeViewBinderInterface())
|
||||
}
|
||||
}
|
||||
|
||||
@@ -146,7 +230,9 @@ public class KLayoutParser(val appPkg : String, val resourceFolders : kotlin.Ite
|
||||
public fun writeViewBinders(dir : File) : Unit {
|
||||
dir.mkdirs()
|
||||
jDataBinder.getLayoutBinders().forEach {
|
||||
writeToFile(File(dir, "${it.getClassName()}.java"), it.writeViewBinder())
|
||||
it.getValue().forEach {
|
||||
writeToFile(File(dir, "${it.getClassName()}.java"), it.writeViewBinder())
|
||||
}
|
||||
}
|
||||
|
||||
}
|
||||
@@ -159,15 +245,15 @@ public class KLayoutParser(val appPkg : String, val resourceFolders : kotlin.Ite
|
||||
private fun toLayoutId(name:String) : String =
|
||||
name.substring(0, name.indexOf("."))
|
||||
|
||||
private fun stripBindingTags(xml : File) {
|
||||
val res = XmlEditor.strip(xml)
|
||||
private fun stripBindingTags(xml : File, newTag : String? = null) {
|
||||
val res = XmlEditor.strip(xml, newTag)
|
||||
if (res != null) {
|
||||
Log.d{"file ${xml.getName()} has changed, overwriting ${xml.getAbsolutePath()}"}
|
||||
xml.writeText(res)
|
||||
}
|
||||
}
|
||||
|
||||
private fun stripFileAndGetOriginal(xml : File) : File? {
|
||||
private fun stripFileAndGetOriginal(xml : File, binderId : String) : File? {
|
||||
System.out.println("parsing resourceY file ${xml.getAbsolutePath()}")
|
||||
val factory = DocumentBuilderFactory.newInstance()
|
||||
val builder = factory.newDocumentBuilder()
|
||||
@@ -194,14 +280,13 @@ public class KLayoutParser(val appPkg : String, val resourceFolders : kotlin.Ite
|
||||
val variableNodes = getVariableNodes(doc, xPath)
|
||||
var changed = variableNodes.getLength() > 0 //TODO do we need to check more?
|
||||
if (changed) {
|
||||
stripBindingTags(xml)
|
||||
stripBindingTags(xml, binderId)
|
||||
}
|
||||
|
||||
return actualFile
|
||||
}
|
||||
|
||||
private fun parseAndStripXml(xml : File, pkg : String) : LayoutBinder? {
|
||||
val original = stripFileAndGetOriginal(xml)
|
||||
private fun parseAndStripXml(xml : File, pkg : String, binderId : String) : LayoutBinder? {
|
||||
val original = stripFileAndGetOriginal(xml, binderId)
|
||||
return if (original == null) {
|
||||
null
|
||||
} else {
|
||||
|
||||
@@ -17,7 +17,7 @@ object ParserHelper {
|
||||
public fun toClassName(name:String) : String {
|
||||
val dot = name.indexOf(".")
|
||||
val stripped = if (dot == -1) name else name.substring(0, name.indexOf("."))
|
||||
return stripped.split("_").map { "${it.substring(0,1).toUpperCase()}${it.substring(1)}" }.join("")
|
||||
return stripped.split("[_-]").map { "${it.substring(0,1).toUpperCase()}${it.substring(1)}" }.join("")
|
||||
}
|
||||
|
||||
}
|
||||
@@ -28,6 +28,7 @@ import com.android.databinding.Position
|
||||
import com.android.databinding.toPosition
|
||||
import com.android.databinding.toEndPosition
|
||||
import java.util.Comparator
|
||||
import com.google.common.base.Preconditions
|
||||
|
||||
/**
|
||||
* Ugly inefficient class to strip unwanted tags from XML.
|
||||
@@ -35,9 +36,14 @@ import java.util.Comparator
|
||||
*/
|
||||
object XmlEditor {
|
||||
val reservedElementNames = arrayListOf("variable", "import")
|
||||
var rootNodeContext: XMLParser.ElementContext? = null
|
||||
var rootNodeHasTag = false
|
||||
val visitor = object : XMLParserBaseVisitor<MutableList<Pair<Position, Position>>>() {
|
||||
override fun visitAttribute(ctx: XMLParser.AttributeContext): MutableList<Pair<Position, Position>>? {
|
||||
log{"attr:${ctx.attrName.getText()} ${ctx.attrValue.getText()}"}
|
||||
log { "attr:${ctx.attrName.getText()} ${ctx.attrValue.getText()} . parent: ${ctx.getParent()}" }
|
||||
if (ctx.getParent() == rootNodeContext && ctx.attrName.getText() == "android:tag") {
|
||||
rootNodeHasTag = true
|
||||
}
|
||||
if (ctx.attrName.getText().startsWith("bind:")) {
|
||||
|
||||
return arrayListOf(Pair(ctx.getStart().toPosition(), ctx.getStop().toEndPosition()))
|
||||
@@ -50,7 +56,10 @@ object XmlEditor {
|
||||
}
|
||||
|
||||
override fun visitElement(ctx: XMLParser.ElementContext): MutableList<Pair<Position, Position>>? {
|
||||
log{"elm ${ctx.elmName.getText()} || ${ctx.Name()}"}
|
||||
log { "elm ${ctx.elmName.getText()} || ${ctx.Name()} paren : ${ctx.getParent()}" }
|
||||
if (rootNodeContext == null) {
|
||||
rootNodeContext = ctx
|
||||
}
|
||||
if (reservedElementNames.contains(ctx.elmName?.getText()) || ctx.elmName.getText().startsWith("bind:")) {
|
||||
return arrayListOf(Pair(ctx.getStart().toPosition(), ctx.getStop().toEndPosition()))
|
||||
}
|
||||
@@ -60,28 +69,37 @@ object XmlEditor {
|
||||
override fun defaultResult(): MutableList<Pair<Position, Position>>? = arrayListOf()
|
||||
|
||||
override fun aggregateResult(aggregate: MutableList<Pair<Position, Position>>?, nextResult: MutableList<Pair<Position, Position>>?): MutableList<Pair<Position, Position>>? =
|
||||
if (aggregate == null) {
|
||||
nextResult
|
||||
} else if (nextResult == null) {
|
||||
aggregate
|
||||
} else {
|
||||
aggregate.addAll(nextResult)
|
||||
aggregate
|
||||
}
|
||||
if (aggregate == null) {
|
||||
nextResult
|
||||
} else if (nextResult == null) {
|
||||
aggregate
|
||||
} else {
|
||||
aggregate.addAll(nextResult)
|
||||
aggregate
|
||||
}
|
||||
}
|
||||
|
||||
fun strip(f : File) : String? {
|
||||
fun strip(f: File, newTag: String? = null): String? {
|
||||
rootNodeContext = null //clear it
|
||||
rootNodeHasTag = false
|
||||
val inputStream = ANTLRInputStream(FileReader(f))
|
||||
val lexer = XMLLexer(inputStream)
|
||||
val tokenStream = CommonTokenStream(lexer)
|
||||
val parser = XMLParser(tokenStream)
|
||||
val expr = parser.document()
|
||||
val parsedExpr = expr.accept(visitor)
|
||||
if (parsedExpr.size() == 0) {
|
||||
if (parsedExpr.isEmpty()) {
|
||||
return null//nothing to strip
|
||||
}
|
||||
Preconditions.checkNotNull(rootNodeContext, "Cannot find root node for ${f.getName()}")
|
||||
Preconditions.checkState(rootNodeHasTag == false, """You cannot set a tag in the layout
|
||||
root if you are using binding. Invalid file: ${f}""")
|
||||
val rootNodeBounds = Pair(rootNodeContext!!.getStart().toPosition(), rootNodeContext!!.getStop().toEndPosition())
|
||||
|
||||
log { "root node bounds: ${rootNodeBounds}" }
|
||||
val out = StringBuilder()
|
||||
val lines = f.readLines("utf-8")
|
||||
|
||||
lines.forEach { out.appendln(it) }
|
||||
|
||||
// TODO we probably don't need to sort
|
||||
@@ -95,22 +113,30 @@ object XmlEditor {
|
||||
}
|
||||
})
|
||||
var lineStarts = arrayListOf(0)
|
||||
lines.withIndices().forEach {
|
||||
if (it.first > 0) {
|
||||
lineStarts.add(lineStarts[it.first - 1] + lines[it.first - 1].length() + 1)
|
||||
lines.withIndex().forEach {
|
||||
if (it.index > 0) {
|
||||
lineStarts.add(lineStarts[it.index - 1] + lines[it.index - 1].length() + 1)
|
||||
}
|
||||
}
|
||||
|
||||
val seperator = System.lineSeparator().charAt(0)
|
||||
val separator = System.lineSeparator().charAt(0)
|
||||
sorted.forEach {
|
||||
val posStart = lineStarts[it.first.line] + it.first.charIndex
|
||||
val posEnd = lineStarts[it.second.line] + it.second.charIndex
|
||||
for( i in posStart..(posEnd - 1)) {
|
||||
if (out.charAt(i) != seperator) {
|
||||
for ( i in posStart..(posEnd - 1)) {
|
||||
if (out.charAt(i) != separator) {
|
||||
out.setCharAt(i, ' ')
|
||||
}
|
||||
}
|
||||
}
|
||||
Log.d{"new tag to set: $newTag"}
|
||||
if (newTag != null) {
|
||||
Preconditions.checkState(rootNodeBounds.first.line != rootNodeBounds.second.line,
|
||||
"""The root tag should be multi line to add the tag. ${f.getName()}""")
|
||||
val line = rootNodeBounds.first.line
|
||||
out.insert(lineStarts[line] + lines[line].length(), """ android:tag = "$newTag" """)
|
||||
}
|
||||
|
||||
|
||||
return out.toString()
|
||||
}
|
||||
|
||||
@@ -15,7 +15,7 @@ package com.android.databinding.writer
|
||||
|
||||
import com.android.databinding.LayoutBinder
|
||||
|
||||
class DataBinderWriter(val pkg: String, val projectPackage: String, val className: String, val layoutBinders : List<LayoutBinder> ) {
|
||||
class DataBinderWriter(val pkg: String, val projectPackage: String, val className: String, val layoutBinders : Map<String, List<LayoutBinder>> ) {
|
||||
fun write() =
|
||||
kcode("") {
|
||||
tab("package $pkg;")
|
||||
@@ -25,8 +25,23 @@ class DataBinderWriter(val pkg: String, val projectPackage: String, val classNam
|
||||
tab("public com.android.databinding.library.ViewDataBinder getDataBinder(android.view.View view, int layoutId) {") {
|
||||
tab("switch(layoutId) {") {
|
||||
layoutBinders.forEach {
|
||||
tab("case R.layout.${it.getLayoutname()}:") {
|
||||
tab("return new ${it.getPackage()}.${it.getClassName()}(view);")
|
||||
tab("case R.layout.${it.value.first!!.getLayoutname()}:") {
|
||||
if (it.value.size() == 1) {
|
||||
tab("return new ${it.value.first!!.getPackage()}.${it.value.first!!.getClassName()}(view);")
|
||||
} else {
|
||||
// we should check the tag to decide which layout we need to inflate
|
||||
tab("{") {
|
||||
tab("final String tag = (String)view.getTag();")
|
||||
tab("if(tag == null) throw new java.lang.RuntimeException(\"view must have a tag\");")
|
||||
it.value.forEach {
|
||||
// TODO don't use strings. not necessary
|
||||
tab("if (tag.equals(String.valueOf(${it.getId()}))) {") {
|
||||
tab("return new ${it.getPackage()}.${it.getClassName()}(view);")
|
||||
} tab("}")
|
||||
}
|
||||
}tab("}")
|
||||
}
|
||||
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
@@ -323,34 +323,39 @@ class LayoutBinderWriter(val layoutBinder : LayoutBinder) {
|
||||
model.getExprMap().values().filterIsInstance(javaClass<IdentifierExpr>()).filter { it.isVariable() }
|
||||
}
|
||||
|
||||
public fun write() : String =
|
||||
kcode("package ${layoutBinder.getPackage()};") {
|
||||
nl("import ${layoutBinder.getProjectPackage()}.R;")
|
||||
nl("import android.view.View;")
|
||||
nl("public class ${className} extends com.android.databinding.library.ViewDataBinder implements ${interfaceName} {") {
|
||||
tab(declareViews())
|
||||
tab(declareVariables())
|
||||
tab(declareConstructor())
|
||||
tab(declareInvalidateAll())
|
||||
tab(declareLog())
|
||||
tab(declareSetVariable())
|
||||
tab(variableSettersAndGetters())
|
||||
tab(viewGetters())
|
||||
tab(onFieldChange())
|
||||
val usedVariables by Delegates.lazy {
|
||||
variables.filter {it.isUsed()}
|
||||
}
|
||||
|
||||
tab(rebindDirty())
|
||||
public fun write() : String {
|
||||
layoutBinder.resolveWhichExpressionsAreUsed()
|
||||
return kcode("package ${layoutBinder.getPackage()};") {
|
||||
nl("import ${layoutBinder.getProjectPackage()}.R;")
|
||||
nl("import android.view.View;")
|
||||
nl("public class ${className} extends com.android.databinding.library.ViewDataBinder implements ${interfaceName} {") {
|
||||
tab(declareViews())
|
||||
tab(declareVariables())
|
||||
tab(declareConstructor())
|
||||
tab(declareInvalidateAll())
|
||||
tab(declareLog())
|
||||
tab(declareSetVariable())
|
||||
tab(variableSettersAndGetters())
|
||||
tab(viewGetters())
|
||||
tab(onFieldChange())
|
||||
|
||||
tab(declareDirtyFlags())
|
||||
}
|
||||
nl("}")
|
||||
tab(flagMapping())
|
||||
tab("//end")
|
||||
}.generate()
|
||||
tab(rebindDirty())
|
||||
|
||||
tab(declareDirtyFlags())
|
||||
}
|
||||
nl("}")
|
||||
tab(flagMapping())
|
||||
tab("//end")
|
||||
}.generate()
|
||||
}
|
||||
fun declareConstructor() = kcode("") {
|
||||
nl("public ${className}(View root) {") {
|
||||
tab("super(root, ${model.getObservables().size()});")
|
||||
layoutBinder.getBindingTargets().forEach {
|
||||
layoutBinder.getBindingTargets().filter{it.isUsed()}.forEach {
|
||||
if (it.isBinder()) {
|
||||
tab("this.${it.fieldName} = com.android.databinding.library.DataBinder.createBinder(root.findViewById(${it.androidId}), R.layout.${it.getIncludedLayout()});")
|
||||
} else {
|
||||
@@ -371,7 +376,7 @@ class LayoutBinderWriter(val layoutBinder : LayoutBinder) {
|
||||
for (i in (0..(mDirtyFlags.buckets.size() - 1))) {
|
||||
tab("${mDirtyFlags.localValue(i)} = ${fs.localValue(i)};")
|
||||
}
|
||||
includedBinders.forEach { binder ->
|
||||
includedBinders.filter{it.isUsed()}.forEach { binder ->
|
||||
tab("${binder.fieldName}.invalidateAll();")
|
||||
}
|
||||
}
|
||||
@@ -381,7 +386,7 @@ class LayoutBinderWriter(val layoutBinder : LayoutBinder) {
|
||||
fun declareSetVariable() = kcode("") {
|
||||
nl("public boolean setVariable(int variableId, Object variable) {") {
|
||||
tab("switch(variableId) {") {
|
||||
variables.forEach {
|
||||
usedVariables.forEach {
|
||||
tab ("case ${it.getName().br()} :") {
|
||||
tab("${it.setterName}((${it.getResolvedType().toJavaCode()}) variable);")
|
||||
tab("return true;")
|
||||
@@ -402,7 +407,18 @@ class LayoutBinderWriter(val layoutBinder : LayoutBinder) {
|
||||
}
|
||||
|
||||
fun variableSettersAndGetters() = kcode("") {
|
||||
variables.forEach {
|
||||
variables.filterNot{it.isUsed()}.forEach {
|
||||
nl("public void ${it.setterName}(${it.getResolvedType().toJavaCode()} ${it.readableUniqueName}) {") {
|
||||
tab("// not used, ignore")
|
||||
}
|
||||
nl("}")
|
||||
nl("")
|
||||
nl("public ${it.getResolvedType().toJavaCode()} ${it.getterName}() {") {
|
||||
tab("return ${it.getDefaultValue()};")
|
||||
}
|
||||
nl("}")
|
||||
}
|
||||
usedVariables.forEach {
|
||||
nl("public void ${it.setterName}(${it.getResolvedType().toJavaCode()} ${it.readableUniqueName}) {") {
|
||||
if (it.isObservable()) {
|
||||
tab("updateRegistration(${it.getId()}, ${it.readableUniqueName});");
|
||||
@@ -472,7 +488,7 @@ class LayoutBinderWriter(val layoutBinder : LayoutBinder) {
|
||||
}
|
||||
|
||||
fun declareViews() = kcode("// views") {
|
||||
layoutBinder.getBindingTargets().forEach {
|
||||
layoutBinder.getBindingTargets().filter{it.isUsed()}.forEach {
|
||||
nl("private ${it.getViewClass()} ${it.fieldName};")
|
||||
}
|
||||
}
|
||||
@@ -480,15 +496,20 @@ class LayoutBinderWriter(val layoutBinder : LayoutBinder) {
|
||||
fun viewGetters() = kcode("// view getters") {
|
||||
layoutBinder.getBindingTargets().forEach {
|
||||
nl("@Override")
|
||||
nl("public ${it.getViewClass()} ${it.getterName}() {") {
|
||||
tab("return ${it.fieldName};")
|
||||
nl("public ${it.getInterfaceType()} ${it.getterName}() {") {
|
||||
if (it.isUsed()) {
|
||||
tab("return ${it.fieldName};")
|
||||
} else {
|
||||
tab("return null;")
|
||||
}
|
||||
|
||||
}
|
||||
nl("}")
|
||||
}
|
||||
}
|
||||
|
||||
fun declareVariables() = kcode("// variables") {
|
||||
variables.forEach {
|
||||
usedVariables.forEach {
|
||||
nl("private ${it.getResolvedType().toJavaCode()} ${it.fieldName};")
|
||||
}
|
||||
}
|
||||
@@ -546,7 +567,7 @@ class LayoutBinderWriter(val layoutBinder : LayoutBinder) {
|
||||
} while(model.markBitsRead())
|
||||
|
||||
//
|
||||
layoutBinder.getBindingTargets()
|
||||
layoutBinder.getBindingTargets().filter { it.isUsed() }
|
||||
.flatMap { it.getBindings() }
|
||||
.groupBy { it.getExpr() }
|
||||
.forEach {
|
||||
@@ -562,7 +583,7 @@ class LayoutBinderWriter(val layoutBinder : LayoutBinder) {
|
||||
tab("}")
|
||||
}
|
||||
//
|
||||
includedBinders.forEach { binder ->
|
||||
includedBinders.filter{it.isUsed()}.forEach { binder ->
|
||||
tab("${binder.fieldName}.rebindDirty();")
|
||||
}
|
||||
}
|
||||
@@ -649,7 +670,7 @@ class LayoutBinderWriter(val layoutBinder : LayoutBinder) {
|
||||
tab("public void ${it.setterName}(${it.getUserDefinedType()} ${it.readableUniqueName});")
|
||||
}
|
||||
layoutBinder.getBindingTargets().forEach {
|
||||
tab("public ${it.getViewClass()} ${it.getterName}();")
|
||||
tab("public ${it.getInterfaceType()} ${it.getterName}();")
|
||||
}
|
||||
nl("}")
|
||||
}.generate()
|
||||
|
||||
@@ -1,22 +1,6 @@
|
||||
#
|
||||
# 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.
|
||||
#
|
||||
|
||||
#Wed Apr 10 15:27:10 PDT 2013
|
||||
#Mon Feb 02 17:44:27 PST 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
|
||||
distributionUrl=https\://services.gradle.org/distributions/gradle-2.2-all.zip
|
||||
|
||||
@@ -28,7 +28,7 @@ buildscript {
|
||||
}
|
||||
|
||||
dependencies {
|
||||
compile 'com.android.tools.build:gradle:0.14.2'
|
||||
compile "com.android.tools.build:gradle:$androidPluginVersion"
|
||||
compile "org.jetbrains.kotlin:kotlin-stdlib:$kotlinVersion"
|
||||
compile gradleApi()
|
||||
compile 'commons-io:commons-io:2.4'
|
||||
|
||||
@@ -23,7 +23,7 @@ buildscript {
|
||||
mavenCentral()
|
||||
}
|
||||
dependencies {
|
||||
classpath 'com.android.tools.build:gradle:1.0.0'
|
||||
classpath "com.android.tools.build:gradle:$androidPluginVersion"
|
||||
// NOTE: Do not place your application dependencies here; they belong
|
||||
// in the individual module build.gradle files
|
||||
}
|
||||
|
||||
@@ -23,7 +23,7 @@ buildscript {
|
||||
mavenCentral()
|
||||
}
|
||||
dependencies {
|
||||
classpath 'com.android.tools.build:gradle:1.0.0'
|
||||
classpath 'com.android.tools.build:gradle:0.14.2'
|
||||
classpath 'com.android.databinding:dataBinder:0.3-SNAPSHOT'
|
||||
// NOTE: Do not place your application dependencies here; they belong
|
||||
// in the individual module build.gradle files
|
||||
|
||||
Reference in New Issue
Block a user