Revert "Move frameworks/base/tools/ to frameworks/tools/"

This reverts commit 9f6a119c8aa276432ece4fe2118bd8a3c9b1067e.
diff --git a/tools/layoutlib/create/.classpath b/tools/layoutlib/create/.classpath
new file mode 100644
index 0000000..dbc4cfd
--- /dev/null
+++ b/tools/layoutlib/create/.classpath
@@ -0,0 +1,9 @@
+<?xml version="1.0" encoding="UTF-8"?>
+<classpath>
+	<classpathentry kind="src" path="src"/>
+	<classpathentry excluding="mock_android/" kind="src" path="tests"/>
+	<classpathentry kind="con" path="org.eclipse.jdt.launching.JRE_CONTAINER"/>
+	<classpathentry kind="con" path="org.eclipse.jdt.junit.JUNIT_CONTAINER/4"/>
+	<classpathentry kind="var" path="ANDROID_SRC/prebuilts/tools/common/asm-tools/asm-4.0.jar"/>
+	<classpathentry kind="output" path="bin"/>
+</classpath>
diff --git a/tools/layoutlib/create/.project b/tools/layoutlib/create/.project
new file mode 100644
index 0000000..e100d17
--- /dev/null
+++ b/tools/layoutlib/create/.project
@@ -0,0 +1,17 @@
+<?xml version="1.0" encoding="UTF-8"?>
+<projectDescription>
+	<name>layoutlib_create</name>
+	<comment></comment>
+	<projects>
+	</projects>
+	<buildSpec>
+		<buildCommand>
+			<name>org.eclipse.jdt.core.javabuilder</name>
+			<arguments>
+			</arguments>
+		</buildCommand>
+	</buildSpec>
+	<natures>
+		<nature>org.eclipse.jdt.core.javanature</nature>
+	</natures>
+</projectDescription>
diff --git a/tools/layoutlib/create/.settings/README.txt b/tools/layoutlib/create/.settings/README.txt
new file mode 100644
index 0000000..9120b20
--- /dev/null
+++ b/tools/layoutlib/create/.settings/README.txt
@@ -0,0 +1,2 @@
+Copy this in eclipse project as a .settings folder at the root.
+This ensure proper compilation compliance and warning/error levels.
\ No newline at end of file
diff --git a/tools/layoutlib/create/.settings/org.eclipse.jdt.core.prefs b/tools/layoutlib/create/.settings/org.eclipse.jdt.core.prefs
new file mode 100644
index 0000000..5381a0e
--- /dev/null
+++ b/tools/layoutlib/create/.settings/org.eclipse.jdt.core.prefs
@@ -0,0 +1,93 @@
+eclipse.preferences.version=1
+org.eclipse.jdt.core.compiler.annotation.nonnull=com.android.annotations.NonNull
+org.eclipse.jdt.core.compiler.annotation.nonnullbydefault=com.android.annotations.NonNullByDefault
+org.eclipse.jdt.core.compiler.annotation.nonnullisdefault=disabled
+org.eclipse.jdt.core.compiler.annotation.nullable=com.android.annotations.Nullable
+org.eclipse.jdt.core.compiler.annotation.nullanalysis=enabled
+org.eclipse.jdt.core.compiler.codegen.inlineJsrBytecode=enabled
+org.eclipse.jdt.core.compiler.codegen.targetPlatform=1.6
+org.eclipse.jdt.core.compiler.codegen.unusedLocal=preserve
+org.eclipse.jdt.core.compiler.compliance=1.6
+org.eclipse.jdt.core.compiler.debug.lineNumber=generate
+org.eclipse.jdt.core.compiler.debug.localVariable=generate
+org.eclipse.jdt.core.compiler.debug.sourceFile=generate
+org.eclipse.jdt.core.compiler.problem.annotationSuperInterface=warning
+org.eclipse.jdt.core.compiler.problem.assertIdentifier=error
+org.eclipse.jdt.core.compiler.problem.autoboxing=ignore
+org.eclipse.jdt.core.compiler.problem.comparingIdentical=warning
+org.eclipse.jdt.core.compiler.problem.deadCode=warning
+org.eclipse.jdt.core.compiler.problem.deprecation=warning
+org.eclipse.jdt.core.compiler.problem.deprecationInDeprecatedCode=disabled
+org.eclipse.jdt.core.compiler.problem.deprecationWhenOverridingDeprecatedMethod=disabled
+org.eclipse.jdt.core.compiler.problem.discouragedReference=warning
+org.eclipse.jdt.core.compiler.problem.emptyStatement=ignore
+org.eclipse.jdt.core.compiler.problem.enumIdentifier=error
+org.eclipse.jdt.core.compiler.problem.explicitlyClosedAutoCloseable=ignore
+org.eclipse.jdt.core.compiler.problem.fallthroughCase=warning
+org.eclipse.jdt.core.compiler.problem.fatalOptionalError=enabled
+org.eclipse.jdt.core.compiler.problem.fieldHiding=warning
+org.eclipse.jdt.core.compiler.problem.finalParameterBound=warning
+org.eclipse.jdt.core.compiler.problem.finallyBlockNotCompletingNormally=warning
+org.eclipse.jdt.core.compiler.problem.forbiddenReference=error
+org.eclipse.jdt.core.compiler.problem.hiddenCatchBlock=warning
+org.eclipse.jdt.core.compiler.problem.includeNullInfoFromAsserts=enabled
+org.eclipse.jdt.core.compiler.problem.incompatibleNonInheritedInterfaceMethod=warning
+org.eclipse.jdt.core.compiler.problem.incompleteEnumSwitch=ignore
+org.eclipse.jdt.core.compiler.problem.indirectStaticAccess=ignore
+org.eclipse.jdt.core.compiler.problem.localVariableHiding=warning
+org.eclipse.jdt.core.compiler.problem.methodWithConstructorName=warning
+org.eclipse.jdt.core.compiler.problem.missingDeprecatedAnnotation=warning
+org.eclipse.jdt.core.compiler.problem.missingHashCodeMethod=warning
+org.eclipse.jdt.core.compiler.problem.missingOverrideAnnotation=error
+org.eclipse.jdt.core.compiler.problem.missingOverrideAnnotationForInterfaceMethodImplementation=enabled
+org.eclipse.jdt.core.compiler.problem.missingSerialVersion=warning
+org.eclipse.jdt.core.compiler.problem.missingSynchronizedOnInheritedMethod=ignore
+org.eclipse.jdt.core.compiler.problem.noEffectAssignment=warning
+org.eclipse.jdt.core.compiler.problem.noImplicitStringConversion=warning
+org.eclipse.jdt.core.compiler.problem.nonExternalizedStringLiteral=ignore
+org.eclipse.jdt.core.compiler.problem.nullReference=error
+org.eclipse.jdt.core.compiler.problem.nullSpecInsufficientInfo=warning
+org.eclipse.jdt.core.compiler.problem.nullSpecViolation=error
+org.eclipse.jdt.core.compiler.problem.overridingPackageDefaultMethod=warning
+org.eclipse.jdt.core.compiler.problem.parameterAssignment=ignore
+org.eclipse.jdt.core.compiler.problem.possibleAccidentalBooleanAssignment=warning
+org.eclipse.jdt.core.compiler.problem.potentialNullReference=warning
+org.eclipse.jdt.core.compiler.problem.potentialNullSpecViolation=error
+org.eclipse.jdt.core.compiler.problem.potentiallyUnclosedCloseable=warning
+org.eclipse.jdt.core.compiler.problem.rawTypeReference=warning
+org.eclipse.jdt.core.compiler.problem.redundantNullAnnotation=warning
+org.eclipse.jdt.core.compiler.problem.redundantNullCheck=ignore
+org.eclipse.jdt.core.compiler.problem.redundantSpecificationOfTypeArguments=ignore
+org.eclipse.jdt.core.compiler.problem.redundantSuperinterface=warning
+org.eclipse.jdt.core.compiler.problem.reportMethodCanBePotentiallyStatic=ignore
+org.eclipse.jdt.core.compiler.problem.reportMethodCanBeStatic=ignore
+org.eclipse.jdt.core.compiler.problem.specialParameterHidingField=disabled
+org.eclipse.jdt.core.compiler.problem.staticAccessReceiver=warning
+org.eclipse.jdt.core.compiler.problem.suppressOptionalErrors=disabled
+org.eclipse.jdt.core.compiler.problem.suppressWarnings=enabled
+org.eclipse.jdt.core.compiler.problem.syntheticAccessEmulation=ignore
+org.eclipse.jdt.core.compiler.problem.typeParameterHiding=warning
+org.eclipse.jdt.core.compiler.problem.unavoidableGenericTypeProblems=disabled
+org.eclipse.jdt.core.compiler.problem.uncheckedTypeOperation=warning
+org.eclipse.jdt.core.compiler.problem.unclosedCloseable=error
+org.eclipse.jdt.core.compiler.problem.undocumentedEmptyBlock=ignore
+org.eclipse.jdt.core.compiler.problem.unhandledWarningToken=warning
+org.eclipse.jdt.core.compiler.problem.unnecessaryElse=ignore
+org.eclipse.jdt.core.compiler.problem.unnecessaryTypeCheck=warning
+org.eclipse.jdt.core.compiler.problem.unqualifiedFieldAccess=ignore
+org.eclipse.jdt.core.compiler.problem.unusedDeclaredThrownException=warning
+org.eclipse.jdt.core.compiler.problem.unusedDeclaredThrownExceptionExemptExceptionAndThrowable=enabled
+org.eclipse.jdt.core.compiler.problem.unusedDeclaredThrownExceptionIncludeDocCommentReference=enabled
+org.eclipse.jdt.core.compiler.problem.unusedDeclaredThrownExceptionWhenOverriding=disabled
+org.eclipse.jdt.core.compiler.problem.unusedImport=warning
+org.eclipse.jdt.core.compiler.problem.unusedLabel=warning
+org.eclipse.jdt.core.compiler.problem.unusedLocal=warning
+org.eclipse.jdt.core.compiler.problem.unusedObjectAllocation=warning
+org.eclipse.jdt.core.compiler.problem.unusedParameter=ignore
+org.eclipse.jdt.core.compiler.problem.unusedParameterIncludeDocCommentReference=enabled
+org.eclipse.jdt.core.compiler.problem.unusedParameterWhenImplementingAbstract=disabled
+org.eclipse.jdt.core.compiler.problem.unusedParameterWhenOverridingConcrete=disabled
+org.eclipse.jdt.core.compiler.problem.unusedPrivateMember=warning
+org.eclipse.jdt.core.compiler.problem.unusedWarningToken=warning
+org.eclipse.jdt.core.compiler.problem.varargsArgumentNeedCast=warning
+org.eclipse.jdt.core.compiler.source=1.6
diff --git a/tools/layoutlib/create/Android.mk b/tools/layoutlib/create/Android.mk
new file mode 100644
index 0000000..9bd48ab
--- /dev/null
+++ b/tools/layoutlib/create/Android.mk
@@ -0,0 +1,28 @@
+#
+# Copyright (C) 2008 The Android Open Source Project
+#
+# Licensed under the Apache License, Version 2.0 (the "License");
+# you may not use this file except in compliance with the License.
+# You may obtain a copy of the License at
+#
+#      http://www.apache.org/licenses/LICENSE-2.0
+#
+# Unless required by applicable law or agreed to in writing, software
+# distributed under the License is distributed on an "AS IS" BASIS,
+# WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+# See the License for the specific language governing permissions and
+# limitations under the License.
+#
+LOCAL_PATH := $(call my-dir)
+include $(CLEAR_VARS)
+
+LOCAL_SRC_FILES := $(call all-java-files-under,src)
+
+LOCAL_JAR_MANIFEST := manifest.txt
+LOCAL_STATIC_JAVA_LIBRARIES := \
+	asm-4.0
+
+LOCAL_MODULE := layoutlib_create
+
+include $(BUILD_HOST_JAVA_LIBRARY)
+
diff --git a/tools/layoutlib/create/README.txt b/tools/layoutlib/create/README.txt
new file mode 100644
index 0000000..894611b
--- /dev/null
+++ b/tools/layoutlib/create/README.txt
@@ -0,0 +1,240 @@
+# Copyright (C) 2008 The Android Open Source Project
+
+
+- Description -
+---------------
+
+Layoutlib_create generates a JAR library used by the Eclipse graphical layout editor
+to perform layout.
+
+
+- Usage -
+---------
+
+ ./layoutlib_create path/to/android.jar destination.jar
+
+
+- Design Overview -
+-------------------
+
+Layoutlib_create uses the "android.jar" containing all the Java code used by Android
+as generated by the Android build, right before the classes are converted to a DEX format.
+
+The Android JAR can't be used directly in Eclipse:
+- it contains references to native code (which we want to avoid in Eclipse),
+- some classes need to be overridden, for example all the drawing code that is
+  replaced by Java 2D calls in Eclipse.
+- some of the classes that need to be changed are final and/or we need access
+  to their private internal state.
+
+Consequently this tool:
+- parses the input JAR,
+- modifies some of the classes directly using some bytecode manipulation,
+- filters some packages and removes those we don't want in the output JAR,
+- injects some new classes,
+- generates a modified JAR file that is suitable for the Android plugin
+  for Eclipse to perform rendering.
+
+The ASM library is used to do the bytecode modification using its visitor pattern API.
+
+The layoutlib_create is *NOT* generic. There is no configuration file. Instead all the
+configuration is done in the main() method and the CreateInfo structure is expected to
+change with the Android platform as new classes are added, changed or removed.
+
+The resulting JAR is used by layoutlib_bridge (a.k.a. "the bridge"), also part of the
+platform, that provides all the necessary missing implementation for rendering graphics
+in Eclipse.
+
+
+
+- Implementation Notes -
+------------------------
+
+The tool works in two phases:
+- first analyze the input jar (AsmAnalyzer class)
+- then generate the output jar (AsmGenerator class),
+
+
+- Analyzer
+----------
+
+The goal of the analyzer is to create a graph of all the classes from the input JAR
+with their dependencies and then only keep the ones we want.
+
+To do that, the analyzer is created with a list of base classes to keep -- everything
+that derives from these is kept. Currently the one such class is android.view.View:
+since we want to render layouts, anything that is sort of a view needs to be kept.
+
+The analyzer is also given a list of class names to keep in the output.
+This is done using shell-like glob patterns that filter on the fully-qualified
+class names, for example "android.*.R**" ("*" does not matches dots whilst "**" does,
+and "." and "$" are interpreted as-is).
+In practice we almost but not quite request the inclusion of full packages.
+
+With this information, the analyzer parses the input zip to find all the classes.
+All classes deriving from the requested bases classes are kept.
+All classes which name matched the glob pattern are kept.
+The analysis then finds all the dependencies of the classes that are to be kept
+using an ASM visitor on the class, the field types, the method types and annotations types.
+Classes that belong to the current JRE are excluded.
+
+The output of the analyzer is a set of ASM ClassReader instances which are then
+fed to the generator.
+
+
+- Generator
+-----------
+
+The generator is constructed from a CreateInfo struct that acts as a config file
+and lists:
+- the classes to inject in the output JAR -- these classes are directly implemented
+  in layoutlib_create and will be used to interface with the renderer in Eclipse.
+- specific methods to override (see method stubs details below).
+- specific methods for which to delegate calls.
+- specific methods to remove based on their return type.
+- specific classes to rename.
+
+Each of these are specific strategies we use to be able to modify the Android code
+to fit within the Eclipse renderer. These strategies are explained beow.
+
+The core method of the generator is transform(): it takes an input ASM ClassReader
+and modifies it to produce a byte array suitable for the final JAR file.
+
+The first step of the transformation is changing the name of the class in case
+we requested the class to be renamed. This uses the RenameClassAdapter to also rename
+all inner classes and references in methods and types. Note that other classes are
+not transformed and keep referencing the original name.
+
+The TransformClassAdapter is then used to process the potentially renamed class.
+All protected or private classes are market as public.
+All classes are made non-final.
+Interfaces are left as-is.
+
+If a method has a return type that must be erased, the whole method is skipped.
+Methods are also changed from protected/private to public.
+The code of the methods is then kept as-is, except for native methods which are
+replaced by a stub. Methods that are to be overridden are also replaced by a stub.
+
+The transformed class is then fed through the DelegateClassAdapter to implement
+method delegates. 
+
+Finally fields are also visited and changed from protected/private to public.
+
+
+- Method stubs
+--------------
+
+As indicated above, all native and overridden methods are replaced by a stub.
+We don't have the code to replace with in layoutlib_create.
+Instead the StubMethodAdapter replaces the code of the method by a call to
+OverrideMethod.invokeX(). When using the final JAR, the bridge can register
+listeners from these overridden method calls based on the method signatures.
+
+The listeners are currently pretty basic: we only pass the signature of the
+method being called, its caller object and a flag indicating whether the
+method was native. We do not currently provide the parameters. The listener
+can however specify the return value of the overridden method.
+
+This strategy is now obsolete and replaced by the method delegates.
+
+
+- Strategies
+------------
+
+We currently have 4 strategies to deal with overriding the rendering code
+and make it run in Eclipse. Most of these strategies are implemented hand-in-hand
+by the bridge (which runs in Eclipse) and the generator.
+
+
+1- Class Injection
+
+This is the easiest: we currently inject 4 classes, namely:
+- OverrideMethod and its associated MethodListener and MethodAdapter are used
+  to intercept calls to some specific methods that are stubbed out and change
+  their return value.
+- CreateInfo class, which configured the generator. Not used yet, but could
+  in theory help us track what the generator changed.
+
+
+2- Overriding methods
+
+As explained earlier, the creator doesn't have any replacement code for
+methods to override. Instead it removes the original code and replaces it
+by a call to a specific OveriddeMethod.invokeX(). The bridge then registers
+a listener on the method signature and can provide an implementation.
+
+This strategy is now obsolete and replaced by the method delegates.
+See strategy 5 below.
+
+
+3- Renaming classes
+
+This simply changes the name of a class in its definition, as well as all its
+references in internal inner classes and methods.
+Calls from other classes are not modified -- they keep referencing the original
+class name. This allows the bridge to literally replace an implementation.
+
+An example will make this easier: android.graphics.Paint is the main drawing
+class that we need to replace. To do so, the generator renames Paint to _original_Paint.
+Later the bridge provides its own replacement version of Paint which will be used
+by the rest of the Android stack. The replacement version of Paint can still use
+(either by inheritance or delegation) all the original non-native code of _original_Paint
+if it so desires.
+
+Some of the Android classes are basically wrappers over native objects and since
+we don't have the native code in Eclipse, we need to provide a full alternate
+implementation. Sub-classing doesn't work as some native methods are static and
+we don't control object creation.
+
+This won't rename/replace the inner static methods of a given class.
+
+
+4- Method erasure based on return type
+
+This is mostly an implementation detail of the bridge: in the Paint class
+mentioned above, some inner static classes are used to pass around
+attributes (e.g. FontMetrics, or the Style enum) and all the original implementation
+is native.
+
+In this case we have a strategy that tells the generator that anything returning, for
+example, the inner class Paint$Style in the Paint class should be discarded and the
+bridge will provide its own implementation.
+
+
+5- Method Delegates
+
+This strategy is used to override method implementations.
+Given a method SomeClass.MethodName(), 1 or 2 methods are generated:
+a- A copy of the original method named SomeClass.MethodName_Original().
+   The content is the original method as-is from the reader.
+   This step is omitted if the method is native, since it has no Java implementation.
+b- A brand new implementation of SomeClass.MethodName() which calls to a
+   non-existing static method named SomeClass_Delegate.MethodName().
+   The implementation of this 'delegate' method is done in layoutlib_brigde.
+
+The delegate method is a static method.
+If the original method is non-static, the delegate method receives the original 'this'
+as its first argument. If the original method is an inner non-static method, it also
+receives the inner 'this' as the second argument.
+
+
+
+- References -
+--------------
+
+
+The JVM Specification 2nd edition:
+  http://java.sun.com/docs/books/jvms/second_edition/html/VMSpecTOC.doc.html
+
+Understanding bytecode:
+  http://www.ibm.com/developerworks/ibm/library/it-haggar_bytecode/
+
+Bytecode opcode list:
+  http://en.wikipedia.org/wiki/Java_bytecode_instruction_listings
+
+ASM user guide:
+  http://download.forge.objectweb.org/asm/asm-guide.pdf
+
+
+--
+end
diff --git a/tools/layoutlib/create/manifest.txt b/tools/layoutlib/create/manifest.txt
new file mode 100644
index 0000000..238e7f9
--- /dev/null
+++ b/tools/layoutlib/create/manifest.txt
@@ -0,0 +1 @@
+Main-Class: com.android.tools.layoutlib.create.Main
diff --git a/tools/layoutlib/create/src/com/android/tools/layoutlib/annotations/LayoutlibDelegate.java b/tools/layoutlib/create/src/com/android/tools/layoutlib/annotations/LayoutlibDelegate.java
new file mode 100644
index 0000000..9a48ea6
--- /dev/null
+++ b/tools/layoutlib/create/src/com/android/tools/layoutlib/annotations/LayoutlibDelegate.java
@@ -0,0 +1,27 @@
+/*
+ * Copyright (C) 2010 The Android Open Source Project
+ *
+ * Licensed under the Apache License, Version 2.0 (the "License");
+ * you may not use this file except in compliance with the License.
+ * You may obtain a copy of the License at
+ *
+ *      http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing, software
+ * distributed under the License is distributed on an "AS IS" BASIS,
+ * WITHOUT WARRANTIES 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.tools.layoutlib.annotations;
+
+import java.lang.annotation.Retention;
+import java.lang.annotation.RetentionPolicy;
+
+/**
+ * Denotes a method that has been converted to a delegate by layoutlib_create.
+ */
+@Retention(RetentionPolicy.RUNTIME)
+public @interface LayoutlibDelegate {
+}
diff --git a/tools/layoutlib/create/src/com/android/tools/layoutlib/annotations/Nullable.java b/tools/layoutlib/create/src/com/android/tools/layoutlib/annotations/Nullable.java
new file mode 100644
index 0000000..0689c92
--- /dev/null
+++ b/tools/layoutlib/create/src/com/android/tools/layoutlib/annotations/Nullable.java
@@ -0,0 +1,35 @@
+/*
+ * Copyright (C) 2010 The Android Open Source Project
+ *
+ * Licensed under the Apache License, Version 2.0 (the "License");
+ * you may not use this file except in compliance with the License.
+ * You may obtain a copy of the License at
+ *
+ *      http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing, software
+ * distributed under the License is distributed on an "AS IS" BASIS,
+ * WITHOUT WARRANTIES 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.tools.layoutlib.annotations;
+
+import java.lang.annotation.Retention;
+import java.lang.annotation.RetentionPolicy;
+
+/**
+ * Denotes a parameter or field can be null.
+ * <p/>
+ * When decorating a method call parameter, this denotes the parameter can
+ * legitimately be null and the method will gracefully deal with it. Typically used
+ * on optional parameters.
+ * <p/>
+ * When decorating a method, this denotes the method might legitimately return null.
+ * <p/>
+ * This is a marker annotation and it has no specific attributes.
+ */
+@Retention(RetentionPolicy.SOURCE)
+public @interface Nullable {
+}
diff --git a/tools/layoutlib/create/src/com/android/tools/layoutlib/annotations/VisibleForTesting.java b/tools/layoutlib/create/src/com/android/tools/layoutlib/annotations/VisibleForTesting.java
new file mode 100644
index 0000000..e4e016b
--- /dev/null
+++ b/tools/layoutlib/create/src/com/android/tools/layoutlib/annotations/VisibleForTesting.java
@@ -0,0 +1,50 @@
+/*
+ * Copyright (C) 2010 The Android Open Source Project
+ *
+ * Licensed under the Apache License, Version 2.0 (the "License");
+ * you may not use this file except in compliance with the License.
+ * You may obtain a copy of the License at
+ *
+ *      http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing, software
+ * distributed under the License is distributed on an "AS IS" BASIS,
+ * WITHOUT WARRANTIES 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.tools.layoutlib.annotations;
+
+import java.lang.annotation.Retention;
+import java.lang.annotation.RetentionPolicy;
+
+/**
+ * Denotes that the class, method or field has its visibility relaxed so
+ * that unit tests can access it.
+ * <p/>
+ * The <code>visibility</code> argument can be used to specific what the original
+ * visibility should have been if it had not been made public or package-private for testing.
+ * The default is to consider the element private.
+ */
+@Retention(RetentionPolicy.SOURCE)
+public @interface VisibleForTesting {
+    /**
+     * Intended visibility if the element had not been made public or package-private for
+     * testing.
+     */
+    enum Visibility {
+        /** The element should be considered protected. */
+        PROTECTED,
+        /** The element should be considered package-private. */
+        PACKAGE,
+        /** The element should be considered private. */
+        PRIVATE
+    }
+
+    /**
+     * Intended visibility if the element had not been made public or package-private for testing.
+     * If not specified, one should assume the element originally intended to be private.
+     */
+    Visibility visibility() default Visibility.PRIVATE;
+}
diff --git a/tools/layoutlib/create/src/com/android/tools/layoutlib/create/AsmAnalyzer.java b/tools/layoutlib/create/src/com/android/tools/layoutlib/create/AsmAnalyzer.java
new file mode 100644
index 0000000..412695f
--- /dev/null
+++ b/tools/layoutlib/create/src/com/android/tools/layoutlib/create/AsmAnalyzer.java
@@ -0,0 +1,845 @@
+/*
+ * Copyright (C) 2008 The Android Open Source Project
+ *
+ * Licensed under the Apache License, Version 2.0 (the "License");
+ * you may not use this file except in compliance with the License.
+ * You may obtain a copy of the License at
+ *
+ *      http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing, software
+ * distributed under the License is distributed on an "AS IS" BASIS,
+ * WITHOUT WARRANTIES 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.tools.layoutlib.create;
+
+import org.objectweb.asm.AnnotationVisitor;
+import org.objectweb.asm.Attribute;
+import org.objectweb.asm.ClassReader;
+import org.objectweb.asm.ClassVisitor;
+import org.objectweb.asm.FieldVisitor;
+import org.objectweb.asm.Label;
+import org.objectweb.asm.MethodVisitor;
+import org.objectweb.asm.Opcodes;
+import org.objectweb.asm.Type;
+import org.objectweb.asm.signature.SignatureReader;
+import org.objectweb.asm.signature.SignatureVisitor;
+
+import java.io.IOException;
+import java.util.ArrayList;
+import java.util.Enumeration;
+import java.util.List;
+import java.util.Map;
+import java.util.Map.Entry;
+import java.util.TreeMap;
+import java.util.regex.Pattern;
+import java.util.zip.ZipEntry;
+import java.util.zip.ZipFile;
+
+/**
+ * Analyzes the input JAR using the ASM java bytecode manipulation library
+ * to list the desired classes and their dependencies.
+ */
+public class AsmAnalyzer {
+
+    // Note: a bunch of stuff has package-level access for unit tests. Consider it private.
+
+    /** Output logger. */
+    private final Log mLog;
+    /** The input source JAR to parse. */
+    private final List<String> mOsSourceJar;
+    /** The generator to fill with the class list and dependency list. */
+    private final AsmGenerator mGen;
+    /** Keep all classes that derive from these one (these included). */
+    private final String[] mDeriveFrom;
+    /** Glob patterns of classes to keep, e.g. "com.foo.*" */
+    private final String[] mIncludeGlobs;
+
+    /**
+     * Creates a new analyzer.
+     *
+     * @param log The log output.
+     * @param osJarPath The input source JARs to parse.
+     * @param gen The generator to fill with the class list and dependency list.
+     * @param deriveFrom Keep all classes that derive from these one (these included).
+     * @param includeGlobs Glob patterns of classes to keep, e.g. "com.foo.*"
+     *        ("*" does not matches dots whilst "**" does, "." and "$" are interpreted as-is)
+     */
+    public AsmAnalyzer(Log log, List<String> osJarPath, AsmGenerator gen,
+            String[] deriveFrom, String[] includeGlobs) {
+        mLog = log;
+        mGen = gen;
+        mOsSourceJar = osJarPath != null ? osJarPath : new ArrayList<String>();
+        mDeriveFrom = deriveFrom != null ? deriveFrom : new String[0];
+        mIncludeGlobs = includeGlobs != null ? includeGlobs : new String[0];
+    }
+
+    /**
+     * Starts the analysis using parameters from the constructor.
+     * Fills the generator with classes & dependencies found.
+     */
+    public void analyze() throws IOException, LogAbortException {
+
+        AsmAnalyzer visitor = this;
+
+        Map<String, ClassReader> zipClasses = parseZip(mOsSourceJar);
+        mLog.info("Found %d classes in input JAR%s.", zipClasses.size(),
+                mOsSourceJar.size() > 1 ? "s" : "");
+
+        Map<String, ClassReader> found = findIncludes(zipClasses);
+        Map<String, ClassReader> deps = findDeps(zipClasses, found);
+
+        if (mGen != null) {
+            mGen.setKeep(found);
+            mGen.setDeps(deps);
+        }
+    }
+
+    /**
+     * Parses a JAR file and returns a list of all classes founds using a map
+     * class name => ASM ClassReader. Class names are in the form "android.view.View".
+     */
+    Map<String,ClassReader> parseZip(List<String> jarPathList) throws IOException {
+        TreeMap<String, ClassReader> classes = new TreeMap<String, ClassReader>();
+
+        for (String jarPath : jarPathList) {
+            ZipFile zip = new ZipFile(jarPath);
+            Enumeration<? extends ZipEntry> entries = zip.entries();
+            ZipEntry entry;
+            while (entries.hasMoreElements()) {
+                entry = entries.nextElement();
+                if (entry.getName().endsWith(".class")) {
+                    ClassReader cr = new ClassReader(zip.getInputStream(entry));
+                    String className = classReaderToClassName(cr);
+                    classes.put(className, cr);
+                }
+            }
+        }
+
+        return classes;
+    }
+
+    /**
+     * Utility that returns the fully qualified binary class name for a ClassReader.
+     * E.g. it returns something like android.view.View.
+     */
+    static String classReaderToClassName(ClassReader classReader) {
+        if (classReader == null) {
+            return null;
+        } else {
+            return classReader.getClassName().replace('/', '.');
+        }
+    }
+
+    /**
+     * Utility that returns the fully qualified binary class name from a path-like FQCN.
+     * E.g. it returns android.view.View from android/view/View.
+     */
+    static String internalToBinaryClassName(String className) {
+        if (className == null) {
+            return null;
+        } else {
+            return className.replace('/', '.');
+        }
+    }
+
+    /**
+     * Process the "includes" arrays.
+     * <p/>
+     * This updates the in_out_found map.
+     */
+    Map<String, ClassReader> findIncludes(Map<String, ClassReader> zipClasses)
+            throws LogAbortException {
+        TreeMap<String, ClassReader> found = new TreeMap<String, ClassReader>();
+
+        mLog.debug("Find classes to include.");
+
+        for (String s : mIncludeGlobs) {
+            findGlobs(s, zipClasses, found);
+        }
+        for (String s : mDeriveFrom) {
+            findClassesDerivingFrom(s, zipClasses, found);
+        }
+
+        return found;
+    }
+
+
+    /**
+     * Uses ASM to find the class reader for the given FQCN class name.
+     * If found, insert it in the in_out_found map.
+     * Returns the class reader object.
+     */
+    ClassReader findClass(String className, Map<String, ClassReader> zipClasses,
+            Map<String, ClassReader> inOutFound) throws LogAbortException {
+        ClassReader classReader = zipClasses.get(className);
+        if (classReader == null) {
+            throw new LogAbortException("Class %s not found by ASM in %s",
+                    className, mOsSourceJar);
+        }
+
+        inOutFound.put(className, classReader);
+        return classReader;
+    }
+
+    /**
+     * Insert in the inOutFound map all classes found in zipClasses that match the
+     * given glob pattern.
+     * <p/>
+     * The glob pattern is not a regexp. It only accepts the "*" keyword to mean
+     * "anything but a period". The "." and "$" characters match themselves.
+     * The "**" keyword means everything including ".".
+     * <p/>
+     * Examples:
+     * <ul>
+     * <li>com.foo.* matches all classes in the package com.foo but NOT sub-packages.
+     * <li>com.foo*.*$Event matches all internal Event classes in a com.foo*.* class.
+     * </ul>
+     */
+    void findGlobs(String globPattern, Map<String, ClassReader> zipClasses,
+            Map<String, ClassReader> inOutFound) throws LogAbortException {
+        // transforms the glob pattern in a regexp:
+        // - escape "." with "\."
+        // - replace "*" by "[^.]*"
+        // - escape "$" with "\$"
+        // - add end-of-line match $
+        globPattern = globPattern.replaceAll("\\$", "\\\\\\$");
+        globPattern = globPattern.replaceAll("\\.", "\\\\.");
+        // prevent ** from being altered by the next rule, then process the * rule and finally
+        // the real ** rule (which is now @)
+        globPattern = globPattern.replaceAll("\\*\\*", "@");
+        globPattern = globPattern.replaceAll("\\*", "[^.]*");
+        globPattern = globPattern.replaceAll("@", ".*");
+        globPattern += "$";
+
+        Pattern regexp = Pattern.compile(globPattern);
+
+        for (Entry<String, ClassReader> entry : zipClasses.entrySet()) {
+            String class_name = entry.getKey();
+            if (regexp.matcher(class_name).matches()) {
+                findClass(class_name, zipClasses, inOutFound);
+            }
+        }
+    }
+
+    /**
+     * Checks all the classes defined in the JarClassName instance and uses BCEL to
+     * determine if they are derived from the given FQCN super class name.
+     * Inserts the super class and all the class objects found in the map.
+     */
+    void findClassesDerivingFrom(String super_name, Map<String, ClassReader> zipClasses,
+            Map<String, ClassReader> inOutFound) throws LogAbortException {
+        ClassReader super_clazz = findClass(super_name, zipClasses, inOutFound);
+
+        for (Entry<String, ClassReader> entry : zipClasses.entrySet()) {
+            String className = entry.getKey();
+            if (super_name.equals(className)) {
+                continue;
+            }
+            ClassReader classReader = entry.getValue();
+            ClassReader parent_cr = classReader;
+            while (parent_cr != null) {
+                String parent_name = internalToBinaryClassName(parent_cr.getSuperName());
+                if (parent_name == null) {
+                    // not found
+                    break;
+                } else if (super_name.equals(parent_name)) {
+                    inOutFound.put(className, classReader);
+                    break;
+                }
+                parent_cr = zipClasses.get(parent_name);
+            }
+        }
+    }
+
+    /**
+     * Instantiates a new DependencyVisitor. Useful for unit tests.
+     */
+    DependencyVisitor getVisitor(Map<String, ClassReader> zipClasses,
+            Map<String, ClassReader> inKeep,
+            Map<String, ClassReader> outKeep,
+            Map<String, ClassReader> inDeps,
+            Map<String, ClassReader> outDeps) {
+        return new DependencyVisitor(zipClasses, inKeep, outKeep, inDeps, outDeps);
+    }
+
+    /**
+     * Finds all dependencies for all classes in keepClasses which are also
+     * listed in zipClasses. Returns a map of all the dependencies found.
+     */
+    Map<String, ClassReader> findDeps(Map<String, ClassReader> zipClasses,
+            Map<String, ClassReader> inOutKeepClasses) {
+
+        TreeMap<String, ClassReader> deps = new TreeMap<String, ClassReader>();
+        TreeMap<String, ClassReader> new_deps = new TreeMap<String, ClassReader>();
+        TreeMap<String, ClassReader> new_keep = new TreeMap<String, ClassReader>();
+        TreeMap<String, ClassReader> temp = new TreeMap<String, ClassReader>();
+
+        DependencyVisitor visitor = getVisitor(zipClasses,
+                inOutKeepClasses, new_keep,
+                deps, new_deps);
+
+        for (ClassReader cr : inOutKeepClasses.values()) {
+            cr.accept(visitor, 0 /* flags */);
+        }
+
+        while (new_deps.size() > 0 || new_keep.size() > 0) {
+            deps.putAll(new_deps);
+            inOutKeepClasses.putAll(new_keep);
+
+            temp.clear();
+            temp.putAll(new_deps);
+            temp.putAll(new_keep);
+            new_deps.clear();
+            new_keep.clear();
+            mLog.debug("Found %1$d to keep, %2$d dependencies.",
+                    inOutKeepClasses.size(), deps.size());
+
+            for (ClassReader cr : temp.values()) {
+                cr.accept(visitor, 0 /* flags */);
+            }
+        }
+
+        mLog.info("Found %1$d classes to keep, %2$d class dependencies.",
+                inOutKeepClasses.size(), deps.size());
+
+        return deps;
+    }
+
+
+
+    // ----------------------------------
+
+    /**
+     * Visitor to collect all the type dependencies from a class.
+     */
+    public class DependencyVisitor extends ClassVisitor {
+
+        /** All classes found in the source JAR. */
+        private final Map<String, ClassReader> mZipClasses;
+        /** Classes from which dependencies are to be found. */
+        private final Map<String, ClassReader> mInKeep;
+        /** Dependencies already known. */
+        private final Map<String, ClassReader> mInDeps;
+        /** New dependencies found by this visitor. */
+        private final Map<String, ClassReader> mOutDeps;
+        /** New classes to keep as-is found by this visitor. */
+        private final Map<String, ClassReader> mOutKeep;
+
+        /**
+         * Creates a new visitor that will find all the dependencies for the visited class.
+         * Types which are already in the zipClasses, keepClasses or inDeps are not marked.
+         * New dependencies are marked in outDeps.
+         *
+         * @param zipClasses All classes found in the source JAR.
+         * @param inKeep Classes from which dependencies are to be found.
+         * @param inDeps Dependencies already known.
+         * @param outDeps New dependencies found by this visitor.
+         */
+        public DependencyVisitor(Map<String, ClassReader> zipClasses,
+                Map<String, ClassReader> inKeep,
+                Map<String, ClassReader> outKeep,
+                Map<String,ClassReader> inDeps,
+                Map<String,ClassReader> outDeps) {
+            super(Opcodes.ASM4);
+            mZipClasses = zipClasses;
+            mInKeep = inKeep;
+            mOutKeep = outKeep;
+            mInDeps = inDeps;
+            mOutDeps = outDeps;
+        }
+
+        /**
+         * Considers the given class name as a dependency.
+         * If it does, add to the mOutDeps map.
+         */
+        public void considerName(String className) {
+            if (className == null) {
+                return;
+            }
+
+            className = internalToBinaryClassName(className);
+
+            // exclude classes that have already been found
+            if (mInKeep.containsKey(className) ||
+                    mOutKeep.containsKey(className) ||
+                    mInDeps.containsKey(className) ||
+                    mOutDeps.containsKey(className)) {
+                return;
+            }
+
+            // exclude classes that are not part of the JAR file being examined
+            ClassReader cr = mZipClasses.get(className);
+            if (cr == null) {
+                return;
+            }
+
+            try {
+                // exclude classes that are part of the default JRE (the one executing this program)
+                if (getClass().getClassLoader().loadClass(className) != null) {
+                    return;
+                }
+            } catch (ClassNotFoundException e) {
+                // ignore
+            }
+
+            // accept this class:
+            // - android classes are added to dependencies
+            // - non-android classes are added to the list of classes to keep as-is (they don't need
+            //   to be stubbed).
+            if (className.indexOf("android") >= 0) {  // TODO make configurable
+                mOutDeps.put(className, cr);
+            } else {
+                mOutKeep.put(className, cr);
+            }
+        }
+
+        /**
+         * Considers this array of names using considerName().
+         */
+        public void considerNames(String[] classNames) {
+            if (classNames != null) {
+                for (String className : classNames) {
+                    considerName(className);
+                }
+            }
+        }
+
+        /**
+         * Considers this signature or type signature by invoking the {@link SignatureVisitor}
+         * on it.
+         */
+        public void considerSignature(String signature) {
+            if (signature != null) {
+                SignatureReader sr = new SignatureReader(signature);
+                // SignatureReader.accept will call accessType so we don't really have
+                // to differentiate where the signature comes from.
+                sr.accept(new MySignatureVisitor());
+            }
+        }
+
+        /**
+         * Considers this {@link Type}. For arrays, the element type is considered.
+         * If the type is an object, it's internal name is considered.
+         */
+        public void considerType(Type t) {
+            if (t != null) {
+                if (t.getSort() == Type.ARRAY) {
+                    t = t.getElementType();
+                }
+                if (t.getSort() == Type.OBJECT) {
+                    considerName(t.getInternalName());
+                }
+            }
+        }
+
+        /**
+         * Considers a descriptor string. The descriptor is converted to a {@link Type}
+         * and then considerType() is invoked.
+         */
+        public void considerDesc(String desc) {
+            if (desc != null) {
+                try {
+                    Type t = Type.getType(desc);
+                    considerType(t);
+                } catch (ArrayIndexOutOfBoundsException e) {
+                    // ignore, not a valid type.
+                }
+            }
+        }
+
+
+        // ---------------------------------------------------
+        // --- ClassVisitor, FieldVisitor
+        // ---------------------------------------------------
+
+        // Visits a class header
+        @Override
+        public void visit(int version, int access, String name,
+                String signature, String superName, String[] interfaces) {
+            // signature is the signature of this class. May be null if the class is not a generic
+            // one, and does not extend or implement generic classes or interfaces.
+
+            if (signature != null) {
+                considerSignature(signature);
+            }
+
+            // superName is the internal of name of the super class (see getInternalName).
+            // For interfaces, the super class is Object. May be null but only for the Object class.
+            considerName(superName);
+
+            // interfaces is the internal names of the class's interfaces (see getInternalName).
+            // May be null.
+            considerNames(interfaces);
+        }
+
+
+        @Override
+        public AnnotationVisitor visitAnnotation(String desc, boolean visible) {
+            // desc is the class descriptor of the annotation class.
+            considerDesc(desc);
+            return new MyAnnotationVisitor();
+        }
+
+        @Override
+        public void visitAttribute(Attribute attr) {
+            // pass
+        }
+
+        // Visits the end of a class
+        @Override
+        public void visitEnd() {
+            // pass
+        }
+
+        private class MyFieldVisitor extends FieldVisitor {
+
+            public MyFieldVisitor() {
+                super(Opcodes.ASM4);
+            }
+
+            @Override
+            public AnnotationVisitor visitAnnotation(String desc, boolean visible) {
+                // desc is the class descriptor of the annotation class.
+                considerDesc(desc);
+                return new MyAnnotationVisitor();
+            }
+
+            @Override
+            public void visitAttribute(Attribute attr) {
+                // pass
+            }
+
+            // Visits the end of a class
+            @Override
+            public void visitEnd() {
+                // pass
+            }
+        }
+
+        @Override
+        public FieldVisitor visitField(int access, String name, String desc,
+                String signature, Object value) {
+            // desc is the field's descriptor (see Type).
+            considerDesc(desc);
+
+            // signature is the field's signature. May be null if the field's type does not use
+            // generic types.
+            considerSignature(signature);
+
+            return new MyFieldVisitor();
+        }
+
+        @Override
+        public void visitInnerClass(String name, String outerName, String innerName, int access) {
+            // name is the internal name of an inner class (see getInternalName).
+            considerName(name);
+        }
+
+        @Override
+        public MethodVisitor visitMethod(int access, String name, String desc,
+                String signature, String[] exceptions) {
+            // desc is the method's descriptor (see Type).
+            considerDesc(desc);
+            // signature is the method's signature. May be null if the method parameters, return
+            // type and exceptions do not use generic types.
+            considerSignature(signature);
+
+            return new MyMethodVisitor();
+        }
+
+        @Override
+        public void visitOuterClass(String owner, String name, String desc) {
+            // pass
+        }
+
+        @Override
+        public void visitSource(String source, String debug) {
+            // pass
+        }
+
+
+        // ---------------------------------------------------
+        // --- MethodVisitor
+        // ---------------------------------------------------
+
+        private class MyMethodVisitor extends MethodVisitor {
+
+            public MyMethodVisitor() {
+                super(Opcodes.ASM4);
+            }
+
+
+            @Override
+            public AnnotationVisitor visitAnnotationDefault() {
+                return new MyAnnotationVisitor();
+            }
+
+            @Override
+            public void visitCode() {
+                // pass
+            }
+
+            // field instruction
+            @Override
+            public void visitFieldInsn(int opcode, String owner, String name, String desc) {
+                // name is the field's name.
+                considerName(name);
+                // desc is the field's descriptor (see Type).
+                considerDesc(desc);
+            }
+
+            @Override
+            public void visitFrame(int type, int local, Object[] local2, int stack, Object[] stack2) {
+                // pass
+            }
+
+            @Override
+            public void visitIincInsn(int var, int increment) {
+                // pass -- an IINC instruction
+            }
+
+            @Override
+            public void visitInsn(int opcode) {
+                // pass -- a zero operand instruction
+            }
+
+            @Override
+            public void visitIntInsn(int opcode, int operand) {
+                // pass -- a single int operand instruction
+            }
+
+            @Override
+            public void visitJumpInsn(int opcode, Label label) {
+                // pass -- a jump instruction
+            }
+
+            @Override
+            public void visitLabel(Label label) {
+                // pass -- a label target
+            }
+
+            // instruction to load a constant from the stack
+            @Override
+            public void visitLdcInsn(Object cst) {
+                if (cst instanceof Type) {
+                    considerType((Type) cst);
+                }
+            }
+
+            @Override
+            public void visitLineNumber(int line, Label start) {
+                // pass
+            }
+
+            @Override
+            public void visitLocalVariable(String name, String desc,
+                    String signature, Label start, Label end, int index) {
+                // desc is the type descriptor of this local variable.
+                considerDesc(desc);
+                // signature is the type signature of this local variable. May be null if the local
+                // variable type does not use generic types.
+                considerSignature(signature);
+            }
+
+            @Override
+            public void visitLookupSwitchInsn(Label dflt, int[] keys, Label[] labels) {
+                // pass -- a lookup switch instruction
+            }
+
+            @Override
+            public void visitMaxs(int maxStack, int maxLocals) {
+                // pass
+            }
+
+            // instruction that invokes a method
+            @Override
+            public void visitMethodInsn(int opcode, String owner, String name, String desc) {
+
+                // owner is the internal name of the method's owner class
+                considerName(owner);
+                // desc is the method's descriptor (see Type).
+                considerDesc(desc);
+            }
+
+            // instruction multianewarray, whatever that is
+            @Override
+            public void visitMultiANewArrayInsn(String desc, int dims) {
+
+                // desc an array type descriptor.
+                considerDesc(desc);
+            }
+
+            @Override
+            public AnnotationVisitor visitParameterAnnotation(int parameter, String desc,
+                    boolean visible) {
+                // desc is the class descriptor of the annotation class.
+                considerDesc(desc);
+                return new MyAnnotationVisitor();
+            }
+
+            @Override
+            public void visitTableSwitchInsn(int min, int max, Label dflt, Label[] labels) {
+                // pass -- table switch instruction
+
+            }
+
+            @Override
+            public void visitTryCatchBlock(Label start, Label end, Label handler, String type) {
+                // type is the internal name of the type of exceptions handled by the handler,
+                // or null to catch any exceptions (for "finally" blocks).
+                considerName(type);
+            }
+
+            // type instruction
+            @Override
+            public void visitTypeInsn(int opcode, String type) {
+                // type is the operand of the instruction to be visited. This operand must be the
+                // internal name of an object or array class.
+                considerName(type);
+            }
+
+            @Override
+            public void visitVarInsn(int opcode, int var) {
+                // pass -- local variable instruction
+            }
+        }
+
+        private class MySignatureVisitor extends SignatureVisitor {
+
+            public MySignatureVisitor() {
+                super(Opcodes.ASM4);
+            }
+
+            // ---------------------------------------------------
+            // --- SignatureVisitor
+            // ---------------------------------------------------
+
+            private String mCurrentSignatureClass = null;
+
+            // Starts the visit of a signature corresponding to a class or interface type
+            @Override
+            public void visitClassType(String name) {
+                mCurrentSignatureClass = name;
+                considerName(name);
+            }
+
+            // Visits an inner class
+            @Override
+            public void visitInnerClassType(String name) {
+                if (mCurrentSignatureClass != null) {
+                    mCurrentSignatureClass += "$" + name;
+                    considerName(mCurrentSignatureClass);
+                }
+            }
+
+            @Override
+            public SignatureVisitor visitArrayType() {
+                return new MySignatureVisitor();
+            }
+
+            @Override
+            public void visitBaseType(char descriptor) {
+                // pass -- a primitive type, ignored
+            }
+
+            @Override
+            public SignatureVisitor visitClassBound() {
+                return new MySignatureVisitor();
+            }
+
+            @Override
+            public SignatureVisitor visitExceptionType() {
+                return new MySignatureVisitor();
+            }
+
+            @Override
+            public void visitFormalTypeParameter(String name) {
+                // pass
+            }
+
+            @Override
+            public SignatureVisitor visitInterface() {
+                return new MySignatureVisitor();
+            }
+
+            @Override
+            public SignatureVisitor visitInterfaceBound() {
+                return new MySignatureVisitor();
+            }
+
+            @Override
+            public SignatureVisitor visitParameterType() {
+                return new MySignatureVisitor();
+            }
+
+            @Override
+            public SignatureVisitor visitReturnType() {
+                return new MySignatureVisitor();
+            }
+
+            @Override
+            public SignatureVisitor visitSuperclass() {
+                return new MySignatureVisitor();
+            }
+
+            @Override
+            public SignatureVisitor visitTypeArgument(char wildcard) {
+                return new MySignatureVisitor();
+            }
+
+            @Override
+            public void visitTypeVariable(String name) {
+                // pass
+            }
+
+            @Override
+            public void visitTypeArgument() {
+                // pass
+            }
+        }
+
+
+        // ---------------------------------------------------
+        // --- AnnotationVisitor
+        // ---------------------------------------------------
+
+        private class MyAnnotationVisitor extends AnnotationVisitor {
+
+            public MyAnnotationVisitor() {
+                super(Opcodes.ASM4);
+            }
+
+            // Visits a primitive value of an annotation
+            @Override
+            public void visit(String name, Object value) {
+                // value is the actual value, whose type must be Byte, Boolean, Character, Short,
+                // Integer, Long, Float, Double, String or Type
+                if (value instanceof Type) {
+                    considerType((Type) value);
+                }
+            }
+
+            @Override
+            public AnnotationVisitor visitAnnotation(String name, String desc) {
+                // desc is the class descriptor of the nested annotation class.
+                considerDesc(desc);
+                return new MyAnnotationVisitor();
+            }
+
+            @Override
+            public AnnotationVisitor visitArray(String name) {
+                return new MyAnnotationVisitor();
+            }
+
+            @Override
+            public void visitEnum(String name, String desc, String value) {
+                // desc is the class descriptor of the enumeration class.
+                considerDesc(desc);
+            }
+        }
+    }
+}
diff --git a/tools/layoutlib/create/src/com/android/tools/layoutlib/create/AsmGenerator.java b/tools/layoutlib/create/src/com/android/tools/layoutlib/create/AsmGenerator.java
new file mode 100644
index 0000000..a9ede26
--- /dev/null
+++ b/tools/layoutlib/create/src/com/android/tools/layoutlib/create/AsmGenerator.java
@@ -0,0 +1,370 @@
+/*
+ * Copyright (C) 2008 The Android Open Source Project
+ *
+ * Licensed under the Apache License, Version 2.0 (the "License");
+ * you may not use this file except in compliance with the License.
+ * You may obtain a copy of the License at
+ *
+ *      http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing, software
+ * distributed under the License is distributed on an "AS IS" BASIS,
+ * WITHOUT WARRANTIES 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.tools.layoutlib.create;
+
+import org.objectweb.asm.ClassReader;
+import org.objectweb.asm.ClassVisitor;
+import org.objectweb.asm.ClassWriter;
+
+import java.io.FileNotFoundException;
+import java.io.FileOutputStream;
+import java.io.IOException;
+import java.io.InputStream;
+import java.util.Arrays;
+import java.util.HashMap;
+import java.util.HashSet;
+import java.util.Map;
+import java.util.Map.Entry;
+import java.util.Set;
+import java.util.TreeMap;
+import java.util.jar.JarEntry;
+import java.util.jar.JarOutputStream;
+
+/**
+ * Class that generates a new JAR from a list of classes, some of which are to be kept as-is
+ * and some of which are to be stubbed partially or totally.
+ */
+public class AsmGenerator {
+
+    /** Output logger. */
+    private final Log mLog;
+    /** The path of the destination JAR to create. */
+    private final String mOsDestJar;
+    /** List of classes to inject in the final JAR from _this_ archive. */
+    private final Class<?>[] mInjectClasses;
+    /** The set of methods to stub out. */
+    private final Set<String> mStubMethods;
+    /** All classes to output as-is, except if they have native methods. */
+    private Map<String, ClassReader> mKeep;
+    /** All dependencies that must be completely stubbed. */
+    private Map<String, ClassReader> mDeps;
+    /** Counter of number of classes renamed during transform. */
+    private int mRenameCount;
+    /** FQCN Names of the classes to rename: map old-FQCN => new-FQCN */
+    private final HashMap<String, String> mRenameClasses;
+    /** FQCN Names of "old" classes that were NOT renamed. This starts with the full list of
+     *  old-FQCN to rename and they get erased as they get renamed. At the end, classes still
+     *  left here are not in the code base anymore and thus were not renamed. */
+    private HashSet<String> mClassesNotRenamed;
+    /** A map { FQCN => set { list of return types to delete from the FQCN } }. */
+    private HashMap<String, Set<String>> mDeleteReturns;
+    /** A map { FQCN => set { method names } } of methods to rewrite as delegates.
+     *  The special name {@link DelegateClassAdapter#ALL_NATIVES} can be used as in internal set. */
+    private final HashMap<String, Set<String>> mDelegateMethods;
+
+    /**
+     * Creates a new generator that can generate the output JAR with the stubbed classes.
+     *
+     * @param log Output logger.
+     * @param osDestJar The path of the destination JAR to create.
+     * @param createInfo Creation parameters. Must not be null.
+     */
+    public AsmGenerator(Log log, String osDestJar, ICreateInfo createInfo) {
+        mLog = log;
+        mOsDestJar = osDestJar;
+        mInjectClasses = createInfo.getInjectedClasses();
+        mStubMethods = new HashSet<String>(Arrays.asList(createInfo.getOverriddenMethods()));
+
+        // Create the map/set of methods to change to delegates
+        mDelegateMethods = new HashMap<String, Set<String>>();
+        for (String signature : createInfo.getDelegateMethods()) {
+            int pos = signature.indexOf('#');
+            if (pos <= 0 || pos >= signature.length() - 1) {
+                continue;
+            }
+            String className = binaryToInternalClassName(signature.substring(0, pos));
+            String methodName = signature.substring(pos + 1);
+            Set<String> methods = mDelegateMethods.get(className);
+            if (methods == null) {
+                methods = new HashSet<String>();
+                mDelegateMethods.put(className, methods);
+            }
+            methods.add(methodName);
+        }
+        for (String className : createInfo.getDelegateClassNatives()) {
+            className = binaryToInternalClassName(className);
+            Set<String> methods = mDelegateMethods.get(className);
+            if (methods == null) {
+                methods = new HashSet<String>();
+                mDelegateMethods.put(className, methods);
+            }
+            methods.add(DelegateClassAdapter.ALL_NATIVES);
+        }
+
+        // Create the map of classes to rename.
+        mRenameClasses = new HashMap<String, String>();
+        mClassesNotRenamed = new HashSet<String>();
+        String[] renameClasses = createInfo.getRenamedClasses();
+        int n = renameClasses.length;
+        for (int i = 0; i < n; i += 2) {
+            assert i + 1 < n;
+            // The ASM class names uses "/" separators, whereas regular FQCN use "."
+            String oldFqcn = binaryToInternalClassName(renameClasses[i]);
+            String newFqcn = binaryToInternalClassName(renameClasses[i + 1]);
+            mRenameClasses.put(oldFqcn, newFqcn);
+            mClassesNotRenamed.add(oldFqcn);
+        }
+
+        // create the map of renamed class -> return type of method to delete.
+        mDeleteReturns = new HashMap<String, Set<String>>();
+        String[] deleteReturns = createInfo.getDeleteReturns();
+        Set<String> returnTypes = null;
+        String renamedClass = null;
+        for (String className : deleteReturns) {
+            // if we reach the end of a section, add it to the main map
+            if (className == null) {
+                if (returnTypes != null) {
+                    mDeleteReturns.put(renamedClass, returnTypes);
+                }
+
+                renamedClass = null;
+                continue;
+            }
+
+            // if the renamed class is null, this is the beginning of a section
+            if (renamedClass == null) {
+                renamedClass = binaryToInternalClassName(className);
+                continue;
+            }
+
+            // just a standard return type, we add it to the list.
+            if (returnTypes == null) {
+                returnTypes = new HashSet<String>();
+            }
+            returnTypes.add(binaryToInternalClassName(className));
+        }
+    }
+
+    /**
+     * Returns the list of classes that have not been renamed yet.
+     * <p/>
+     * The names are "internal class names" rather than FQCN, i.e. they use "/" instead "."
+     * as package separators.
+     */
+    public Set<String> getClassesNotRenamed() {
+        return mClassesNotRenamed;
+    }
+
+    /**
+     * Utility that returns the internal ASM class name from a fully qualified binary class
+     * name. E.g. it returns android/view/View from android.view.View.
+     */
+    String binaryToInternalClassName(String className) {
+        if (className == null) {
+            return null;
+        } else {
+            return className.replace('.', '/');
+        }
+    }
+
+    /** Sets the map of classes to output as-is, except if they have native methods */
+    public void setKeep(Map<String, ClassReader> keep) {
+        mKeep = keep;
+    }
+
+    /** Sets the map of dependencies that must be completely stubbed */
+    public void setDeps(Map<String, ClassReader> deps) {
+        mDeps = deps;
+    }
+
+    /** Gets the map of classes to output as-is, except if they have native methods */
+    public Map<String, ClassReader> getKeep() {
+        return mKeep;
+    }
+
+    /** Gets the map of dependencies that must be completely stubbed */
+    public Map<String, ClassReader> getDeps() {
+        return mDeps;
+    }
+
+    /** Generates the final JAR */
+    public void generate() throws FileNotFoundException, IOException {
+        TreeMap<String, byte[]> all = new TreeMap<String, byte[]>();
+
+        for (Class<?> clazz : mInjectClasses) {
+            String name = classToEntryPath(clazz);
+            InputStream is = ClassLoader.getSystemResourceAsStream(name);
+            ClassReader cr = new ClassReader(is);
+            byte[] b = transform(cr, true /* stubNativesOnly */);
+            name = classNameToEntryPath(transformName(cr.getClassName()));
+            all.put(name, b);
+        }
+
+        for (Entry<String, ClassReader> entry : mDeps.entrySet()) {
+            ClassReader cr = entry.getValue();
+            byte[] b = transform(cr, true /* stubNativesOnly */);
+            String name = classNameToEntryPath(transformName(cr.getClassName()));
+            all.put(name, b);
+        }
+
+        for (Entry<String, ClassReader> entry : mKeep.entrySet()) {
+            ClassReader cr = entry.getValue();
+            byte[] b = transform(cr, true /* stubNativesOnly */);
+            String name = classNameToEntryPath(transformName(cr.getClassName()));
+            all.put(name, b);
+        }
+
+        mLog.info("# deps classes: %d", mDeps.size());
+        mLog.info("# keep classes: %d", mKeep.size());
+        mLog.info("# renamed     : %d", mRenameCount);
+
+        createJar(new FileOutputStream(mOsDestJar), all);
+        mLog.info("Created JAR file %s", mOsDestJar);
+    }
+
+    /**
+     * Writes the JAR file.
+     *
+     * @param outStream The file output stream were to write the JAR.
+     * @param all The map of all classes to output.
+     * @throws IOException if an I/O error has occurred
+     */
+    void createJar(FileOutputStream outStream, Map<String,byte[]> all) throws IOException {
+        JarOutputStream jar = new JarOutputStream(outStream);
+        for (Entry<String, byte[]> entry : all.entrySet()) {
+            String name = entry.getKey();
+            JarEntry jar_entry = new JarEntry(name);
+            jar.putNextEntry(jar_entry);
+            jar.write(entry.getValue());
+            jar.closeEntry();
+        }
+        jar.flush();
+        jar.close();
+    }
+
+    /**
+     * Utility method that converts a fully qualified java name into a JAR entry path
+     * e.g. for the input "android.view.View" it returns "android/view/View.class"
+     */
+    String classNameToEntryPath(String className) {
+        return className.replaceAll("\\.", "/").concat(".class");
+    }
+
+    /**
+     * Utility method to get the JAR entry path from a Class name.
+     * e.g. it returns someting like "com/foo/OuterClass$InnerClass1$InnerClass2.class"
+     */
+    private String classToEntryPath(Class<?> clazz) {
+        String name = "";
+        Class<?> parent;
+        while ((parent = clazz.getEnclosingClass()) != null) {
+            name = "$" + clazz.getSimpleName() + name;
+            clazz = parent;
+        }
+        return classNameToEntryPath(clazz.getCanonicalName() + name);
+    }
+
+    /**
+     * Transforms a class.
+     * <p/>
+     * There are 3 kind of transformations:
+     *
+     * 1- For "mock" dependencies classes, we want to remove all code from methods and replace
+     * by a stub. Native methods must be implemented with this stub too. Abstract methods are
+     * left intact. Modified classes must be overridable (non-private, non-final).
+     * Native methods must be made non-final, non-private.
+     *
+     * 2- For "keep" classes, we want to rewrite all native methods as indicated above.
+     * If a class has native methods, it must also be made non-private, non-final.
+     *
+     * Note that unfortunately static methods cannot be changed to non-static (since static and
+     * non-static are invoked differently.)
+     */
+    byte[] transform(ClassReader cr, boolean stubNativesOnly) {
+
+        boolean hasNativeMethods = hasNativeMethods(cr);
+
+        // Get the class name, as an internal name (e.g. com/android/SomeClass$InnerClass)
+        String className = cr.getClassName();
+
+        String newName = transformName(className);
+        // transformName returns its input argument if there's no need to rename the class
+        if (newName != className) {
+            mRenameCount++;
+            // This class is being renamed, so remove it from the list of classes not renamed.
+            mClassesNotRenamed.remove(className);
+        }
+
+        mLog.debug("Transform %s%s%s%s", className,
+                newName == className ? "" : " (renamed to " + newName + ")",
+                hasNativeMethods ? " -- has natives" : "",
+                stubNativesOnly ? " -- stub natives only" : "");
+
+        // Rewrite the new class from scratch, without reusing the constant pool from the
+        // original class reader.
+        ClassWriter cw = new ClassWriter(ClassWriter.COMPUTE_MAXS);
+
+        ClassVisitor rv = cw;
+        if (newName != className) {
+            rv = new RenameClassAdapter(cw, className, newName);
+        }
+
+        ClassVisitor cv = new TransformClassAdapter(mLog, mStubMethods,
+                mDeleteReturns.get(className),
+                newName, rv,
+                stubNativesOnly, stubNativesOnly || hasNativeMethods);
+
+        Set<String> delegateMethods = mDelegateMethods.get(className);
+        if (delegateMethods != null && !delegateMethods.isEmpty()) {
+            // If delegateMethods only contains one entry ALL_NATIVES and the class is
+            // known to have no native methods, just skip this step.
+            if (hasNativeMethods ||
+                    !(delegateMethods.size() == 1 &&
+                            delegateMethods.contains(DelegateClassAdapter.ALL_NATIVES))) {
+                cv = new DelegateClassAdapter(mLog, cv, className, delegateMethods);
+            }
+        }
+
+        cr.accept(cv, 0 /* flags */);
+        return cw.toByteArray();
+    }
+
+    /**
+     * Should this class be renamed, this returns the new name. Otherwise it returns the
+     * original name.
+     *
+     * @param className The internal ASM name of the class that may have to be renamed
+     * @return A new transformed name or the original input argument.
+     */
+    String transformName(String className) {
+        String newName = mRenameClasses.get(className);
+        if (newName != null) {
+            return newName;
+        }
+        int pos = className.indexOf('$');
+        if (pos > 0) {
+            // Is this an inner class of a renamed class?
+            String base = className.substring(0, pos);
+            newName = mRenameClasses.get(base);
+            if (newName != null) {
+                return newName + className.substring(pos);
+            }
+        }
+
+        return className;
+    }
+
+    /**
+     * Returns true if a class has any native methods.
+     */
+    boolean hasNativeMethods(ClassReader cr) {
+        ClassHasNativeVisitor cv = new ClassHasNativeVisitor();
+        cr.accept(cv, 0 /* flags */);
+        return cv.hasNativeMethods();
+    }
+
+}
diff --git a/tools/layoutlib/create/src/com/android/tools/layoutlib/create/ClassHasNativeVisitor.java b/tools/layoutlib/create/src/com/android/tools/layoutlib/create/ClassHasNativeVisitor.java
new file mode 100644
index 0000000..2c955fd
--- /dev/null
+++ b/tools/layoutlib/create/src/com/android/tools/layoutlib/create/ClassHasNativeVisitor.java
@@ -0,0 +1,102 @@
+/*
+ * Copyright (C) 2008 The Android Open Source Project
+ *
+ * Licensed under the Apache License, Version 2.0 (the "License");
+ * you may not use this file except in compliance with the License.
+ * You may obtain a copy of the License at
+ *
+ *      http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing, software
+ * distributed under the License is distributed on an "AS IS" BASIS,
+ * WITHOUT WARRANTIES 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.tools.layoutlib.create;
+
+import com.android.tools.layoutlib.annotations.VisibleForTesting;
+import com.android.tools.layoutlib.annotations.VisibleForTesting.Visibility;
+
+import org.objectweb.asm.AnnotationVisitor;
+import org.objectweb.asm.Attribute;
+import org.objectweb.asm.ClassVisitor;
+import org.objectweb.asm.FieldVisitor;
+import org.objectweb.asm.MethodVisitor;
+import org.objectweb.asm.Opcodes;
+
+/**
+ * Indicates if a class contains any native methods.
+ */
+public class ClassHasNativeVisitor extends ClassVisitor {
+    public ClassHasNativeVisitor() {
+        super(Opcodes.ASM4);
+    }
+
+    private boolean mHasNativeMethods = false;
+
+    public boolean hasNativeMethods() {
+        return mHasNativeMethods;
+    }
+
+    @VisibleForTesting(visibility=Visibility.PRIVATE)
+    protected void setHasNativeMethods(boolean hasNativeMethods, String methodName) {
+        mHasNativeMethods = hasNativeMethods;
+    }
+
+    @Override
+    public void visit(int version, int access, String name, String signature,
+            String superName, String[] interfaces) {
+        // pass
+    }
+
+    @Override
+    public AnnotationVisitor visitAnnotation(String desc, boolean visible) {
+        // pass
+        return null;
+    }
+
+    @Override
+    public void visitAttribute(Attribute attr) {
+        // pass
+    }
+
+    @Override
+    public void visitEnd() {
+        // pass
+    }
+
+    @Override
+    public FieldVisitor visitField(int access, String name, String desc,
+            String signature, Object value) {
+        // pass
+        return null;
+    }
+
+    @Override
+    public void visitInnerClass(String name, String outerName,
+            String innerName, int access) {
+        // pass
+    }
+
+    @Override
+    public MethodVisitor visitMethod(int access, String name, String desc,
+            String signature, String[] exceptions) {
+        if ((access & Opcodes.ACC_NATIVE) != 0) {
+            setHasNativeMethods(true, name);
+        }
+        return null;
+    }
+
+    @Override
+    public void visitOuterClass(String owner, String name, String desc) {
+        // pass
+    }
+
+    @Override
+    public void visitSource(String source, String debug) {
+        // pass
+    }
+
+}
diff --git a/tools/layoutlib/create/src/com/android/tools/layoutlib/create/CreateInfo.java b/tools/layoutlib/create/src/com/android/tools/layoutlib/create/CreateInfo.java
new file mode 100644
index 0000000..d955040
--- /dev/null
+++ b/tools/layoutlib/create/src/com/android/tools/layoutlib/create/CreateInfo.java
@@ -0,0 +1,207 @@
+/*
+ * Copyright (C) 2008 The Android Open Source Project
+ *
+ * Licensed under the Apache License, Version 2.0 (the "License");
+ * you may not use this file except in compliance with the License.
+ * You may obtain a copy of the License at
+ *
+ *      http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing, software
+ * distributed under the License is distributed on an "AS IS" BASIS,
+ * WITHOUT WARRANTIES 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.tools.layoutlib.create;
+
+import com.android.tools.layoutlib.annotations.LayoutlibDelegate;
+
+/**
+ * Describes the work to be done by {@link AsmGenerator}.
+ */
+public final class CreateInfo implements ICreateInfo {
+
+    /**
+     * Returns the list of class from layoutlib_create to inject in layoutlib.
+     * The list can be empty but must not be null.
+     */
+    @Override
+    public Class<?>[] getInjectedClasses() {
+        return INJECTED_CLASSES;
+    }
+
+    /**
+     * Returns the list of methods to rewrite as delegates.
+     * The list can be empty but must not be null.
+     */
+    @Override
+    public String[] getDelegateMethods() {
+        return DELEGATE_METHODS;
+    }
+
+    /**
+     * Returns the list of classes on which to delegate all native methods.
+     * The list can be empty but must not be null.
+     */
+    @Override
+    public String[] getDelegateClassNatives() {
+        return DELEGATE_CLASS_NATIVES;
+    }
+
+    /**
+     * Returns The list of methods to stub out. Each entry must be in the form
+     * "package.package.OuterClass$InnerClass#MethodName".
+     * The list can be empty but must not be null.
+     * <p/>
+     * This usage is deprecated. Please use method 'delegates' instead.
+     */
+    @Override
+    public String[] getOverriddenMethods() {
+        return OVERRIDDEN_METHODS;
+    }
+
+    /**
+     * Returns the list of classes to rename, must be an even list: the binary FQCN
+     * of class to replace followed by the new FQCN.
+     * The list can be empty but must not be null.
+     */
+    @Override
+    public String[] getRenamedClasses() {
+        return RENAMED_CLASSES;
+    }
+
+    /**
+     * Returns the list of classes for which the methods returning them should be deleted.
+     * The array contains a list of null terminated section starting with the name of the class
+     * to rename in which the methods are deleted, followed by a list of return types identifying
+     * the methods to delete.
+     * The list can be empty but must not be null.
+     */
+    @Override
+    public String[] getDeleteReturns() {
+        return DELETE_RETURNS;
+    }
+
+    //-----
+
+    /**
+     * The list of class from layoutlib_create to inject in layoutlib.
+     */
+    private final static Class<?>[] INJECTED_CLASSES = new Class<?>[] {
+            OverrideMethod.class,
+            MethodListener.class,
+            MethodAdapter.class,
+            ICreateInfo.class,
+            CreateInfo.class,
+            LayoutlibDelegate.class
+        };
+
+    /**
+     * The list of methods to rewrite as delegates.
+     */
+    public final static String[] DELEGATE_METHODS = new String[] {
+        "android.app.Fragment#instantiate", //(Landroid/content/Context;Ljava/lang/String;Landroid/os/Bundle;)Landroid/app/Fragment;",
+        "android.content.res.Resources$Theme#obtainStyledAttributes",
+        "android.content.res.Resources$Theme#resolveAttribute",
+        "android.content.res.TypedArray#getValueAt",
+        "android.graphics.BitmapFactory#finishDecode",
+        "android.os.Handler#sendMessageAtTime",
+        "android.os.HandlerThread#run",
+        "android.os.Build#getString",
+        "android.text.format.DateFormat#is24HourFormat",
+        "android.view.Choreographer#getRefreshRate",
+        "android.view.Display#updateDisplayInfoLocked",
+        "android.view.LayoutInflater#rInflate",
+        "android.view.LayoutInflater#parseInclude",
+        "android.view.View#isInEditMode",
+        "android.view.ViewRootImpl#isInTouchMode",
+        "android.view.WindowManagerGlobal#getWindowManagerService",
+        "android.view.inputmethod.InputMethodManager#getInstance",
+        "com.android.internal.util.XmlUtils#convertValueToInt",
+        "com.android.internal.textservice.ITextServicesManager$Stub#asInterface",
+    };
+
+    /**
+     * The list of classes on which to delegate all native methods.
+     */
+    public final static String[] DELEGATE_CLASS_NATIVES = new String[] {
+        "android.animation.PropertyValuesHolder",
+        "android.graphics.AvoidXfermode",
+        "android.graphics.Bitmap",
+        "android.graphics.BitmapFactory",
+        "android.graphics.BitmapShader",
+        "android.graphics.BlurMaskFilter",
+        "android.graphics.Canvas",
+        "android.graphics.ColorFilter",
+        "android.graphics.ColorMatrixColorFilter",
+        "android.graphics.ComposePathEffect",
+        "android.graphics.ComposeShader",
+        "android.graphics.CornerPathEffect",
+        "android.graphics.DashPathEffect",
+        "android.graphics.DiscretePathEffect",
+        "android.graphics.DrawFilter",
+        "android.graphics.EmbossMaskFilter",
+        "android.graphics.LayerRasterizer",
+        "android.graphics.LightingColorFilter",
+        "android.graphics.LinearGradient",
+        "android.graphics.MaskFilter",
+        "android.graphics.Matrix",
+        "android.graphics.NinePatch",
+        "android.graphics.Paint",
+        "android.graphics.PaintFlagsDrawFilter",
+        "android.graphics.Path",
+        "android.graphics.PathDashPathEffect",
+        "android.graphics.PathEffect",
+        "android.graphics.PixelXorXfermode",
+        "android.graphics.PorterDuffColorFilter",
+        "android.graphics.PorterDuffXfermode",
+        "android.graphics.RadialGradient",
+        "android.graphics.Rasterizer",
+        "android.graphics.Region",
+        "android.graphics.Shader",
+        "android.graphics.SumPathEffect",
+        "android.graphics.SweepGradient",
+        "android.graphics.Typeface",
+        "android.graphics.Xfermode",
+        "android.os.SystemClock",
+        "android.text.AndroidBidi",
+        "android.util.FloatMath",
+        "android.view.Display",
+        "libcore.icu.ICU",
+    };
+
+    /**
+     * The list of methods to stub out. Each entry must be in the form
+     *  "package.package.OuterClass$InnerClass#MethodName".
+     *  This usage is deprecated. Please use method 'delegates' instead.
+     */
+    private final static String[] OVERRIDDEN_METHODS = new String[] {
+    };
+
+    /**
+     *  The list of classes to rename, must be an even list: the binary FQCN
+     *  of class to replace followed by the new FQCN.
+     */
+    private final static String[] RENAMED_CLASSES =
+        new String[] {
+            "android.os.ServiceManager",                       "android.os._Original_ServiceManager",
+            "android.util.LruCache",                           "android.util._Original_LruCache",
+            "android.view.SurfaceView",                        "android.view._Original_SurfaceView",
+            "android.view.accessibility.AccessibilityManager", "android.view.accessibility._Original_AccessibilityManager",
+            "android.webkit.WebView",                          "android.webkit._Original_WebView",
+            "com.android.internal.policy.PolicyManager",       "com.android.internal.policy._Original_PolicyManager",
+        };
+
+    /**
+     * List of classes for which the methods returning them should be deleted.
+     * The array contains a list of null terminated section starting with the name of the class
+     * to rename in which the methods are deleted, followed by a list of return types identifying
+     * the methods to delete.
+     */
+    private final static String[] DELETE_RETURNS =
+        new String[] {
+            null };                         // separator, for next class/methods list.
+}
+
diff --git a/tools/layoutlib/create/src/com/android/tools/layoutlib/create/DelegateClassAdapter.java b/tools/layoutlib/create/src/com/android/tools/layoutlib/create/DelegateClassAdapter.java
new file mode 100644
index 0000000..927be97
--- /dev/null
+++ b/tools/layoutlib/create/src/com/android/tools/layoutlib/create/DelegateClassAdapter.java
@@ -0,0 +1,133 @@
+/*
+ * Copyright (C) 2010 The Android Open Source Project
+ *
+ * Licensed under the Apache License, Version 2.0 (the "License");
+ * you may not use this file except in compliance with the License.
+ * You may obtain a copy of the License at
+ *
+ *      http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing, software
+ * distributed under the License is distributed on an "AS IS" BASIS,
+ * WITHOUT WARRANTIES 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.tools.layoutlib.create;
+
+import org.objectweb.asm.ClassVisitor;
+import org.objectweb.asm.MethodVisitor;
+import org.objectweb.asm.Opcodes;
+
+import java.util.Set;
+
+/**
+ * A {@link DelegateClassAdapter} can transform some methods from a class into
+ * delegates that defer the call to an associated delegate class.
+ * <p/>
+ * This is used to override specific methods and or all native methods in classes.
+ */
+public class DelegateClassAdapter extends ClassVisitor {
+
+    /** Suffix added to original methods. */
+    private static final String ORIGINAL_SUFFIX = "_Original";
+    private static String CONSTRUCTOR = "<init>";
+    private static String CLASS_INIT = "<clinit>";
+
+    public final static String ALL_NATIVES = "<<all_natives>>";
+
+    private final String mClassName;
+    private final Set<String> mDelegateMethods;
+    private final Log mLog;
+
+    /**
+     * Creates a new {@link DelegateClassAdapter} that can transform some methods
+     * from a class into delegates that defer the call to an associated delegate class.
+     * <p/>
+     * This is used to override specific methods and or all native methods in classes.
+     *
+     * @param log The logger object. Must not be null.
+     * @param cv the class visitor to which this adapter must delegate calls.
+     * @param className The internal class name of the class to visit,
+     *          e.g. <code>com/android/SomeClass$InnerClass</code>.
+     * @param delegateMethods The set of method names to modify and/or the
+     *          special constant {@link #ALL_NATIVES} to convert all native methods.
+     */
+    public DelegateClassAdapter(Log log,
+            ClassVisitor cv,
+            String className,
+            Set<String> delegateMethods) {
+        super(Opcodes.ASM4, cv);
+        mLog = log;
+        mClassName = className;
+        mDelegateMethods = delegateMethods;
+    }
+
+    //----------------------------------
+    // Methods from the ClassAdapter
+
+    @Override
+    public MethodVisitor visitMethod(int access, String name, String desc,
+            String signature, String[] exceptions) {
+
+        boolean isStatic = (access & Opcodes.ACC_STATIC) != 0;
+        boolean isNative = (access & Opcodes.ACC_NATIVE) != 0;
+
+        boolean useDelegate = (isNative && mDelegateMethods.contains(ALL_NATIVES)) ||
+                              mDelegateMethods.contains(name);
+
+        if (!useDelegate) {
+            // Not creating a delegate for this method, pass it as-is from the reader
+            // to the writer.
+            return super.visitMethod(access, name, desc, signature, exceptions);
+        }
+
+        if (useDelegate) {
+            if (CONSTRUCTOR.equals(name) || CLASS_INIT.equals(name)) {
+                // We don't currently support generating delegates for constructors.
+                throw new UnsupportedOperationException(
+                    String.format(
+                        "Delegate doesn't support overriding constructor %1$s:%2$s(%3$s)",  //$NON-NLS-1$
+                        mClassName, name, desc));
+            }
+        }
+
+        if (isNative) {
+            // Remove native flag
+            access = access & ~Opcodes.ACC_NATIVE;
+            MethodVisitor mwDelegate = super.visitMethod(access, name, desc, signature, exceptions);
+
+            DelegateMethodAdapter2 a = new DelegateMethodAdapter2(
+                    mLog, null /*mwOriginal*/, mwDelegate, mClassName, name, desc, isStatic);
+
+            // A native has no code to visit, so we need to generate it directly.
+            a.generateDelegateCode();
+
+            return mwDelegate;
+        }
+
+        // Given a non-native SomeClass.MethodName(), we want to generate 2 methods:
+        // - A copy of the original method named SomeClass.MethodName_Original().
+        //   The content is the original method as-is from the reader.
+        // - A brand new implementation of SomeClass.MethodName() which calls to a
+        //   non-existing method named SomeClass_Delegate.MethodName().
+        //   The implementation of this 'delegate' method is done in layoutlib_brigde.
+
+        int accessDelegate = access;
+        // change access to public for the original one
+        if (Main.sOptions.generatePublicAccess) {
+            access &= ~(Opcodes.ACC_PROTECTED | Opcodes.ACC_PRIVATE);
+            access |= Opcodes.ACC_PUBLIC;
+        }
+
+        MethodVisitor mwOriginal = super.visitMethod(access, name + ORIGINAL_SUFFIX,
+                                                     desc, signature, exceptions);
+        MethodVisitor mwDelegate = super.visitMethod(accessDelegate, name,
+                                                     desc, signature, exceptions);
+
+        DelegateMethodAdapter2 a = new DelegateMethodAdapter2(
+                mLog, mwOriginal, mwDelegate, mClassName, name, desc, isStatic);
+        return a;
+    }
+}
diff --git a/tools/layoutlib/create/src/com/android/tools/layoutlib/create/DelegateMethodAdapter2.java b/tools/layoutlib/create/src/com/android/tools/layoutlib/create/DelegateMethodAdapter2.java
new file mode 100644
index 0000000..0000b22
--- /dev/null
+++ b/tools/layoutlib/create/src/com/android/tools/layoutlib/create/DelegateMethodAdapter2.java
@@ -0,0 +1,461 @@
+/*
+ * Copyright (C) 2010 The Android Open Source Project
+ *
+ * Licensed under the Apache License, Version 2.0 (the "License");
+ * you may not use this file except in compliance with the License.
+ * You may obtain a copy of the License at
+ *
+ *      http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing, software
+ * distributed under the License is distributed on an "AS IS" BASIS,
+ * WITHOUT WARRANTIES 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.tools.layoutlib.create;
+
+import com.android.tools.layoutlib.annotations.LayoutlibDelegate;
+
+import org.objectweb.asm.AnnotationVisitor;
+import org.objectweb.asm.Attribute;
+import org.objectweb.asm.ClassReader;
+import org.objectweb.asm.ClassVisitor;
+import org.objectweb.asm.Label;
+import org.objectweb.asm.MethodVisitor;
+import org.objectweb.asm.Opcodes;
+import org.objectweb.asm.Type;
+
+import java.util.ArrayList;
+
+/**
+ * This method adapter generates delegate methods.
+ * <p/>
+ * Given a method {@code SomeClass.MethodName()}, this generates 1 or 2 methods:
+ * <ul>
+ * <li> A copy of the original method named {@code SomeClass.MethodName_Original()}.
+ *   The content is the original method as-is from the reader.
+ *   This step is omitted if the method is native, since it has no Java implementation.
+ * <li> A brand new implementation of {@code SomeClass.MethodName()} which calls to a
+ *   non-existing method named {@code SomeClass_Delegate.MethodName()}.
+ *   The implementation of this 'delegate' method is done in layoutlib_brigde.
+ * </ul>
+ * A method visitor is generally constructed to generate a single method; however
+ * here we might want to generate one or two depending on the context. To achieve
+ * that, the visitor here generates the 'original' method and acts as a no-op if
+ * no such method exists (e.g. when the original is a native method).
+ * The delegate method is generated after the {@code visitEnd} of the original method
+ * or by having the class adapter <em>directly</em> call {@link #generateDelegateCode()}
+ * for native methods.
+ * <p/>
+ * When generating the 'delegate', the implementation generates a call to a class
+ * class named <code>&lt;className&gt;_Delegate</code> with static methods matching
+ * the methods to be overridden here. The methods have the same return type.
+ * The argument type list is the same except the "this" reference is passed first
+ * for non-static methods.
+ * <p/>
+ * A new annotation is added to these 'delegate' methods so that we can easily find them
+ * for automated testing.
+ * <p/>
+ * This class isn't intended to be generic or reusable.
+ * It is called by {@link DelegateClassAdapter}, which takes care of properly initializing
+ * the two method writers for the original and the delegate class, as needed, with their
+ * expected names.
+ * <p/>
+ * The class adapter also takes care of calling {@link #generateDelegateCode()} directly for
+ * a native and use the visitor pattern for non-natives.
+ * Note that native methods have, by definition, no code so there's nothing a visitor
+ * can visit.
+ * <p/>
+ * Instances of this class are not re-usable.
+ * The class adapter creates a new instance for each method.
+ */
+class DelegateMethodAdapter2 extends MethodVisitor {
+
+    /** Suffix added to delegate classes. */
+    public static final String DELEGATE_SUFFIX = "_Delegate";
+
+    /** The parent method writer to copy of the original method.
+     *  Null when dealing with a native original method. */
+    private MethodVisitor mOrgWriter;
+    /** The parent method writer to generate the delegating method. Never null. */
+    private MethodVisitor mDelWriter;
+    /** The original method descriptor (return type + argument types.) */
+    private String mDesc;
+    /** True if the original method is static. */
+    private final boolean mIsStatic;
+    /** The internal class name (e.g. <code>com/android/SomeClass$InnerClass</code>.) */
+    private final String mClassName;
+    /** The method name. */
+    private final String mMethodName;
+    /** Logger object. */
+    private final Log mLog;
+
+    /** Array used to capture the first line number information from the original method
+     *  and duplicate it in the delegate. */
+    private Object[] mDelegateLineNumber;
+
+    /**
+     * Creates a new {@link DelegateMethodAdapter2} that will transform this method
+     * into a delegate call.
+     * <p/>
+     * See {@link DelegateMethodAdapter2} for more details.
+     *
+     * @param log The logger object. Must not be null.
+     * @param mvOriginal The parent method writer to copy of the original method.
+     *          Must be {@code null} when dealing with a native original method.
+     * @param mvDelegate The parent method writer to generate the delegating method.
+     *          Must never be null.
+     * @param className The internal class name of the class to visit,
+     *          e.g. <code>com/android/SomeClass$InnerClass</code>.
+     * @param methodName The simple name of the method.
+     * @param desc A method descriptor (c.f. {@link Type#getReturnType(String)} +
+     *          {@link Type#getArgumentTypes(String)})
+     * @param isStatic True if the method is declared static.
+     */
+    public DelegateMethodAdapter2(Log log,
+            MethodVisitor mvOriginal,
+            MethodVisitor mvDelegate,
+            String className,
+            String methodName,
+            String desc,
+            boolean isStatic) {
+        super(Opcodes.ASM4);
+        mLog = log;
+        mOrgWriter = mvOriginal;
+        mDelWriter = mvDelegate;
+        mClassName = className;
+        mMethodName = methodName;
+        mDesc = desc;
+        mIsStatic = isStatic;
+    }
+
+    /**
+     * Generates the new code for the method.
+     * <p/>
+     * For native methods, this must be invoked directly by {@link DelegateClassAdapter}
+     * (since they have no code to visit).
+     * <p/>
+     * Otherwise for non-native methods the {@link DelegateClassAdapter} simply needs to
+     * return this instance of {@link DelegateMethodAdapter2} and let the normal visitor pattern
+     * invoke it as part of the {@link ClassReader#accept(ClassVisitor, int)} workflow and then
+     * this method will be invoked from {@link MethodVisitor#visitEnd()}.
+     */
+    public void generateDelegateCode() {
+        /*
+         * The goal is to generate a call to a static delegate method.
+         * If this method is non-static, the first parameter will be 'this'.
+         * All the parameters must be passed and then the eventual return type returned.
+         *
+         * Example, let's say we have a method such as
+         *   public void myMethod(int a, Object b, ArrayList<String> c) { ... }
+         *
+         * We'll want to create a body that calls a delegate method like this:
+         *   TheClass_Delegate.myMethod(this, a, b, c);
+         *
+         * If the method is non-static and the class name is an inner class (e.g. has $ in its
+         * last segment), we want to push the 'this' of the outer class first:
+         *   OuterClass_InnerClass_Delegate.myMethod(
+         *     OuterClass.this,
+         *     OuterClass$InnerClass.this,
+         *     a, b, c);
+         *
+         * Only one level of inner class is supported right now, for simplicity and because
+         * we don't need more.
+         *
+         * The generated class name is the current class name with "_Delegate" appended to it.
+         * One thing to realize is that we don't care about generics -- since generic types
+         * are erased at build time, they have no influence on the method name being called.
+         */
+
+        // Add our annotation
+        AnnotationVisitor aw = mDelWriter.visitAnnotation(
+                Type.getObjectType(Type.getInternalName(LayoutlibDelegate.class)).toString(),
+                true); // visible at runtime
+        if (aw != null) {
+            aw.visitEnd();
+        }
+
+        mDelWriter.visitCode();
+
+        if (mDelegateLineNumber != null) {
+            Object[] p = mDelegateLineNumber;
+            mDelWriter.visitLineNumber((Integer) p[0], (Label) p[1]);
+        }
+
+        ArrayList<Type> paramTypes = new ArrayList<Type>();
+        String delegateClassName = mClassName + DELEGATE_SUFFIX;
+        boolean pushedArg0 = false;
+        int maxStack = 0;
+
+        // Check if the last segment of the class name has inner an class.
+        // Right now we only support one level of inner classes.
+        Type outerType = null;
+        int slash = mClassName.lastIndexOf('/');
+        int dol = mClassName.lastIndexOf('$');
+        if (dol != -1 && dol > slash && dol == mClassName.indexOf('$')) {
+            String outerClass = mClassName.substring(0, dol);
+            outerType = Type.getObjectType(outerClass);
+
+            // Change a delegate class name to "com/foo/Outer_Inner_Delegate"
+            delegateClassName = delegateClassName.replace('$', '_');
+        }
+
+        // For an instance method (e.g. non-static), push the 'this' preceded
+        // by the 'this' of any outer class, if any.
+        if (!mIsStatic) {
+
+            if (outerType != null) {
+                // The first-level inner class has a package-protected member called 'this$0'
+                // that points to the outer class.
+
+                // Push this.getField("this$0") on the call stack.
+                mDelWriter.visitVarInsn(Opcodes.ALOAD, 0); // var 0 = this
+                mDelWriter.visitFieldInsn(Opcodes.GETFIELD,
+                        mClassName,                 // class where the field is defined
+                        "this$0",                   // field name
+                        outerType.getDescriptor()); // type of the field
+                maxStack++;
+                paramTypes.add(outerType);
+
+            }
+
+            // Push "this" for the instance method, which is always ALOAD 0
+            mDelWriter.visitVarInsn(Opcodes.ALOAD, 0);
+            maxStack++;
+            pushedArg0 = true;
+            paramTypes.add(Type.getObjectType(mClassName));
+        }
+
+        // Push all other arguments. Start at arg 1 if we already pushed 'this' above.
+        Type[] argTypes = Type.getArgumentTypes(mDesc);
+        int maxLocals = pushedArg0 ? 1 : 0;
+        for (Type t : argTypes) {
+            int size = t.getSize();
+            mDelWriter.visitVarInsn(t.getOpcode(Opcodes.ILOAD), maxLocals);
+            maxLocals += size;
+            maxStack += size;
+            paramTypes.add(t);
+        }
+
+        // Construct the descriptor of the delegate based on the parameters
+        // we pushed on the call stack. The return type remains unchanged.
+        String desc = Type.getMethodDescriptor(
+                Type.getReturnType(mDesc),
+                paramTypes.toArray(new Type[paramTypes.size()]));
+
+        // Invoke the static delegate
+        mDelWriter.visitMethodInsn(Opcodes.INVOKESTATIC,
+                delegateClassName,
+                mMethodName,
+                desc);
+
+        Type returnType = Type.getReturnType(mDesc);
+        mDelWriter.visitInsn(returnType.getOpcode(Opcodes.IRETURN));
+
+        mDelWriter.visitMaxs(maxStack, maxLocals);
+        mDelWriter.visitEnd();
+
+        // For debugging now. Maybe we should collect these and store them in
+        // a text file for helping create the delegates. We could also compare
+        // the text file to a golden and break the build on unsupported changes
+        // or regressions. Even better we could fancy-print something that looks
+        // like the expected Java method declaration.
+        mLog.debug("Delegate: %1$s # %2$s %3$s", delegateClassName, mMethodName, desc);
+    }
+
+    /* Pass down to visitor writer. In this implementation, either do nothing. */
+    @Override
+    public void visitCode() {
+        if (mOrgWriter != null) {
+            mOrgWriter.visitCode();
+        }
+    }
+
+    /*
+     * visitMaxs is called just before visitEnd if there was any code to rewrite.
+     */
+    @Override
+    public void visitMaxs(int maxStack, int maxLocals) {
+        if (mOrgWriter != null) {
+            mOrgWriter.visitMaxs(maxStack, maxLocals);
+        }
+    }
+
+    /** End of visiting. Generate the delegating code. */
+    @Override
+    public void visitEnd() {
+        if (mOrgWriter != null) {
+            mOrgWriter.visitEnd();
+        }
+        generateDelegateCode();
+    }
+
+    /* Writes all annotation from the original method. */
+    @Override
+    public AnnotationVisitor visitAnnotation(String desc, boolean visible) {
+        if (mOrgWriter != null) {
+            return mOrgWriter.visitAnnotation(desc, visible);
+        } else {
+            return null;
+        }
+    }
+
+    /* Writes all annotation default values from the original method. */
+    @Override
+    public AnnotationVisitor visitAnnotationDefault() {
+        if (mOrgWriter != null) {
+            return mOrgWriter.visitAnnotationDefault();
+        } else {
+            return null;
+        }
+    }
+
+    @Override
+    public AnnotationVisitor visitParameterAnnotation(int parameter, String desc,
+            boolean visible) {
+        if (mOrgWriter != null) {
+            return mOrgWriter.visitParameterAnnotation(parameter, desc, visible);
+        } else {
+            return null;
+        }
+    }
+
+    /* Writes all attributes from the original method. */
+    @Override
+    public void visitAttribute(Attribute attr) {
+        if (mOrgWriter != null) {
+            mOrgWriter.visitAttribute(attr);
+        }
+    }
+
+    /*
+     * Only writes the first line number present in the original code so that source
+     * viewers can direct to the correct method, even if the content doesn't match.
+     */
+    @Override
+    public void visitLineNumber(int line, Label start) {
+        // Capture the first line values for the new delegate method
+        if (mDelegateLineNumber == null) {
+            mDelegateLineNumber = new Object[] { line, start };
+        }
+        if (mOrgWriter != null) {
+            mOrgWriter.visitLineNumber(line, start);
+        }
+    }
+
+    @Override
+    public void visitInsn(int opcode) {
+        if (mOrgWriter != null) {
+            mOrgWriter.visitInsn(opcode);
+        }
+    }
+
+    @Override
+    public void visitLabel(Label label) {
+        if (mOrgWriter != null) {
+            mOrgWriter.visitLabel(label);
+        }
+    }
+
+    @Override
+    public void visitTryCatchBlock(Label start, Label end, Label handler, String type) {
+        if (mOrgWriter != null) {
+            mOrgWriter.visitTryCatchBlock(start, end, handler, type);
+        }
+    }
+
+    @Override
+    public void visitMethodInsn(int opcode, String owner, String name, String desc) {
+        if (mOrgWriter != null) {
+            mOrgWriter.visitMethodInsn(opcode, owner, name, desc);
+        }
+    }
+
+    @Override
+    public void visitFieldInsn(int opcode, String owner, String name, String desc) {
+        if (mOrgWriter != null) {
+            mOrgWriter.visitFieldInsn(opcode, owner, name, desc);
+        }
+    }
+
+    @Override
+    public void visitFrame(int type, int nLocal, Object[] local, int nStack, Object[] stack) {
+        if (mOrgWriter != null) {
+            mOrgWriter.visitFrame(type, nLocal, local, nStack, stack);
+        }
+    }
+
+    @Override
+    public void visitIincInsn(int var, int increment) {
+        if (mOrgWriter != null) {
+            mOrgWriter.visitIincInsn(var, increment);
+        }
+    }
+
+    @Override
+    public void visitIntInsn(int opcode, int operand) {
+        if (mOrgWriter != null) {
+            mOrgWriter.visitIntInsn(opcode, operand);
+        }
+    }
+
+    @Override
+    public void visitJumpInsn(int opcode, Label label) {
+        if (mOrgWriter != null) {
+            mOrgWriter.visitJumpInsn(opcode, label);
+        }
+    }
+
+    @Override
+    public void visitLdcInsn(Object cst) {
+        if (mOrgWriter != null) {
+            mOrgWriter.visitLdcInsn(cst);
+        }
+    }
+
+    @Override
+    public void visitLocalVariable(String name, String desc, String signature,
+            Label start, Label end, int index) {
+        if (mOrgWriter != null) {
+            mOrgWriter.visitLocalVariable(name, desc, signature, start, end, index);
+        }
+    }
+
+    @Override
+    public void visitLookupSwitchInsn(Label dflt, int[] keys, Label[] labels) {
+        if (mOrgWriter != null) {
+            mOrgWriter.visitLookupSwitchInsn(dflt, keys, labels);
+        }
+    }
+
+    @Override
+    public void visitMultiANewArrayInsn(String desc, int dims) {
+        if (mOrgWriter != null) {
+            mOrgWriter.visitMultiANewArrayInsn(desc, dims);
+        }
+    }
+
+    @Override
+    public void visitTableSwitchInsn(int min, int max, Label dflt, Label[] labels) {
+        if (mOrgWriter != null) {
+            mOrgWriter.visitTableSwitchInsn(min, max, dflt, labels);
+        }
+    }
+
+    @Override
+    public void visitTypeInsn(int opcode, String type) {
+        if (mOrgWriter != null) {
+            mOrgWriter.visitTypeInsn(opcode, type);
+        }
+    }
+
+    @Override
+    public void visitVarInsn(int opcode, int var) {
+        if (mOrgWriter != null) {
+            mOrgWriter.visitVarInsn(opcode, var);
+        }
+    }
+
+}
diff --git a/tools/layoutlib/create/src/com/android/tools/layoutlib/create/DependencyFinder.java b/tools/layoutlib/create/src/com/android/tools/layoutlib/create/DependencyFinder.java
new file mode 100644
index 0000000..c988c70
--- /dev/null
+++ b/tools/layoutlib/create/src/com/android/tools/layoutlib/create/DependencyFinder.java
@@ -0,0 +1,787 @@
+/*
+ * Copyright (C) 2012 The Android Open Source Project
+ *
+ * Licensed under the Apache License, Version 2.0 (the "License");
+ * you may not use this file except in compliance with the License.
+ * You may obtain a copy of the License at
+ *
+ *      http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing, software
+ * distributed under the License is distributed on an "AS IS" BASIS,
+ * WITHOUT WARRANTIES 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.tools.layoutlib.create;
+
+import com.android.tools.layoutlib.annotations.VisibleForTesting;
+import com.android.tools.layoutlib.annotations.VisibleForTesting.Visibility;
+
+import org.objectweb.asm.AnnotationVisitor;
+import org.objectweb.asm.Attribute;
+import org.objectweb.asm.ClassReader;
+import org.objectweb.asm.ClassVisitor;
+import org.objectweb.asm.FieldVisitor;
+import org.objectweb.asm.Label;
+import org.objectweb.asm.MethodVisitor;
+import org.objectweb.asm.Opcodes;
+import org.objectweb.asm.Type;
+import org.objectweb.asm.signature.SignatureReader;
+import org.objectweb.asm.signature.SignatureVisitor;
+
+import java.io.IOException;
+import java.util.ArrayList;
+import java.util.Enumeration;
+import java.util.List;
+import java.util.Map;
+import java.util.Map.Entry;
+import java.util.Set;
+import java.util.TreeMap;
+import java.util.TreeSet;
+import java.util.zip.ZipEntry;
+import java.util.zip.ZipFile;
+
+/**
+ * Analyzes the input JAR using the ASM java bytecode manipulation library
+ * to list the classes and their dependencies. A "dependency" is a class
+ * used by another class.
+ */
+public class DependencyFinder {
+
+    // Note: a bunch of stuff has package-level access for unit tests. Consider it private.
+
+    /** Output logger. */
+    private final Log mLog;
+
+    /**
+     * Creates a new analyzer.
+     *
+     * @param log The log output.
+     */
+    public DependencyFinder(Log log) {
+        mLog = log;
+    }
+
+    /**
+     * Starts the analysis using parameters from the constructor.
+     *
+     * @param osJarPath The input source JARs to parse.
+     * @return A pair: [0]: map { class FQCN => set of FQCN class dependencies }.
+     *                 [1]: map { missing class FQCN => set of FQCN class that uses it. }
+     */
+    public List<Map<String, Set<String>>> findDeps(List<String> osJarPath) throws IOException {
+
+        Map<String, ClassReader> zipClasses = parseZip(osJarPath);
+        mLog.info("Found %d classes in input JAR%s.",
+                zipClasses.size(),
+                osJarPath.size() > 1 ? "s" : "");
+
+        Map<String, Set<String>> deps = findClassesDeps(zipClasses);
+
+        Map<String, Set<String>> missing = findMissingClasses(deps, zipClasses.keySet());
+
+        List<Map<String, Set<String>>> result = new ArrayList<Map<String,Set<String>>>(2);
+        result.add(deps);
+        result.add(missing);
+        return result;
+    }
+
+    /**
+     * Prints dependencies to the current logger, found stuff and missing stuff.
+     */
+    public void printAllDeps(List<Map<String, Set<String>>> result) {
+        assert result.size() == 2;
+        Map<String, Set<String>> deps = result.get(0);
+        Map<String, Set<String>> missing = result.get(1);
+
+        // Print all dependences found in the format:
+        // +Found: <FQCN from zip>
+        //     uses: FQCN
+
+        mLog.info("++++++ %d Entries found in source JARs", deps.size());
+        mLog.info("");
+
+        for (Entry<String, Set<String>> entry : deps.entrySet()) {
+            mLog.info(    "+Found  : %s", entry.getKey());
+            for (String dep : entry.getValue()) {
+                mLog.info("    uses: %s", dep);
+            }
+
+            mLog.info("");
+        }
+
+
+        // Now print all missing dependences in the format:
+        // -Missing <FQCN>:
+        //     used by: <FQCN>
+
+        mLog.info("");
+        mLog.info("------ %d Entries missing from source JARs", missing.size());
+        mLog.info("");
+
+        for (Entry<String, Set<String>> entry : missing.entrySet()) {
+            mLog.info(    "-Missing  : %s", entry.getKey());
+            for (String dep : entry.getValue()) {
+                mLog.info("   used by: %s", dep);
+            }
+
+            mLog.info("");
+        }
+    }
+
+    /**
+     * Prints only a summary of the missing dependencies to the current logger.
+     */
+    public void printMissingDeps(List<Map<String, Set<String>>> result) {
+        assert result.size() == 2;
+        @SuppressWarnings("unused") Map<String, Set<String>> deps = result.get(0);
+        Map<String, Set<String>> missing = result.get(1);
+
+        for (String fqcn : missing.keySet()) {
+            mLog.info("%s", fqcn);
+        }
+    }
+
+    // ----------------
+
+    /**
+     * Parses a JAR file and returns a list of all classes founds using a map
+     * class name => ASM ClassReader. Class names are in the form "android.view.View".
+     */
+    Map<String,ClassReader> parseZip(List<String> jarPathList) throws IOException {
+        TreeMap<String, ClassReader> classes = new TreeMap<String, ClassReader>();
+
+        for (String jarPath : jarPathList) {
+            ZipFile zip = new ZipFile(jarPath);
+            Enumeration<? extends ZipEntry> entries = zip.entries();
+            ZipEntry entry;
+            while (entries.hasMoreElements()) {
+                entry = entries.nextElement();
+                if (entry.getName().endsWith(".class")) {
+                    ClassReader cr = new ClassReader(zip.getInputStream(entry));
+                    String className = classReaderToClassName(cr);
+                    classes.put(className, cr);
+                }
+            }
+        }
+
+        return classes;
+    }
+
+    /**
+     * Utility that returns the fully qualified binary class name for a ClassReader.
+     * E.g. it returns something like android.view.View.
+     */
+    static String classReaderToClassName(ClassReader classReader) {
+        if (classReader == null) {
+            return null;
+        } else {
+            return classReader.getClassName().replace('/', '.');
+        }
+    }
+
+    /**
+     * Utility that returns the fully qualified binary class name from a path-like FQCN.
+     * E.g. it returns android.view.View from android/view/View.
+     */
+    static String internalToBinaryClassName(String className) {
+        if (className == null) {
+            return null;
+        } else {
+            return className.replace('/', '.');
+        }
+    }
+
+    /**
+     * Finds all dependencies for all classes in keepClasses which are also
+     * listed in zipClasses. Returns a map of all the dependencies found.
+     */
+    Map<String, Set<String>> findClassesDeps(Map<String, ClassReader> zipClasses) {
+
+        // The dependencies that we'll collect.
+        // It's a map Class name => uses class names.
+        Map<String, Set<String>> dependencyMap = new TreeMap<String, Set<String>>();
+
+        DependencyVisitor visitor = getVisitor();
+
+        int count = 0;
+        try {
+            for (Entry<String, ClassReader> entry : zipClasses.entrySet()) {
+                String name = entry.getKey();
+
+                TreeSet<String> set = new TreeSet<String>();
+                dependencyMap.put(name, set);
+                visitor.setDependencySet(set);
+
+                ClassReader cr = entry.getValue();
+                cr.accept(visitor, 0 /* flags */);
+
+                visitor.setDependencySet(null);
+
+                mLog.debugNoln("Visited %d classes\r", ++count);
+            }
+        } finally {
+            mLog.debugNoln("\n");
+        }
+
+        return dependencyMap;
+    }
+
+    /**
+     * Computes which classes FQCN were found as dependencies that are NOT listed
+     * in the original JAR classes.
+     *
+     * @param deps The map { FQCN => dependencies[] } returned by {@link #findClassesDeps(Map)}.
+     * @param zipClasses The set of all classes FQCN found in the JAR files.
+     * @return A map { FQCN not found in the zipClasses => classes using it }
+     */
+    private Map<String, Set<String>> findMissingClasses(
+            Map<String, Set<String>> deps,
+            Set<String> zipClasses) {
+        Map<String, Set<String>> missing = new TreeMap<String, Set<String>>();
+
+        for (Entry<String, Set<String>> entry : deps.entrySet()) {
+            String name = entry.getKey();
+
+            for (String dep : entry.getValue()) {
+                if (!zipClasses.contains(dep)) {
+                    // This dependency doesn't exist in the zip classes.
+                    Set<String> set = missing.get(dep);
+                    if (set == null) {
+                        set = new TreeSet<String>();
+                        missing.put(dep, set);
+                    }
+                    set.add(name);
+                }
+            }
+
+        }
+
+        return missing;
+    }
+
+
+    // ----------------------------------
+
+    /**
+     * Instantiates a new DependencyVisitor. Useful for unit tests.
+     */
+    @VisibleForTesting(visibility=Visibility.PRIVATE)
+    DependencyVisitor getVisitor() {
+        return new DependencyVisitor();
+    }
+
+    /**
+     * Visitor to collect all the type dependencies from a class.
+     */
+    public class DependencyVisitor extends ClassVisitor {
+
+        private Set<String> mCurrentDepSet;
+
+        /**
+         * Creates a new visitor that will find all the dependencies for the visited class.
+         */
+        public DependencyVisitor() {
+            super(Opcodes.ASM4);
+        }
+
+        /**
+         * Sets the {@link Set} where to record direct dependencies for this class.
+         * This will change before each {@link ClassReader#accept(ClassVisitor, int)} call.
+         */
+        public void setDependencySet(Set<String> set) {
+            mCurrentDepSet = set;
+        }
+
+        /**
+         * Considers the given class name as a dependency.
+         */
+        public void considerName(String className) {
+            if (className == null) {
+                return;
+            }
+
+            className = internalToBinaryClassName(className);
+
+            try {
+                // exclude classes that are part of the default JRE (the one executing this program)
+                if (getClass().getClassLoader().loadClass(className) != null) {
+                    return;
+                }
+            } catch (ClassNotFoundException e) {
+                // ignore
+            }
+
+            // Add it to the dependency set for the currently visited class, as needed.
+            assert mCurrentDepSet != null;
+            if (mCurrentDepSet != null) {
+                mCurrentDepSet.add(className);
+            }
+        }
+
+        /**
+         * Considers this array of names using considerName().
+         */
+        public void considerNames(String[] classNames) {
+            if (classNames != null) {
+                for (String className : classNames) {
+                    considerName(className);
+                }
+            }
+        }
+
+        /**
+         * Considers this signature or type signature by invoking the {@link SignatureVisitor}
+         * on it.
+         */
+        public void considerSignature(String signature) {
+            if (signature != null) {
+                SignatureReader sr = new SignatureReader(signature);
+                // SignatureReader.accept will call accessType so we don't really have
+                // to differentiate where the signature comes from.
+                sr.accept(new MySignatureVisitor());
+            }
+        }
+
+        /**
+         * Considers this {@link Type}. For arrays, the element type is considered.
+         * If the type is an object, it's internal name is considered.
+         */
+        public void considerType(Type t) {
+            if (t != null) {
+                if (t.getSort() == Type.ARRAY) {
+                    t = t.getElementType();
+                }
+                if (t.getSort() == Type.OBJECT) {
+                    considerName(t.getInternalName());
+                }
+            }
+        }
+
+        /**
+         * Considers a descriptor string. The descriptor is converted to a {@link Type}
+         * and then considerType() is invoked.
+         */
+        public boolean considerDesc(String desc) {
+            if (desc != null) {
+                try {
+                    if (desc.length() > 0 && desc.charAt(0) == '(') {
+                        // This is a method descriptor with arguments and a return type.
+                        Type t = Type.getReturnType(desc);
+                        considerType(t);
+
+                        for (Type arg : Type.getArgumentTypes(desc)) {
+                            considerType(arg);
+                        }
+
+                    } else {
+                        Type t = Type.getType(desc);
+                        considerType(t);
+                    }
+                    return true;
+                } catch (ArrayIndexOutOfBoundsException e) {
+                    // ignore, not a valid type.
+                }
+            }
+            return false;
+        }
+
+
+        // ---------------------------------------------------
+        // --- ClassVisitor, FieldVisitor
+        // ---------------------------------------------------
+
+        // Visits a class header
+        @Override
+        public void visit(int version, int access, String name,
+                String signature, String superName, String[] interfaces) {
+            // signature is the signature of this class. May be null if the class is not a generic
+            // one, and does not extend or implement generic classes or interfaces.
+
+            if (signature != null) {
+                considerSignature(signature);
+            }
+
+            // superName is the internal of name of the super class (see getInternalName).
+            // For interfaces, the super class is Object. May be null but only for the Object class.
+            considerName(superName);
+
+            // interfaces is the internal names of the class's interfaces (see getInternalName).
+            // May be null.
+            considerNames(interfaces);
+        }
+
+
+        @Override
+        public AnnotationVisitor visitAnnotation(String desc, boolean visible) {
+            // desc is the class descriptor of the annotation class.
+            considerDesc(desc);
+            return new MyAnnotationVisitor();
+        }
+
+        @Override
+        public void visitAttribute(Attribute attr) {
+            // pass
+        }
+
+        // Visits the end of a class
+        @Override
+        public void visitEnd() {
+            // pass
+        }
+
+        private class MyFieldVisitor extends FieldVisitor {
+
+            public MyFieldVisitor() {
+                super(Opcodes.ASM4);
+            }
+
+            @Override
+            public AnnotationVisitor visitAnnotation(String desc, boolean visible) {
+                // desc is the class descriptor of the annotation class.
+                considerDesc(desc);
+                return new MyAnnotationVisitor();
+            }
+
+            @Override
+            public void visitAttribute(Attribute attr) {
+                // pass
+            }
+
+            // Visits the end of a class
+            @Override
+            public void visitEnd() {
+                // pass
+            }
+        }
+
+        @Override
+        public FieldVisitor visitField(int access, String name, String desc,
+                String signature, Object value) {
+            // desc is the field's descriptor (see Type).
+            considerDesc(desc);
+
+            // signature is the field's signature. May be null if the field's type does not use
+            // generic types.
+            considerSignature(signature);
+
+            return new MyFieldVisitor();
+        }
+
+        @Override
+        public void visitInnerClass(String name, String outerName, String innerName, int access) {
+            // name is the internal name of an inner class (see getInternalName).
+            // Note: outerName/innerName seems to be null when we're reading the
+            // _Original_ClassName classes generated by layoutlib_create.
+            if (outerName != null) {
+                considerName(name);
+            }
+        }
+
+        @Override
+        public MethodVisitor visitMethod(int access, String name, String desc,
+                String signature, String[] exceptions) {
+            // desc is the method's descriptor (see Type).
+            considerDesc(desc);
+            // signature is the method's signature. May be null if the method parameters, return
+            // type and exceptions do not use generic types.
+            considerSignature(signature);
+
+            return new MyMethodVisitor();
+        }
+
+        @Override
+        public void visitOuterClass(String owner, String name, String desc) {
+            // pass
+        }
+
+        @Override
+        public void visitSource(String source, String debug) {
+            // pass
+        }
+
+
+        // ---------------------------------------------------
+        // --- MethodVisitor
+        // ---------------------------------------------------
+
+        private class MyMethodVisitor extends MethodVisitor {
+
+            public MyMethodVisitor() {
+                super(Opcodes.ASM4);
+            }
+
+
+            @Override
+            public AnnotationVisitor visitAnnotationDefault() {
+                return new MyAnnotationVisitor();
+            }
+
+            @Override
+            public void visitCode() {
+                // pass
+            }
+
+            // field instruction
+            @Override
+            public void visitFieldInsn(int opcode, String owner, String name, String desc) {
+                // name is the field's name.
+                // desc is the field's descriptor (see Type).
+                considerDesc(desc);
+            }
+
+            @Override
+            public void visitFrame(int type, int local, Object[] local2, int stack, Object[] stack2) {
+                // pass
+            }
+
+            @Override
+            public void visitIincInsn(int var, int increment) {
+                // pass -- an IINC instruction
+            }
+
+            @Override
+            public void visitInsn(int opcode) {
+                // pass -- a zero operand instruction
+            }
+
+            @Override
+            public void visitIntInsn(int opcode, int operand) {
+                // pass -- a single int operand instruction
+            }
+
+            @Override
+            public void visitJumpInsn(int opcode, Label label) {
+                // pass -- a jump instruction
+            }
+
+            @Override
+            public void visitLabel(Label label) {
+                // pass -- a label target
+            }
+
+            // instruction to load a constant from the stack
+            @Override
+            public void visitLdcInsn(Object cst) {
+                if (cst instanceof Type) {
+                    considerType((Type) cst);
+                }
+            }
+
+            @Override
+            public void visitLineNumber(int line, Label start) {
+                // pass
+            }
+
+            @Override
+            public void visitLocalVariable(String name, String desc,
+                    String signature, Label start, Label end, int index) {
+                // desc is the type descriptor of this local variable.
+                considerDesc(desc);
+                // signature is the type signature of this local variable. May be null if the local
+                // variable type does not use generic types.
+                considerSignature(signature);
+            }
+
+            @Override
+            public void visitLookupSwitchInsn(Label dflt, int[] keys, Label[] labels) {
+                // pass -- a lookup switch instruction
+            }
+
+            @Override
+            public void visitMaxs(int maxStack, int maxLocals) {
+                // pass
+            }
+
+            // instruction that invokes a method
+            @Override
+            public void visitMethodInsn(int opcode, String owner, String name, String desc) {
+
+                // owner is the internal name of the method's owner class
+                if (!considerDesc(owner) && owner.indexOf('/') != -1) {
+                    considerName(owner);
+                }
+                // desc is the method's descriptor (see Type).
+                considerDesc(desc);
+            }
+
+            // instruction multianewarray, whatever that is
+            @Override
+            public void visitMultiANewArrayInsn(String desc, int dims) {
+
+                // desc an array type descriptor.
+                considerDesc(desc);
+            }
+
+            @Override
+            public AnnotationVisitor visitParameterAnnotation(int parameter, String desc,
+                    boolean visible) {
+                // desc is the class descriptor of the annotation class.
+                considerDesc(desc);
+                return new MyAnnotationVisitor();
+            }
+
+            @Override
+            public void visitTableSwitchInsn(int min, int max, Label dflt, Label[] labels) {
+                // pass -- table switch instruction
+
+            }
+
+            @Override
+            public void visitTryCatchBlock(Label start, Label end, Label handler, String type) {
+                // type is the internal name of the type of exceptions handled by the handler,
+                // or null to catch any exceptions (for "finally" blocks).
+                considerName(type);
+            }
+
+            // type instruction
+            @Override
+            public void visitTypeInsn(int opcode, String type) {
+                // type is the operand of the instruction to be visited. This operand must be the
+                // internal name of an object or array class.
+                considerName(type);
+            }
+
+            @Override
+            public void visitVarInsn(int opcode, int var) {
+                // pass -- local variable instruction
+            }
+        }
+
+        private class MySignatureVisitor extends SignatureVisitor {
+
+            public MySignatureVisitor() {
+                super(Opcodes.ASM4);
+            }
+
+            // ---------------------------------------------------
+            // --- SignatureVisitor
+            // ---------------------------------------------------
+
+            private String mCurrentSignatureClass = null;
+
+            // Starts the visit of a signature corresponding to a class or interface type
+            @Override
+            public void visitClassType(String name) {
+                mCurrentSignatureClass = name;
+                considerName(name);
+            }
+
+            // Visits an inner class
+            @Override
+            public void visitInnerClassType(String name) {
+                if (mCurrentSignatureClass != null) {
+                    mCurrentSignatureClass += "$" + name;
+                    considerName(mCurrentSignatureClass);
+                }
+            }
+
+            @Override
+            public SignatureVisitor visitArrayType() {
+                return new MySignatureVisitor();
+            }
+
+            @Override
+            public void visitBaseType(char descriptor) {
+                // pass -- a primitive type, ignored
+            }
+
+            @Override
+            public SignatureVisitor visitClassBound() {
+                return new MySignatureVisitor();
+            }
+
+            @Override
+            public SignatureVisitor visitExceptionType() {
+                return new MySignatureVisitor();
+            }
+
+            @Override
+            public void visitFormalTypeParameter(String name) {
+                // pass
+            }
+
+            @Override
+            public SignatureVisitor visitInterface() {
+                return new MySignatureVisitor();
+            }
+
+            @Override
+            public SignatureVisitor visitInterfaceBound() {
+                return new MySignatureVisitor();
+            }
+
+            @Override
+            public SignatureVisitor visitParameterType() {
+                return new MySignatureVisitor();
+            }
+
+            @Override
+            public SignatureVisitor visitReturnType() {
+                return new MySignatureVisitor();
+            }
+
+            @Override
+            public SignatureVisitor visitSuperclass() {
+                return new MySignatureVisitor();
+            }
+
+            @Override
+            public SignatureVisitor visitTypeArgument(char wildcard) {
+                return new MySignatureVisitor();
+            }
+
+            @Override
+            public void visitTypeVariable(String name) {
+                // pass
+            }
+
+            @Override
+            public void visitTypeArgument() {
+                // pass
+            }
+        }
+
+
+        // ---------------------------------------------------
+        // --- AnnotationVisitor
+        // ---------------------------------------------------
+
+        private class MyAnnotationVisitor extends AnnotationVisitor {
+
+            public MyAnnotationVisitor() {
+                super(Opcodes.ASM4);
+            }
+
+            // Visits a primitive value of an annotation
+            @Override
+            public void visit(String name, Object value) {
+                // value is the actual value, whose type must be Byte, Boolean, Character, Short,
+                // Integer, Long, Float, Double, String or Type
+                if (value instanceof Type) {
+                    considerType((Type) value);
+                }
+            }
+
+            @Override
+            public AnnotationVisitor visitAnnotation(String name, String desc) {
+                // desc is the class descriptor of the nested annotation class.
+                considerDesc(desc);
+                return new MyAnnotationVisitor();
+            }
+
+            @Override
+            public AnnotationVisitor visitArray(String name) {
+                return new MyAnnotationVisitor();
+            }
+
+            @Override
+            public void visitEnum(String name, String desc, String value) {
+                // desc is the class descriptor of the enumeration class.
+                considerDesc(desc);
+            }
+        }
+    }
+}
diff --git a/tools/layoutlib/create/src/com/android/tools/layoutlib/create/ICreateInfo.java b/tools/layoutlib/create/src/com/android/tools/layoutlib/create/ICreateInfo.java
new file mode 100644
index 0000000..40c1706
--- /dev/null
+++ b/tools/layoutlib/create/src/com/android/tools/layoutlib/create/ICreateInfo.java
@@ -0,0 +1,65 @@
+/*
+ * Copyright (C) 2010 The Android Open Source Project
+ *
+ * Licensed under the Apache License, Version 2.0 (the "License");
+ * you may not use this file except in compliance with the License.
+ * You may obtain a copy of the License at
+ *
+ *      http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing, software
+ * distributed under the License is distributed on an "AS IS" BASIS,
+ * WITHOUT WARRANTIES 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.tools.layoutlib.create;
+
+/**
+ * Interface describing the work to be done by {@link AsmGenerator}.
+ */
+public interface ICreateInfo {
+
+    /**
+     * Returns the list of class from layoutlib_create to inject in layoutlib.
+     * The list can be empty but must not be null.
+     */
+    public abstract Class<?>[] getInjectedClasses();
+
+    /**
+     * Returns the list of methods to rewrite as delegates.
+     * The list can be empty but must not be null.
+     */
+    public abstract String[] getDelegateMethods();
+
+    /**
+     * Returns the list of classes on which to delegate all native methods.
+     * The list can be empty but must not be null.
+     */
+    public abstract String[] getDelegateClassNatives();
+
+    /**
+     * Returns The list of methods to stub out. Each entry must be in the form
+     * "package.package.OuterClass$InnerClass#MethodName".
+     * The list can be empty but must not be null.
+     */
+    public abstract String[] getOverriddenMethods();
+
+    /**
+     * Returns the list of classes to rename, must be an even list: the binary FQCN
+     * of class to replace followed by the new FQCN.
+     * The list can be empty but must not be null.
+     */
+    public abstract String[] getRenamedClasses();
+
+    /**
+     * Returns the list of classes for which the methods returning them should be deleted.
+     * The array contains a list of null terminated section starting with the name of the class
+     * to rename in which the methods are deleted, followed by a list of return types identifying
+     * the methods to delete.
+     * The list can be empty but must not be null.
+     */
+    public abstract String[] getDeleteReturns();
+
+}
diff --git a/tools/layoutlib/create/src/com/android/tools/layoutlib/create/Log.java b/tools/layoutlib/create/src/com/android/tools/layoutlib/create/Log.java
new file mode 100644
index 0000000..c3ba591
--- /dev/null
+++ b/tools/layoutlib/create/src/com/android/tools/layoutlib/create/Log.java
@@ -0,0 +1,72 @@
+/*
+ * Copyright (C) 2008 The Android Open Source Project
+ *
+ * Licensed under the Apache License, Version 2.0 (the "License");
+ * you may not use this file except in compliance with the License.
+ * You may obtain a copy of the License at
+ *
+ *      http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing, software
+ * distributed under the License is distributed on an "AS IS" BASIS,
+ * WITHOUT WARRANTIES 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.tools.layoutlib.create;
+
+import java.io.PrintWriter;
+import java.io.StringWriter;
+
+public class Log {
+
+    private boolean mVerbose = false;
+
+    public void setVerbose(boolean verbose) {
+        mVerbose = verbose;
+    }
+
+    public void debug(String format, Object... args) {
+        if (mVerbose) {
+            info(format, args);
+        }
+    }
+
+    /** Similar to debug() but doesn't do a \n automatically. */
+    public void debugNoln(String format, Object... args) {
+        if (mVerbose) {
+            String s = String.format(format, args);
+            System.out.print(s);
+        }
+    }
+
+    public void info(String format, Object... args) {
+        String s = String.format(format, args);
+        outPrintln(s);
+    }
+
+    public void error(String format, Object... args) {
+        String s = String.format(format, args);
+        errPrintln(s);
+    }
+
+    public void exception(Throwable t, String format, Object... args) {
+        StringWriter sw = new StringWriter();
+        PrintWriter pw = new PrintWriter(sw);
+        t.printStackTrace(pw);
+        pw.flush();
+        error(format + "\n" + sw.toString(), args);
+    }
+
+    /** for unit testing */
+    protected void errPrintln(String msg) {
+        System.err.println(msg);
+    }
+
+    /** for unit testing */
+    protected void outPrintln(String msg) {
+        System.out.println(msg);
+    }
+
+}
diff --git a/tools/layoutlib/create/src/com/android/tools/layoutlib/create/LogAbortException.java b/tools/layoutlib/create/src/com/android/tools/layoutlib/create/LogAbortException.java
new file mode 100644
index 0000000..dc4b4a7
--- /dev/null
+++ b/tools/layoutlib/create/src/com/android/tools/layoutlib/create/LogAbortException.java
@@ -0,0 +1,32 @@
+/*
+ * Copyright (C) 2008 The Android Open Source Project
+ *
+ * Licensed under the Apache License, Version 2.0 (the "License");
+ * you may not use this file except in compliance with the License.
+ * You may obtain a copy of the License at
+ *
+ *      http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing, software
+ * distributed under the License is distributed on an "AS IS" BASIS,
+ * WITHOUT WARRANTIES 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.tools.layoutlib.create;
+
+public class LogAbortException extends Exception {
+
+    private final String mFormat;
+    private final Object[] mArgs;
+
+    public LogAbortException(String format, Object... args) {
+        mFormat = format;
+        mArgs = args;
+    }
+    
+    public void error(Log log) {
+        log.error(mFormat, mArgs);
+    }
+}
diff --git a/tools/layoutlib/create/src/com/android/tools/layoutlib/create/Main.java b/tools/layoutlib/create/src/com/android/tools/layoutlib/create/Main.java
new file mode 100644
index 0000000..9cd74db
--- /dev/null
+++ b/tools/layoutlib/create/src/com/android/tools/layoutlib/create/Main.java
@@ -0,0 +1,212 @@
+/*
+ * Copyright (C) 2008 The Android Open Source Project
+ *
+ * Licensed under the Apache License, Version 2.0 (the "License");
+ * you may not use this file except in compliance with the License.
+ * You may obtain a copy of the License at
+ *
+ *      http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing, software
+ * distributed under the License is distributed on an "AS IS" BASIS,
+ * WITHOUT WARRANTIES 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.tools.layoutlib.create;
+
+import java.io.IOException;
+import java.util.ArrayList;
+import java.util.List;
+import java.util.Map;
+import java.util.Set;
+
+
+/**
+ * Entry point for the layoutlib_create tool.
+ * <p/>
+ * The tool does not currently rely on any external configuration file.
+ * Instead the configuration is mostly done via the {@link CreateInfo} class.
+ * <p/>
+ * For a complete description of the tool and its implementation, please refer to
+ * the "README.txt" file at the root of this project.
+ * <p/>
+ * For a quick test, invoke this as follows:
+ * <pre>
+ * $ make layoutlib
+ * </pre>
+ * which does:
+ * <pre>
+ * $ make layoutlib_create &lt;bunch of framework jars&gt;
+ * $ java -jar out/host/linux-x86/framework/layoutlib_create.jar \
+ *        out/host/common/obj/JAVA_LIBRARIES/temp_layoutlib_intermediates/javalib.jar \
+ *        out/target/common/obj/JAVA_LIBRARIES/core_intermediates/classes.jar \
+ *        out/target/common/obj/JAVA_LIBRARIES/framework_intermediates/classes.jar
+ * </pre>
+ */
+public class Main {
+
+    public static class Options {
+        public boolean generatePublicAccess = true;
+        public boolean listAllDeps = false;
+        public boolean listOnlyMissingDeps = false;
+    }
+
+    public static final Options sOptions = new Options();
+
+    public static void main(String[] args) {
+
+        Log log = new Log();
+
+        ArrayList<String> osJarPath = new ArrayList<String>();
+        String[] osDestJar = { null };
+
+        if (!processArgs(log, args, osJarPath, osDestJar)) {
+            log.error("Usage: layoutlib_create [-v] [-p] output.jar input.jar ...");
+            log.error("Usage: layoutlib_create [-v] [--list-deps|--missing-deps] input.jar ...");
+            System.exit(1);
+        }
+
+        if (sOptions.listAllDeps || sOptions.listOnlyMissingDeps) {
+            System.exit(listDeps(osJarPath, log));
+
+        } else {
+            System.exit(createLayoutLib(osDestJar[0], osJarPath, log));
+        }
+
+
+        System.exit(1);
+    }
+
+    private static int createLayoutLib(String osDestJar, ArrayList<String> osJarPath, Log log) {
+        log.info("Output: %1$s", osDestJar);
+        for (String path : osJarPath) {
+            log.info("Input :      %1$s", path);
+        }
+
+        try {
+            AsmGenerator agen = new AsmGenerator(log, osDestJar, new CreateInfo());
+
+            AsmAnalyzer aa = new AsmAnalyzer(log, osJarPath, agen,
+                    new String[] {                          // derived from
+                        "android.view.View",
+                        "android.app.Fragment"
+                    },
+                    new String[] {                          // include classes
+                        "android.*", // for android.R
+                        "android.util.*",
+                        "com.android.internal.util.*",
+                        "android.view.*",
+                        "android.widget.*",
+                        "com.android.internal.widget.*",
+                        "android.text.**",
+                        "android.graphics.*",
+                        "android.graphics.drawable.*",
+                        "android.content.*",
+                        "android.content.res.*",
+                        "org.apache.harmony.xml.*",
+                        "com.android.internal.R**",
+                        "android.pim.*", // for datepicker
+                        "android.os.*",  // for android.os.Handler
+                        "android.database.ContentObserver", // for Digital clock
+                        });
+            aa.analyze();
+            agen.generate();
+
+            // Throw an error if any class failed to get renamed by the generator
+            //
+            // IMPORTANT: if you're building the platform and you get this error message,
+            // it means the renameClasses[] array in AsmGenerator needs to be updated: some
+            // class should have been renamed but it was not found in the input JAR files.
+            Set<String> notRenamed = agen.getClassesNotRenamed();
+            if (notRenamed.size() > 0) {
+                // (80-column guide below for error formatting)
+                // 01234567890123456789012345678901234567890123456789012345678901234567890123456789
+                log.error(
+                  "ERROR when running layoutlib_create: the following classes are referenced\n" +
+                  "by tools/layoutlib/create but were not actually found in the input JAR files.\n" +
+                  "This may be due to some platform classes having been renamed.");
+                for (String fqcn : notRenamed) {
+                    log.error("- Class not found: %s", fqcn.replace('/', '.'));
+                }
+                for (String path : osJarPath) {
+                    log.info("- Input JAR : %1$s", path);
+                }
+                return 1;
+            }
+
+            return 0;
+        } catch (IOException e) {
+            log.exception(e, "Failed to load jar");
+        } catch (LogAbortException e) {
+            e.error(log);
+        }
+
+        return 1;
+    }
+
+    private static int listDeps(ArrayList<String> osJarPath, Log log) {
+        DependencyFinder df = new DependencyFinder(log);
+        try {
+            List<Map<String, Set<String>>> result = df.findDeps(osJarPath);
+            if (sOptions.listAllDeps) {
+                df.printAllDeps(result);
+            } else if (sOptions.listOnlyMissingDeps) {
+                df.printMissingDeps(result);
+            }
+        } catch (IOException e) {
+            log.exception(e, "Failed to load jar");
+        }
+
+        return 0;
+    }
+
+    /**
+     * Returns true if args where properly parsed.
+     * Returns false if program should exit with command-line usage.
+     * <p/>
+     * Note: the String[0] is an output parameter wrapped in an array, since there is no
+     * "out" parameter support.
+     */
+    private static boolean processArgs(Log log, String[] args,
+            ArrayList<String> osJarPath, String[] osDestJar) {
+        boolean needs_dest = true;
+        for (int i = 0; i < args.length; i++) {
+            String s = args[i];
+            if (s.equals("-v")) {
+                log.setVerbose(true);
+            } else if (s.equals("-p")) {
+                sOptions.generatePublicAccess = false;
+            } else if (s.equals("--list-deps")) {
+                sOptions.listAllDeps = true;
+                needs_dest = false;
+            } else if (s.equals("--missing-deps")) {
+                sOptions.listOnlyMissingDeps = true;
+                needs_dest = false;
+            } else if (!s.startsWith("-")) {
+                if (needs_dest && osDestJar[0] == null) {
+                    osDestJar[0] = s;
+                } else {
+                    osJarPath.add(s);
+                }
+            } else {
+                log.error("Unknow argument: %s", s);
+                return false;
+            }
+        }
+
+        if (osJarPath.isEmpty()) {
+            log.error("Missing parameter: path to input jar");
+            return false;
+        }
+        if (needs_dest && osDestJar[0] == null) {
+            log.error("Missing parameter: path to output jar");
+            return false;
+        }
+
+        sOptions.generatePublicAccess = false;
+
+        return true;
+    }
+}
diff --git a/tools/layoutlib/create/src/com/android/tools/layoutlib/create/MethodAdapter.java b/tools/layoutlib/create/src/com/android/tools/layoutlib/create/MethodAdapter.java
new file mode 100644
index 0000000..7d1e4cf
--- /dev/null
+++ b/tools/layoutlib/create/src/com/android/tools/layoutlib/create/MethodAdapter.java
@@ -0,0 +1,97 @@
+/*
+ * Copyright (C) 2008 The Android Open Source Project
+ *
+ * Licensed under the Apache License, Version 2.0 (the "License");
+ * you may not use this file except in compliance with the License.
+ * You may obtain a copy of the License at
+ *
+ *      http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing, software
+ * distributed under the License is distributed on an "AS IS" BASIS,
+ * WITHOUT WARRANTIES 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.tools.layoutlib.create;
+
+
+/**
+ * An adapter to make it easier to use {@link MethodListener}.
+ * <p/>
+ * The adapter calls the void {@link #onInvokeV(String, boolean, Object)} listener
+ * for all types (I, L, F, D and A), returning 0 or null as appropriate.
+ */
+public class MethodAdapter implements MethodListener {
+    /**
+     * A stub method is being invoked.
+     * <p/>
+     * Known limitation: caller arguments are not available.
+     *
+     * @param signature The signature of the method being invoked, composed of the
+     *                  binary class name followed by the method descriptor (aka argument
+     *                  types). Example: "com/foo/MyClass/InnerClass/printInt(I)V".
+     * @param isNative True if the method was a native method.
+     * @param caller The calling object. Null for static methods, "this" for instance methods.
+     */
+    @Override
+    public void onInvokeV(String signature, boolean isNative, Object caller) {
+    }
+
+    /**
+     * Same as {@link #onInvokeV(String, boolean, Object)} but returns an integer or similar.
+     * @see #onInvokeV(String, boolean, Object)
+     * @return an integer, or a boolean, or a short or a byte.
+     */
+    @Override
+    public int onInvokeI(String signature, boolean isNative, Object caller) {
+        onInvokeV(signature, isNative, caller);
+        return 0;
+    }
+
+    /**
+     * Same as {@link #onInvokeV(String, boolean, Object)} but returns a long.
+     * @see #onInvokeV(String, boolean, Object)
+     * @return a long.
+     */
+    @Override
+    public long onInvokeL(String signature, boolean isNative, Object caller) {
+        onInvokeV(signature, isNative, caller);
+        return 0;
+    }
+
+    /**
+     * Same as {@link #onInvokeV(String, boolean, Object)} but returns a float.
+     * @see #onInvokeV(String, boolean, Object)
+     * @return a float.
+     */
+    @Override
+    public float onInvokeF(String signature, boolean isNative, Object caller) {
+        onInvokeV(signature, isNative, caller);
+        return 0;
+    }
+
+    /**
+     * Same as {@link #onInvokeV(String, boolean, Object)} but returns a double.
+     * @see #onInvokeV(String, boolean, Object)
+     * @return a double.
+     */
+    @Override
+    public double onInvokeD(String signature, boolean isNative, Object caller) {
+        onInvokeV(signature, isNative, caller);
+        return 0;
+    }
+
+    /**
+     * Same as {@link #onInvokeV(String, boolean, Object)} but returns an object.
+     * @see #onInvokeV(String, boolean, Object)
+     * @return an object.
+     */
+    @Override
+    public Object onInvokeA(String signature, boolean isNative, Object caller) {
+        onInvokeV(signature, isNative, caller);
+        return null;
+    }
+}
+
diff --git a/tools/layoutlib/create/src/com/android/tools/layoutlib/create/MethodListener.java b/tools/layoutlib/create/src/com/android/tools/layoutlib/create/MethodListener.java
new file mode 100644
index 0000000..6fc2b24
--- /dev/null
+++ b/tools/layoutlib/create/src/com/android/tools/layoutlib/create/MethodListener.java
@@ -0,0 +1,76 @@
+/*
+ * Copyright (C) 2008 The Android Open Source Project
+ *
+ * Licensed under the Apache License, Version 2.0 (the "License");
+ * you may not use this file except in compliance with the License.
+ * You may obtain a copy of the License at
+ *
+ *      http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing, software
+ * distributed under the License is distributed on an "AS IS" BASIS,
+ * WITHOUT WARRANTIES 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.tools.layoutlib.create;
+
+
+/**
+ * Interface to allow a method invocation to be listened upon.
+ * <p/>
+ * This is used by {@link OverrideMethod} to register a listener for methods that
+ * have been stubbed by the {@link AsmGenerator}. At runtime the stub will call either a
+ * default global listener or a specific listener based on the method signature.
+ */
+public interface MethodListener {
+    /**
+     * A stub method is being invoked.
+     * <p/>
+     * Known limitation: caller arguments are not available.
+     *  
+     * @param signature The signature of the method being invoked, composed of the
+     *                  binary class name followed by the method descriptor (aka argument
+     *                  types). Example: "com/foo/MyClass/InnerClass/printInt(I)V".
+     * @param isNative True if the method was a native method.
+     * @param caller The calling object. Null for static methods, "this" for instance methods.
+     */
+    public void onInvokeV(String signature, boolean isNative, Object caller);
+
+    /**
+     * Same as {@link #onInvokeV(String, boolean, Object)} but returns an integer or similar.
+     * @see #onInvokeV(String, boolean, Object)
+     * @return an integer, or a boolean, or a short or a byte.
+     */
+    public int onInvokeI(String signature, boolean isNative, Object caller);
+
+    /**
+     * Same as {@link #onInvokeV(String, boolean, Object)} but returns a long.
+     * @see #onInvokeV(String, boolean, Object)
+     * @return a long.
+     */
+    public long onInvokeL(String signature, boolean isNative, Object caller);
+
+    /**
+     * Same as {@link #onInvokeV(String, boolean, Object)} but returns a float.
+     * @see #onInvokeV(String, boolean, Object)
+     * @return a float.
+     */
+    public float onInvokeF(String signature, boolean isNative, Object caller);
+
+    /**
+     * Same as {@link #onInvokeV(String, boolean, Object)} but returns a double.
+     * @see #onInvokeV(String, boolean, Object)
+     * @return a double.
+     */
+    public double onInvokeD(String signature, boolean isNative, Object caller);
+
+    /**
+     * Same as {@link #onInvokeV(String, boolean, Object)} but returns an object.
+     * @see #onInvokeV(String, boolean, Object)
+     * @return an object.
+     */
+    public Object onInvokeA(String signature, boolean isNative, Object caller);
+}
+    
diff --git a/tools/layoutlib/create/src/com/android/tools/layoutlib/create/OverrideMethod.java b/tools/layoutlib/create/src/com/android/tools/layoutlib/create/OverrideMethod.java
new file mode 100644
index 0000000..a6aff99
--- /dev/null
+++ b/tools/layoutlib/create/src/com/android/tools/layoutlib/create/OverrideMethod.java
@@ -0,0 +1,151 @@
+/*
+ * Copyright (C) 2008 The Android Open Source Project
+ *
+ * Licensed under the Apache License, Version 2.0 (the "License");
+ * you may not use this file except in compliance with the License.
+ * You may obtain a copy of the License at
+ *
+ *      http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing, software
+ * distributed under the License is distributed on an "AS IS" BASIS,
+ * WITHOUT WARRANTIES 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.tools.layoutlib.create;
+
+import java.util.HashMap;
+
+/**
+ * Allows stub methods from LayoutLib to be overriden at runtime.
+ * <p/>
+ * Implementation note: all types required by this class(inner/outer classes & interfaces)
+ * must be referenced by the injectClass argument to {@link AsmGenerator} in Main.java;
+ * Otherwise they won't be accessible in layoutlib.jar at runtime.
+ */
+public final class OverrideMethod {
+
+    /** Map of method overridden. */
+    private static HashMap<String, MethodListener> sMethods = new HashMap<String, MethodListener>();
+    /** Default listener for all method not listed in sMethods. Nothing if null. */
+    private static MethodListener sDefaultListener = null;
+    
+    /**
+     * Sets the default listener for all methods not specifically handled.
+     * Null means to do nothing.
+     */
+    public static void setDefaultListener(MethodListener listener) {
+        sDefaultListener = listener;
+    }
+
+    /**
+     * Defines or reset a listener for the given method signature.
+     * 
+     * @param signature The signature of the method being invoked, composed of the
+     *                  binary class name followed by the method descriptor (aka argument
+     *                  types). Example: "com/foo/MyClass/InnerClass/printInt(I)V"
+     * @param listener The new listener. Removes it if null.
+     */
+    public static void setMethodListener(String signature, MethodListener listener) {
+        if (listener == null) {
+            sMethods.remove(signature);
+        } else {
+            sMethods.put(signature, listener);
+        }
+    }
+    
+    /**
+     * Invokes the specific listener for the given signature or the default one if defined.
+     * <p/>
+     * This version invokes the method listener for the void return type. 
+     * <p/>
+     * Note: this is not intended to be used by the LayoutLib Bridge. It is intended to be called
+     * by the stubbed methods generated by the LayoutLib_create tool.
+     * 
+     * @param signature The signature of the method being invoked, composed of the
+     *                  binary class name followed by the method descriptor (aka argument
+     *                  types). Example: "com/foo/MyClass/InnerClass/printInt(I)V".
+     * @param isNative True if the method was a native method.
+     * @param caller The calling object. Null for static methods, "this" for instance methods.
+     */
+    public static void invokeV(String signature, boolean isNative, Object caller) {
+        MethodListener i = sMethods.get(signature);
+        if (i != null) {
+            i.onInvokeV(signature, isNative, caller);
+        } else if (sDefaultListener != null) {
+            sDefaultListener.onInvokeV(signature, isNative, caller);
+        }
+    }
+    
+    /**
+     * Invokes the specific listener for the int return type.
+     * @see #invokeV(String, boolean, Object)
+     */
+    public static int invokeI(String signature, boolean isNative, Object caller) {
+        MethodListener i = sMethods.get(signature);
+        if (i != null) {
+            return i.onInvokeI(signature, isNative, caller);
+        } else if (sDefaultListener != null) {
+            return sDefaultListener.onInvokeI(signature, isNative, caller);
+        }
+        return 0;
+    }
+    
+    /**
+     * Invokes the specific listener for the long return type.
+     * @see #invokeV(String, boolean, Object)
+     */
+    public static long invokeL(String signature, boolean isNative, Object caller) {
+        MethodListener i = sMethods.get(signature);
+        if (i != null) {
+            return i.onInvokeL(signature, isNative, caller);
+        } else if (sDefaultListener != null) {
+            return sDefaultListener.onInvokeL(signature, isNative, caller);
+        }
+        return 0;
+    }
+    
+    /**
+     * Invokes the specific listener for the float return type.
+     * @see #invokeV(String, boolean, Object)
+     */
+    public static float invokeF(String signature, boolean isNative, Object caller) {
+        MethodListener i = sMethods.get(signature);
+        if (i != null) {
+            return i.onInvokeF(signature, isNative, caller);
+        } else if (sDefaultListener != null) {
+            return sDefaultListener.onInvokeF(signature, isNative, caller);
+        }
+        return 0;
+    }
+    
+    /**
+     * Invokes the specific listener for the double return type.
+     * @see #invokeV(String, boolean, Object)
+     */
+    public static double invokeD(String signature, boolean isNative, Object caller) {
+        MethodListener i = sMethods.get(signature);
+        if (i != null) {
+            return i.onInvokeD(signature, isNative, caller);
+        } else if (sDefaultListener != null) {
+            return sDefaultListener.onInvokeD(signature, isNative, caller);
+        }
+        return 0;
+    }
+    
+    /**
+     * Invokes the specific listener for the object return type.
+     * @see #invokeV(String, boolean, Object)
+     */
+    public static Object invokeA(String signature, boolean isNative, Object caller) {
+        MethodListener i = sMethods.get(signature);
+        if (i != null) {
+            return i.onInvokeA(signature, isNative, caller);
+        } else if (sDefaultListener != null) {
+            return sDefaultListener.onInvokeA(signature, isNative, caller);
+        }
+        return null;
+    }
+}
diff --git a/tools/layoutlib/create/src/com/android/tools/layoutlib/create/RenameClassAdapter.java b/tools/layoutlib/create/src/com/android/tools/layoutlib/create/RenameClassAdapter.java
new file mode 100644
index 0000000..383cbb8
--- /dev/null
+++ b/tools/layoutlib/create/src/com/android/tools/layoutlib/create/RenameClassAdapter.java
@@ -0,0 +1,463 @@
+/*
+ * Copyright (C) 2008 The Android Open Source Project
+ *
+ * Licensed under the Apache License, Version 2.0 (the "License");
+ * you may not use this file except in compliance with the License.
+ * You may obtain a copy of the License at
+ *
+ *      http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing, software
+ * distributed under the License is distributed on an "AS IS" BASIS,
+ * WITHOUT WARRANTIES 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.tools.layoutlib.create;
+
+import org.objectweb.asm.AnnotationVisitor;
+import org.objectweb.asm.ClassVisitor;
+import org.objectweb.asm.ClassWriter;
+import org.objectweb.asm.FieldVisitor;
+import org.objectweb.asm.Label;
+import org.objectweb.asm.MethodVisitor;
+import org.objectweb.asm.Opcodes;
+import org.objectweb.asm.Type;
+import org.objectweb.asm.signature.SignatureReader;
+import org.objectweb.asm.signature.SignatureVisitor;
+import org.objectweb.asm.signature.SignatureWriter;
+
+/**
+ * This class visitor renames a class from a given old name to a given new name.
+ * The class visitor will also rename all inner classes and references in the methods.
+ * <p/>
+ *
+ * For inner classes, this handles only the case where the outer class name changes.
+ * The inner class name should remain the same.
+ */
+public class RenameClassAdapter extends ClassVisitor {
+
+
+    private final String mOldName;
+    private final String mNewName;
+    private String mOldBase;
+    private String mNewBase;
+
+    /**
+     * Creates a class visitor that renames a class from a given old name to a given new name.
+     * The class visitor will also rename all inner classes and references in the methods.
+     * The names must be full qualified internal ASM names (e.g. com/blah/MyClass$InnerClass).
+     */
+    public RenameClassAdapter(ClassWriter cv, String oldName, String newName) {
+        super(Opcodes.ASM4, cv);
+        mOldBase = mOldName = oldName;
+        mNewBase = mNewName = newName;
+
+        int pos = mOldName.indexOf('$');
+        if (pos > 0) {
+            mOldBase = mOldName.substring(0, pos);
+        }
+        pos = mNewName.indexOf('$');
+        if (pos > 0) {
+            mNewBase = mNewName.substring(0, pos);
+        }
+
+        assert (mOldBase == null && mNewBase == null) || (mOldBase != null && mNewBase != null);
+    }
+
+
+    /**
+     * Renames a type descriptor, e.g. "Lcom.package.MyClass;"
+     * If the type doesn't need to be renamed, returns the input string as-is.
+     */
+    String renameTypeDesc(String desc) {
+        if (desc == null) {
+            return null;
+        }
+
+        return renameType(Type.getType(desc));
+    }
+
+    /**
+     * Renames an object type, e.g. "Lcom.package.MyClass;" or an array type that has an
+     * object element, e.g. "[Lcom.package.MyClass;"
+     * If the type doesn't need to be renamed, returns the internal name of the input type.
+     */
+    String renameType(Type type) {
+        if (type == null) {
+            return null;
+        }
+
+        if (type.getSort() == Type.OBJECT) {
+            String in = type.getInternalName();
+            return "L" + renameInternalType(in) + ";";
+        } else if (type.getSort() == Type.ARRAY) {
+            StringBuilder sb = new StringBuilder();
+            for (int n = type.getDimensions(); n > 0; n--) {
+                sb.append('[');
+            }
+            sb.append(renameType(type.getElementType()));
+            return sb.toString();
+        }
+        return type.getDescriptor();
+    }
+
+    /**
+     * Renames an object type, e.g. "Lcom.package.MyClass;" or an array type that has an
+     * object element, e.g. "[Lcom.package.MyClass;".
+     * This is like renameType() except that it returns a Type object.
+     * If the type doesn't need to be renamed, returns the input type object.
+     */
+    Type renameTypeAsType(Type type) {
+        if (type == null) {
+            return null;
+        }
+
+        if (type.getSort() == Type.OBJECT) {
+            String in = type.getInternalName();
+            String newIn = renameInternalType(in);
+            if (newIn != in) {
+                return Type.getType("L" + newIn + ";");
+            }
+        } else if (type.getSort() == Type.ARRAY) {
+            StringBuilder sb = new StringBuilder();
+            for (int n = type.getDimensions(); n > 0; n--) {
+                sb.append('[');
+            }
+            sb.append(renameType(type.getElementType()));
+            return Type.getType(sb.toString());
+        }
+        return type;
+    }
+
+    /**
+     * Renames an internal type name, e.g. "com.package.MyClass".
+     * If the type doesn't need to be renamed, returns the input string as-is.
+     * <p/>
+     * The internal type of some of the MethodVisitor turns out to be a type
+       descriptor sometimes so descriptors are renamed too.
+     */
+    String renameInternalType(String type) {
+        if (type == null) {
+            return null;
+        }
+
+        if (type.equals(mOldName)) {
+            return mNewName;
+        }
+
+        if (mOldBase != mOldName && type.equals(mOldBase)) {
+            return mNewBase;
+        }
+
+        int pos = type.indexOf('$');
+        if (pos == mOldBase.length() && type.startsWith(mOldBase)) {
+            return mNewBase + type.substring(pos);
+        }
+
+        // The internal type of some of the MethodVisitor turns out to be a type
+        // descriptor sometimes. This is the case with visitTypeInsn(type) and
+        // visitMethodInsn(owner). We try to detect it and adjust it here.
+        if (type.indexOf(';') > 0) {
+            type = renameTypeDesc(type);
+        }
+
+        return type;
+    }
+
+    /**
+     * Renames a method descriptor, i.e. applies renameType to all arguments and to the
+     * return value.
+     */
+    String renameMethodDesc(String desc) {
+        if (desc == null) {
+            return null;
+        }
+
+        Type[] args = Type.getArgumentTypes(desc);
+
+        StringBuilder sb = new StringBuilder("(");
+        for (Type arg : args) {
+            String name = renameType(arg);
+            sb.append(name);
+        }
+        sb.append(')');
+
+        Type ret = Type.getReturnType(desc);
+        String name = renameType(ret);
+        sb.append(name);
+
+        return sb.toString();
+    }
+
+
+    /**
+     * Renames the ClassSignature handled by ClassVisitor.visit
+     * or the MethodTypeSignature handled by ClassVisitor.visitMethod.
+     */
+    String renameTypeSignature(String sig) {
+        if (sig == null) {
+            return null;
+        }
+        SignatureReader reader = new SignatureReader(sig);
+        SignatureWriter writer = new SignatureWriter();
+        reader.accept(new RenameSignatureAdapter(writer));
+        sig = writer.toString();
+        return sig;
+    }
+
+
+    /**
+     * Renames the FieldTypeSignature handled by ClassVisitor.visitField
+     * or MethodVisitor.visitLocalVariable.
+     */
+    String renameFieldSignature(String sig) {
+        if (sig == null) {
+            return null;
+        }
+        SignatureReader reader = new SignatureReader(sig);
+        SignatureWriter writer = new SignatureWriter();
+        reader.acceptType(new RenameSignatureAdapter(writer));
+        sig = writer.toString();
+        return sig;
+    }
+
+
+    //----------------------------------
+    // Methods from the ClassAdapter
+
+    @Override
+    public void visit(int version, int access, String name, String signature,
+            String superName, String[] interfaces) {
+        name = renameInternalType(name);
+        superName = renameInternalType(superName);
+        signature = renameTypeSignature(signature);
+
+        super.visit(version, access, name, signature, superName, interfaces);
+    }
+
+    @Override
+    public void visitInnerClass(String name, String outerName, String innerName, int access) {
+        assert outerName.equals(mOldName);
+        outerName = renameInternalType(outerName);
+        name = outerName + "$" + innerName;
+        super.visitInnerClass(name, outerName, innerName, access);
+    }
+
+    @Override
+    public MethodVisitor visitMethod(int access, String name, String desc,
+            String signature, String[] exceptions) {
+        desc = renameMethodDesc(desc);
+        signature = renameTypeSignature(signature);
+        MethodVisitor mw = super.visitMethod(access, name, desc, signature, exceptions);
+        return new RenameMethodAdapter(mw);
+    }
+
+    @Override
+    public AnnotationVisitor visitAnnotation(String desc, boolean visible) {
+        desc = renameTypeDesc(desc);
+        return super.visitAnnotation(desc, visible);
+    }
+
+    @Override
+    public FieldVisitor visitField(int access, String name, String desc,
+            String signature, Object value) {
+        desc = renameTypeDesc(desc);
+        signature = renameFieldSignature(signature);
+        return super.visitField(access, name, desc, signature, value);
+    }
+
+
+    //----------------------------------
+
+    /**
+     * A method visitor that renames all references from an old class name to a new class name.
+     */
+    public class RenameMethodAdapter extends MethodVisitor {
+
+        /**
+         * Creates a method visitor that renames all references from a given old name to a given new
+         * name. The method visitor will also rename all inner classes.
+         * The names must be full qualified internal ASM names (e.g. com/blah/MyClass$InnerClass).
+         */
+        public RenameMethodAdapter(MethodVisitor mv) {
+            super(Opcodes.ASM4, mv);
+        }
+
+        @Override
+        public AnnotationVisitor visitAnnotation(String desc, boolean visible) {
+            desc = renameTypeDesc(desc);
+
+            return super.visitAnnotation(desc, visible);
+        }
+
+        @Override
+        public AnnotationVisitor visitParameterAnnotation(int parameter, String desc, boolean visible) {
+            desc = renameTypeDesc(desc);
+
+            return super.visitParameterAnnotation(parameter, desc, visible);
+        }
+
+        @Override
+        public void visitTypeInsn(int opcode, String type) {
+            type = renameInternalType(type);
+
+            super.visitTypeInsn(opcode, type);
+        }
+
+        @Override
+        public void visitFieldInsn(int opcode, String owner, String name, String desc) {
+            owner = renameInternalType(owner);
+            desc = renameTypeDesc(desc);
+
+            super.visitFieldInsn(opcode, owner, name, desc);
+        }
+
+        @Override
+        public void visitMethodInsn(int opcode, String owner, String name, String desc) {
+            owner = renameInternalType(owner);
+            desc = renameMethodDesc(desc);
+
+            super.visitMethodInsn(opcode, owner, name, desc);
+        }
+
+        @Override
+        public void visitLdcInsn(Object cst) {
+            // If cst is a Type, this means the code is trying to pull the .class constant
+            // for this class, so it needs to be renamed too.
+            if (cst instanceof Type) {
+                cst = renameTypeAsType((Type) cst);
+            }
+            super.visitLdcInsn(cst);
+        }
+
+        @Override
+        public void visitMultiANewArrayInsn(String desc, int dims) {
+            desc = renameTypeDesc(desc);
+
+            super.visitMultiANewArrayInsn(desc, dims);
+        }
+
+        @Override
+        public void visitTryCatchBlock(Label start, Label end, Label handler, String type) {
+            type = renameInternalType(type);
+
+            super.visitTryCatchBlock(start, end, handler, type);
+        }
+
+        @Override
+        public void visitLocalVariable(String name, String desc, String signature,
+                Label start, Label end, int index) {
+            desc = renameTypeDesc(desc);
+            signature = renameFieldSignature(signature);
+
+            super.visitLocalVariable(name, desc, signature, start, end, index);
+        }
+
+    }
+
+    //----------------------------------
+
+    public class RenameSignatureAdapter extends SignatureVisitor {
+
+        private final SignatureVisitor mSv;
+
+        public RenameSignatureAdapter(SignatureVisitor sv) {
+            super(Opcodes.ASM4);
+            mSv = sv;
+        }
+
+        @Override
+        public void visitClassType(String name) {
+            name = renameInternalType(name);
+            mSv.visitClassType(name);
+        }
+
+        @Override
+        public void visitInnerClassType(String name) {
+            name = renameInternalType(name);
+            mSv.visitInnerClassType(name);
+        }
+
+        @Override
+        public SignatureVisitor visitArrayType() {
+            SignatureVisitor sv = mSv.visitArrayType();
+            return new RenameSignatureAdapter(sv);
+        }
+
+        @Override
+        public void visitBaseType(char descriptor) {
+            mSv.visitBaseType(descriptor);
+        }
+
+        @Override
+        public SignatureVisitor visitClassBound() {
+            SignatureVisitor sv = mSv.visitClassBound();
+            return new RenameSignatureAdapter(sv);
+        }
+
+        @Override
+        public void visitEnd() {
+            mSv.visitEnd();
+        }
+
+        @Override
+        public SignatureVisitor visitExceptionType() {
+            SignatureVisitor sv = mSv.visitExceptionType();
+            return new RenameSignatureAdapter(sv);
+        }
+
+        @Override
+        public void visitFormalTypeParameter(String name) {
+            mSv.visitFormalTypeParameter(name);
+        }
+
+        @Override
+        public SignatureVisitor visitInterface() {
+            SignatureVisitor sv = mSv.visitInterface();
+            return new RenameSignatureAdapter(sv);
+        }
+
+        @Override
+        public SignatureVisitor visitInterfaceBound() {
+            SignatureVisitor sv = mSv.visitInterfaceBound();
+            return new RenameSignatureAdapter(sv);
+        }
+
+        @Override
+        public SignatureVisitor visitParameterType() {
+            SignatureVisitor sv = mSv.visitParameterType();
+            return new RenameSignatureAdapter(sv);
+        }
+
+        @Override
+        public SignatureVisitor visitReturnType() {
+            SignatureVisitor sv = mSv.visitReturnType();
+            return new RenameSignatureAdapter(sv);
+        }
+
+        @Override
+        public SignatureVisitor visitSuperclass() {
+            SignatureVisitor sv = mSv.visitSuperclass();
+            return new RenameSignatureAdapter(sv);
+        }
+
+        @Override
+        public void visitTypeArgument() {
+            mSv.visitTypeArgument();
+        }
+
+        @Override
+        public SignatureVisitor visitTypeArgument(char wildcard) {
+            SignatureVisitor sv = mSv.visitTypeArgument(wildcard);
+            return new RenameSignatureAdapter(sv);
+        }
+
+        @Override
+        public void visitTypeVariable(String name) {
+            mSv.visitTypeVariable(name);
+        }
+
+    }
+}
diff --git a/tools/layoutlib/create/src/com/android/tools/layoutlib/create/StubMethodAdapter.java b/tools/layoutlib/create/src/com/android/tools/layoutlib/create/StubMethodAdapter.java
new file mode 100644
index 0000000..51e7535
--- /dev/null
+++ b/tools/layoutlib/create/src/com/android/tools/layoutlib/create/StubMethodAdapter.java
@@ -0,0 +1,376 @@
+/*
+ * Copyright (C) 2008 The Android Open Source Project
+ *
+ * Licensed under the Apache License, Version 2.0 (the "License");
+ * you may not use this file except in compliance with the License.
+ * You may obtain a copy of the License at
+ *
+ *      http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing, software
+ * distributed under the License is distributed on an "AS IS" BASIS,
+ * WITHOUT WARRANTIES 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.tools.layoutlib.create;
+
+import org.objectweb.asm.AnnotationVisitor;
+import org.objectweb.asm.Attribute;
+import org.objectweb.asm.Label;
+import org.objectweb.asm.MethodVisitor;
+import org.objectweb.asm.Opcodes;
+import org.objectweb.asm.Type;
+
+/**
+ * This method adapter rewrites a method by discarding the original code and generating
+ * a stub depending on the return type. Original annotations are passed along unchanged.
+ */
+class StubMethodAdapter extends MethodVisitor {
+
+    private static String CONSTRUCTOR = "<init>";
+    private static String CLASS_INIT = "<clinit>";
+
+    /** The parent method writer */
+    private MethodVisitor mParentVisitor;
+    /** The method return type. Can be null. */
+    private Type mReturnType;
+    /** Message to be printed by stub methods. */
+    private String mInvokeSignature;
+    /** Flag to output the first line number. */
+    private boolean mOutputFirstLineNumber = true;
+    /** Flag that is true when implementing a constructor, to accept all original
+     *  code calling the original super constructor. */
+    private boolean mIsInitMethod = false;
+
+    private boolean mMessageGenerated;
+    private final boolean mIsStatic;
+    private final boolean mIsNative;
+
+    public StubMethodAdapter(MethodVisitor mv, String methodName, Type returnType,
+            String invokeSignature, boolean isStatic, boolean isNative) {
+        super(Opcodes.ASM4);
+        mParentVisitor = mv;
+        mReturnType = returnType;
+        mInvokeSignature = invokeSignature;
+        mIsStatic = isStatic;
+        mIsNative = isNative;
+
+        if (CONSTRUCTOR.equals(methodName) || CLASS_INIT.equals(methodName)) {
+            mIsInitMethod = true;
+        }
+    }
+
+    private void generateInvoke() {
+        /* Generates the code:
+         *  OverrideMethod.invoke("signature", mIsNative ? true : false, null or this);
+         */
+        mParentVisitor.visitLdcInsn(mInvokeSignature);
+        // push true or false
+        mParentVisitor.visitInsn(mIsNative ? Opcodes.ICONST_1 : Opcodes.ICONST_0);
+        // push null or this
+        if (mIsStatic) {
+            mParentVisitor.visitInsn(Opcodes.ACONST_NULL);
+        } else {
+            mParentVisitor.visitVarInsn(Opcodes.ALOAD, 0);
+        }
+
+        int sort = mReturnType != null ? mReturnType.getSort() : Type.VOID;
+        switch(sort) {
+        case Type.VOID:
+            mParentVisitor.visitMethodInsn(Opcodes.INVOKESTATIC,
+                    "com/android/tools/layoutlib/create/OverrideMethod",
+                    "invokeV",
+                    "(Ljava/lang/String;ZLjava/lang/Object;)V");
+            mParentVisitor.visitInsn(Opcodes.RETURN);
+            break;
+        case Type.BOOLEAN:
+        case Type.CHAR:
+        case Type.BYTE:
+        case Type.SHORT:
+        case Type.INT:
+            mParentVisitor.visitMethodInsn(Opcodes.INVOKESTATIC,
+                    "com/android/tools/layoutlib/create/OverrideMethod",
+                    "invokeI",
+                    "(Ljava/lang/String;ZLjava/lang/Object;)I");
+            switch(sort) {
+            case Type.BOOLEAN:
+                Label l1 = new Label();
+                mParentVisitor.visitJumpInsn(Opcodes.IFEQ, l1);
+                mParentVisitor.visitInsn(Opcodes.ICONST_1);
+                mParentVisitor.visitInsn(Opcodes.IRETURN);
+                mParentVisitor.visitLabel(l1);
+                mParentVisitor.visitInsn(Opcodes.ICONST_0);
+                break;
+            case Type.CHAR:
+                mParentVisitor.visitInsn(Opcodes.I2C);
+                break;
+            case Type.BYTE:
+                mParentVisitor.visitInsn(Opcodes.I2B);
+                break;
+            case Type.SHORT:
+                mParentVisitor.visitInsn(Opcodes.I2S);
+                break;
+            }
+            mParentVisitor.visitInsn(Opcodes.IRETURN);
+            break;
+        case Type.LONG:
+            mParentVisitor.visitMethodInsn(Opcodes.INVOKESTATIC,
+                    "com/android/tools/layoutlib/create/OverrideMethod",
+                    "invokeL",
+                    "(Ljava/lang/String;ZLjava/lang/Object;)J");
+            mParentVisitor.visitInsn(Opcodes.LRETURN);
+            break;
+        case Type.FLOAT:
+            mParentVisitor.visitMethodInsn(Opcodes.INVOKESTATIC,
+                    "com/android/tools/layoutlib/create/OverrideMethod",
+                    "invokeF",
+                    "(Ljava/lang/String;ZLjava/lang/Object;)F");
+            mParentVisitor.visitInsn(Opcodes.FRETURN);
+            break;
+        case Type.DOUBLE:
+            mParentVisitor.visitMethodInsn(Opcodes.INVOKESTATIC,
+                    "com/android/tools/layoutlib/create/OverrideMethod",
+                    "invokeD",
+                    "(Ljava/lang/String;ZLjava/lang/Object;)D");
+            mParentVisitor.visitInsn(Opcodes.DRETURN);
+            break;
+        case Type.ARRAY:
+        case Type.OBJECT:
+            mParentVisitor.visitMethodInsn(Opcodes.INVOKESTATIC,
+                    "com/android/tools/layoutlib/create/OverrideMethod",
+                    "invokeA",
+                    "(Ljava/lang/String;ZLjava/lang/Object;)Ljava/lang/Object;");
+            mParentVisitor.visitTypeInsn(Opcodes.CHECKCAST, mReturnType.getInternalName());
+            mParentVisitor.visitInsn(Opcodes.ARETURN);
+            break;
+        }
+
+    }
+
+    private void generatePop() {
+        /* Pops the stack, depending on the return type.
+         */
+        switch(mReturnType != null ? mReturnType.getSort() : Type.VOID) {
+        case Type.VOID:
+            break;
+        case Type.BOOLEAN:
+        case Type.CHAR:
+        case Type.BYTE:
+        case Type.SHORT:
+        case Type.INT:
+        case Type.FLOAT:
+        case Type.ARRAY:
+        case Type.OBJECT:
+            mParentVisitor.visitInsn(Opcodes.POP);
+            break;
+        case Type.LONG:
+        case Type.DOUBLE:
+            mParentVisitor.visitInsn(Opcodes.POP2);
+            break;
+        }
+    }
+
+    /* Pass down to visitor writer. In this implementation, either do nothing. */
+    @Override
+    public void visitCode() {
+        mParentVisitor.visitCode();
+    }
+
+    /*
+     * visitMaxs is called just before visitEnd if there was any code to rewrite.
+     * For non-constructor, generate the messaging code and the return statement
+     * if it hasn't been done before.
+     */
+    @Override
+    public void visitMaxs(int maxStack, int maxLocals) {
+        if (!mIsInitMethod && !mMessageGenerated) {
+            generateInvoke();
+            mMessageGenerated = true;
+        }
+        mParentVisitor.visitMaxs(maxStack, maxLocals);
+    }
+
+    /**
+     * End of visiting.
+     * For non-constructor, generate the messaging code and the return statement
+     * if it hasn't been done before.
+     */
+    @Override
+    public void visitEnd() {
+        if (!mIsInitMethod && !mMessageGenerated) {
+            generateInvoke();
+            mMessageGenerated = true;
+            mParentVisitor.visitMaxs(1, 1);
+        }
+        mParentVisitor.visitEnd();
+    }
+
+    /* Writes all annotation from the original method. */
+    @Override
+    public AnnotationVisitor visitAnnotation(String desc, boolean visible) {
+        return mParentVisitor.visitAnnotation(desc, visible);
+    }
+
+    /* Writes all annotation default values from the original method. */
+    @Override
+    public AnnotationVisitor visitAnnotationDefault() {
+        return mParentVisitor.visitAnnotationDefault();
+    }
+
+    @Override
+    public AnnotationVisitor visitParameterAnnotation(int parameter, String desc,
+            boolean visible) {
+        return mParentVisitor.visitParameterAnnotation(parameter, desc, visible);
+    }
+
+    /* Writes all attributes from the original method. */
+    @Override
+    public void visitAttribute(Attribute attr) {
+        mParentVisitor.visitAttribute(attr);
+    }
+
+    /*
+     * Only writes the first line number present in the original code so that source
+     * viewers can direct to the correct method, even if the content doesn't match.
+     */
+    @Override
+    public void visitLineNumber(int line, Label start) {
+        if (mIsInitMethod || mOutputFirstLineNumber) {
+            mParentVisitor.visitLineNumber(line, start);
+            mOutputFirstLineNumber = false;
+        }
+    }
+
+    /**
+     * For non-constructor, rewrite existing "return" instructions to write the message.
+     */
+    @Override
+    public void visitInsn(int opcode) {
+        if (mIsInitMethod) {
+            switch (opcode) {
+            case Opcodes.RETURN:
+            case Opcodes.ARETURN:
+            case Opcodes.DRETURN:
+            case Opcodes.FRETURN:
+            case Opcodes.IRETURN:
+            case Opcodes.LRETURN:
+                // Pop the last word from the stack since invoke will generate its own return.
+                generatePop();
+                generateInvoke();
+                mMessageGenerated = true;
+                //$FALL-THROUGH$
+            default:
+                mParentVisitor.visitInsn(opcode);
+            }
+        }
+    }
+
+    @Override
+    public void visitLabel(Label label) {
+        if (mIsInitMethod) {
+            mParentVisitor.visitLabel(label);
+        }
+    }
+
+    @Override
+    public void visitTryCatchBlock(Label start, Label end, Label handler, String type) {
+        if (mIsInitMethod) {
+            mParentVisitor.visitTryCatchBlock(start, end, handler, type);
+        }
+    }
+
+    @Override
+    public void visitMethodInsn(int opcode, String owner, String name, String desc) {
+        if (mIsInitMethod) {
+            mParentVisitor.visitMethodInsn(opcode, owner, name, desc);
+        }
+    }
+
+    @Override
+    public void visitFieldInsn(int opcode, String owner, String name, String desc) {
+        if (mIsInitMethod) {
+            mParentVisitor.visitFieldInsn(opcode, owner, name, desc);
+        }
+    }
+
+    @Override
+    public void visitFrame(int type, int nLocal, Object[] local, int nStack, Object[] stack) {
+        if (mIsInitMethod) {
+            mParentVisitor.visitFrame(type, nLocal, local, nStack, stack);
+        }
+    }
+
+    @Override
+    public void visitIincInsn(int var, int increment) {
+        if (mIsInitMethod) {
+            mParentVisitor.visitIincInsn(var, increment);
+        }
+    }
+
+    @Override
+    public void visitIntInsn(int opcode, int operand) {
+        if (mIsInitMethod) {
+            mParentVisitor.visitIntInsn(opcode, operand);
+        }
+    }
+
+    @Override
+    public void visitJumpInsn(int opcode, Label label) {
+        if (mIsInitMethod) {
+            mParentVisitor.visitJumpInsn(opcode, label);
+        }
+    }
+
+    @Override
+    public void visitLdcInsn(Object cst) {
+        if (mIsInitMethod) {
+            mParentVisitor.visitLdcInsn(cst);
+        }
+    }
+
+    @Override
+    public void visitLocalVariable(String name, String desc, String signature,
+            Label start, Label end, int index) {
+        if (mIsInitMethod) {
+            mParentVisitor.visitLocalVariable(name, desc, signature, start, end, index);
+        }
+    }
+
+    @Override
+    public void visitLookupSwitchInsn(Label dflt, int[] keys, Label[] labels) {
+        if (mIsInitMethod) {
+            mParentVisitor.visitLookupSwitchInsn(dflt, keys, labels);
+        }
+    }
+
+    @Override
+    public void visitMultiANewArrayInsn(String desc, int dims) {
+        if (mIsInitMethod) {
+            mParentVisitor.visitMultiANewArrayInsn(desc, dims);
+        }
+    }
+
+    @Override
+    public void visitTableSwitchInsn(int min, int max, Label dflt, Label[] labels) {
+        if (mIsInitMethod) {
+            mParentVisitor.visitTableSwitchInsn(min, max, dflt, labels);
+        }
+    }
+
+    @Override
+    public void visitTypeInsn(int opcode, String type) {
+        if (mIsInitMethod) {
+            mParentVisitor.visitTypeInsn(opcode, type);
+        }
+    }
+
+    @Override
+    public void visitVarInsn(int opcode, int var) {
+        if (mIsInitMethod) {
+            mParentVisitor.visitVarInsn(opcode, var);
+        }
+    }
+
+}
diff --git a/tools/layoutlib/create/src/com/android/tools/layoutlib/create/TransformClassAdapter.java b/tools/layoutlib/create/src/com/android/tools/layoutlib/create/TransformClassAdapter.java
new file mode 100644
index 0000000..d45a183
--- /dev/null
+++ b/tools/layoutlib/create/src/com/android/tools/layoutlib/create/TransformClassAdapter.java
@@ -0,0 +1,183 @@
+/*
+ * Copyright (C) 2008 The Android Open Source Project
+ *
+ * Licensed under the Apache License, Version 2.0 (the "License");
+ * you may not use this file except in compliance with the License.
+ * You may obtain a copy of the License at
+ *
+ *      http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing, software
+ * distributed under the License is distributed on an "AS IS" BASIS,
+ * WITHOUT WARRANTIES 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.tools.layoutlib.create;
+
+import org.objectweb.asm.ClassVisitor;
+import org.objectweb.asm.FieldVisitor;
+import org.objectweb.asm.MethodVisitor;
+import org.objectweb.asm.Opcodes;
+import org.objectweb.asm.Type;
+
+import java.util.Set;
+
+/**
+ * Class adapter that can stub some or all of the methods of the class.
+ */
+class TransformClassAdapter extends ClassVisitor {
+
+    /** True if all methods should be stubbed, false if only native ones must be stubbed. */
+    private final boolean mStubAll;
+    /** True if the class is an interface. */
+    private boolean mIsInterface;
+    private final String mClassName;
+    private final Log mLog;
+    private final Set<String> mStubMethods;
+    private Set<String> mDeleteReturns;
+
+    /**
+     * Creates a new class adapter that will stub some or all methods.
+     * @param logger
+     * @param stubMethods  list of method signatures to always stub out
+     * @param deleteReturns list of types that trigger the deletion of methods returning them.
+     * @param className The name of the class being modified
+     * @param cv The parent class writer visitor
+     * @param stubNativesOnly True if only native methods should be stubbed. False if all
+     *                        methods should be stubbed.
+     * @param hasNative True if the method has natives, in which case its access should be
+     *                  changed.
+     */
+    public TransformClassAdapter(Log logger, Set<String> stubMethods,
+            Set<String> deleteReturns, String className, ClassVisitor cv,
+            boolean stubNativesOnly, boolean hasNative) {
+        super(Opcodes.ASM4, cv);
+        mLog = logger;
+        mStubMethods = stubMethods;
+        mClassName = className;
+        mStubAll = !stubNativesOnly;
+        mIsInterface = false;
+        mDeleteReturns = deleteReturns;
+    }
+
+    /* Visits the class header. */
+    @Override
+    public void visit(int version, int access, String name,
+            String signature, String superName, String[] interfaces) {
+
+        // This class might be being renamed.
+        name = mClassName;
+
+        // remove protected or private and set as public
+        if (Main.sOptions.generatePublicAccess) {
+            access = access & ~(Opcodes.ACC_PRIVATE | Opcodes.ACC_PROTECTED);
+            access |= Opcodes.ACC_PUBLIC;
+        }
+        // remove final
+        access = access & ~Opcodes.ACC_FINAL;
+        // note: leave abstract classes as such
+        // don't try to implement stub for interfaces
+
+        mIsInterface = ((access & Opcodes.ACC_INTERFACE) != 0);
+        super.visit(version, access, name, signature, superName, interfaces);
+    }
+
+    /* Visits the header of an inner class. */
+    @Override
+    public void visitInnerClass(String name, String outerName, String innerName, int access) {
+        // remove protected or private and set as public
+        if (Main.sOptions.generatePublicAccess) {
+            access = access & ~(Opcodes.ACC_PRIVATE | Opcodes.ACC_PROTECTED);
+            access |= Opcodes.ACC_PUBLIC;
+        }
+        // remove final
+        access = access & ~Opcodes.ACC_FINAL;
+        // note: leave abstract classes as such
+        // don't try to implement stub for interfaces
+
+        super.visitInnerClass(name, outerName, innerName, access);
+    }
+
+    /* Visits a method. */
+    @Override
+    public MethodVisitor visitMethod(int access, String name, String desc,
+            String signature, String[] exceptions) {
+
+        if (mDeleteReturns != null) {
+            Type t = Type.getReturnType(desc);
+            if (t.getSort() == Type.OBJECT) {
+                String returnType = t.getInternalName();
+                if (returnType != null) {
+                    if (mDeleteReturns.contains(returnType)) {
+                        return null;
+                    }
+                }
+            }
+        }
+
+        String methodSignature = mClassName.replace('/', '.') + "#" + name;
+
+        // change access to public
+        if (Main.sOptions.generatePublicAccess) {
+            access &= ~(Opcodes.ACC_PROTECTED | Opcodes.ACC_PRIVATE);
+            access |= Opcodes.ACC_PUBLIC;
+        }
+
+        // remove final
+        access = access & ~Opcodes.ACC_FINAL;
+
+        // stub this method if they are all to be stubbed or if it is a native method
+        // and don't try to stub interfaces nor abstract non-native methods.
+        if (!mIsInterface &&
+            ((access & (Opcodes.ACC_ABSTRACT | Opcodes.ACC_NATIVE)) != Opcodes.ACC_ABSTRACT) &&
+            (mStubAll ||
+             (access & Opcodes.ACC_NATIVE) != 0) ||
+             mStubMethods.contains(methodSignature)) {
+
+            boolean isStatic = (access & Opcodes.ACC_STATIC) != 0;
+            boolean isNative = (access & Opcodes.ACC_NATIVE) != 0;
+
+            // remove abstract, final and native
+            access = access & ~(Opcodes.ACC_ABSTRACT | Opcodes.ACC_FINAL | Opcodes.ACC_NATIVE);
+
+            String invokeSignature = methodSignature + desc;
+            mLog.debug("  Stub: %s (%s)", invokeSignature, isNative ? "native" : "");
+
+            MethodVisitor mw = super.visitMethod(access, name, desc, signature, exceptions);
+            return new StubMethodAdapter(mw, name, returnType(desc), invokeSignature,
+                    isStatic, isNative);
+
+        } else {
+            mLog.debug("  Keep: %s %s", name, desc);
+            return super.visitMethod(access, name, desc, signature, exceptions);
+        }
+    }
+
+    /* Visits a field. Makes it public. */
+    @Override
+    public FieldVisitor visitField(int access, String name, String desc, String signature,
+            Object value) {
+        // change access to public
+        if (Main.sOptions.generatePublicAccess) {
+            access &= ~(Opcodes.ACC_PROTECTED | Opcodes.ACC_PRIVATE);
+            access |= Opcodes.ACC_PUBLIC;
+        }
+        return super.visitField(access, name, desc, signature, value);
+    }
+
+    /**
+     * Extracts the return {@link Type} of this descriptor.
+     */
+    Type returnType(String desc) {
+        if (desc != null) {
+            try {
+                return Type.getReturnType(desc);
+            } catch (ArrayIndexOutOfBoundsException e) {
+                // ignore, not a valid type.
+            }
+        }
+        return null;
+    }
+}
diff --git a/tools/layoutlib/create/tests/com/android/tools/layoutlib/create/AsmAnalyzerTest.java b/tools/layoutlib/create/tests/com/android/tools/layoutlib/create/AsmAnalyzerTest.java
new file mode 100644
index 0000000..d6dba6a
--- /dev/null
+++ b/tools/layoutlib/create/tests/com/android/tools/layoutlib/create/AsmAnalyzerTest.java
@@ -0,0 +1,227 @@
+/*
+ * Copyright (C) 2008 The Android Open Source Project
+ *
+ * Licensed under the Apache License, Version 2.0 (the "License");
+ * you may not use this file except in compliance with the License.
+ * You may obtain a copy of the License at
+ *
+ *      http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing, software
+ * distributed under the License is distributed on an "AS IS" BASIS,
+ * WITHOUT WARRANTIES 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.tools.layoutlib.create;
+
+import static org.junit.Assert.assertArrayEquals;
+import static org.junit.Assert.assertEquals;
+import static org.junit.Assert.assertNotNull;
+
+import com.android.tools.layoutlib.create.AsmAnalyzer.DependencyVisitor;
+
+import org.junit.After;
+import org.junit.Before;
+import org.junit.Test;
+import org.objectweb.asm.ClassReader;
+
+import java.io.IOException;
+import java.net.URL;
+import java.util.ArrayList;
+import java.util.Map;
+import java.util.TreeMap;
+
+/**
+ * Unit tests for some methods of {@link AsmAnalyzer}.
+ */
+public class AsmAnalyzerTest {
+
+    private MockLog mLog;
+    private ArrayList<String> mOsJarPath;
+    private AsmAnalyzer mAa;
+
+    @Before
+    public void setUp() throws Exception {
+        mLog = new MockLog();
+        URL url = this.getClass().getClassLoader().getResource("data/mock_android.jar");
+
+        mOsJarPath = new ArrayList<String>();
+        mOsJarPath.add(url.getFile());
+
+        mAa = new AsmAnalyzer(mLog, mOsJarPath, null /* gen */,
+                null /* deriveFrom */, null /* includeGlobs */ );
+    }
+
+    @After
+    public void tearDown() throws Exception {
+    }
+
+    @Test
+    public void testParseZip() throws IOException {
+        Map<String, ClassReader> map = mAa.parseZip(mOsJarPath);
+
+        assertArrayEquals(new String[] {
+                "mock_android.dummy.InnerTest",
+                "mock_android.dummy.InnerTest$DerivingClass",
+                "mock_android.dummy.InnerTest$MyGenerics1",
+                "mock_android.dummy.InnerTest$MyIntEnum",
+                "mock_android.dummy.InnerTest$MyStaticInnerClass",
+                "mock_android.dummy.InnerTest$NotStaticInner1",
+                "mock_android.dummy.InnerTest$NotStaticInner2",
+                "mock_android.view.View",
+                "mock_android.view.ViewGroup",
+                "mock_android.view.ViewGroup$LayoutParams",
+                "mock_android.view.ViewGroup$MarginLayoutParams",
+                "mock_android.widget.LinearLayout",
+                "mock_android.widget.LinearLayout$LayoutParams",
+                "mock_android.widget.TableLayout",
+                "mock_android.widget.TableLayout$LayoutParams"
+            },
+            map.keySet().toArray());
+    }
+
+    @Test
+    public void testFindClass() throws IOException, LogAbortException {
+        Map<String, ClassReader> zipClasses = mAa.parseZip(mOsJarPath);
+        TreeMap<String, ClassReader> found = new TreeMap<String, ClassReader>();
+
+        ClassReader cr = mAa.findClass("mock_android.view.ViewGroup$LayoutParams",
+                zipClasses, found);
+
+        assertNotNull(cr);
+        assertEquals("mock_android/view/ViewGroup$LayoutParams", cr.getClassName());
+        assertArrayEquals(new String[] { "mock_android.view.ViewGroup$LayoutParams" },
+                found.keySet().toArray());
+        assertArrayEquals(new ClassReader[] { cr }, found.values().toArray());
+    }
+
+    @Test
+    public void testFindGlobs() throws IOException, LogAbortException {
+        Map<String, ClassReader> zipClasses = mAa.parseZip(mOsJarPath);
+        TreeMap<String, ClassReader> found = new TreeMap<String, ClassReader>();
+
+        // this matches classes, a package match returns nothing
+        found.clear();
+        mAa.findGlobs("mock_android.view", zipClasses, found);
+
+        assertArrayEquals(new String[] { },
+            found.keySet().toArray());
+
+        // a complex glob search. * is a search pattern that matches names, not dots
+        mAa.findGlobs("mock_android.*.*Group$*Layout*", zipClasses, found);
+
+        assertArrayEquals(new String[] {
+                "mock_android.view.ViewGroup$LayoutParams",
+                "mock_android.view.ViewGroup$MarginLayoutParams"
+            },
+            found.keySet().toArray());
+
+        // a complex glob search. ** is a search pattern that matches names including dots
+        mAa.findGlobs("mock_android.**Group*", zipClasses, found);
+
+        assertArrayEquals(new String[] {
+                "mock_android.view.ViewGroup",
+                "mock_android.view.ViewGroup$LayoutParams",
+                "mock_android.view.ViewGroup$MarginLayoutParams"
+            },
+            found.keySet().toArray());
+
+        // matches a single class
+        found.clear();
+        mAa.findGlobs("mock_android.view.View", zipClasses, found);
+
+        assertArrayEquals(new String[] {
+                "mock_android.view.View"
+            },
+            found.keySet().toArray());
+
+        // matches everyting inside the given package but not sub-packages
+        found.clear();
+        mAa.findGlobs("mock_android.view.*", zipClasses, found);
+
+        assertArrayEquals(new String[] {
+                "mock_android.view.View",
+                "mock_android.view.ViewGroup",
+                "mock_android.view.ViewGroup$LayoutParams",
+                "mock_android.view.ViewGroup$MarginLayoutParams"
+            },
+            found.keySet().toArray());
+
+        for (String key : found.keySet()) {
+            ClassReader value = found.get(key);
+            assertNotNull("No value for " + key, value);
+            assertEquals(key, AsmAnalyzer.classReaderToClassName(value));
+        }
+    }
+
+    @Test
+    public void testFindClassesDerivingFrom() throws LogAbortException, IOException {
+        Map<String, ClassReader> zipClasses = mAa.parseZip(mOsJarPath);
+        TreeMap<String, ClassReader> found = new TreeMap<String, ClassReader>();
+
+        mAa.findClassesDerivingFrom("mock_android.view.View", zipClasses, found);
+
+        assertArrayEquals(new String[] {
+                "mock_android.view.View",
+                "mock_android.view.ViewGroup",
+                "mock_android.widget.LinearLayout",
+                "mock_android.widget.TableLayout",
+            },
+            found.keySet().toArray());
+
+        for (String key : found.keySet()) {
+            ClassReader value = found.get(key);
+            assertNotNull("No value for " + key, value);
+            assertEquals(key, AsmAnalyzer.classReaderToClassName(value));
+        }
+    }
+
+    @Test
+    public void testDependencyVisitor() throws IOException, LogAbortException {
+        Map<String, ClassReader> zipClasses = mAa.parseZip(mOsJarPath);
+        TreeMap<String, ClassReader> keep = new TreeMap<String, ClassReader>();
+        TreeMap<String, ClassReader> new_keep = new TreeMap<String, ClassReader>();
+        TreeMap<String, ClassReader> in_deps = new TreeMap<String, ClassReader>();
+        TreeMap<String, ClassReader> out_deps = new TreeMap<String, ClassReader>();
+
+        ClassReader cr = mAa.findClass("mock_android.widget.TableLayout", zipClasses, keep);
+        DependencyVisitor visitor = mAa.getVisitor(zipClasses, keep, new_keep, in_deps, out_deps);
+
+        // get first level dependencies
+        cr.accept(visitor, 0 /* flags */);
+
+        assertArrayEquals(new String[] {
+                "mock_android.view.ViewGroup",
+                "mock_android.widget.TableLayout$LayoutParams",
+            },
+            out_deps.keySet().toArray());
+
+        in_deps.putAll(out_deps);
+        out_deps.clear();
+
+        // get second level dependencies
+        for (ClassReader cr2 : in_deps.values()) {
+            cr2.accept(visitor, 0 /* flags */);
+        }
+
+        assertArrayEquals(new String[] {
+                "mock_android.view.View",
+                "mock_android.view.ViewGroup$LayoutParams",
+                "mock_android.view.ViewGroup$MarginLayoutParams",
+            },
+            out_deps.keySet().toArray());
+
+        in_deps.putAll(out_deps);
+        out_deps.clear();
+
+        // get third level dependencies (there are none)
+        for (ClassReader cr2 : in_deps.values()) {
+            cr2.accept(visitor, 0 /* flags */);
+        }
+
+        assertArrayEquals(new String[] { }, out_deps.keySet().toArray());
+    }
+}
diff --git a/tools/layoutlib/create/tests/com/android/tools/layoutlib/create/AsmGeneratorTest.java b/tools/layoutlib/create/tests/com/android/tools/layoutlib/create/AsmGeneratorTest.java
new file mode 100644
index 0000000..7b76a5b
--- /dev/null
+++ b/tools/layoutlib/create/tests/com/android/tools/layoutlib/create/AsmGeneratorTest.java
@@ -0,0 +1,119 @@
+/*
+ * Copyright (C) 2008 The Android Open Source Project
+ *
+ * Licensed under the Apache License, Version 2.0 (the "License");
+ * you may not use this file except in compliance with the License.
+ * You may obtain a copy of the License at
+ *
+ *      http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing, software
+ * distributed under the License is distributed on an "AS IS" BASIS,
+ * WITHOUT WARRANTIES 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.tools.layoutlib.create;
+
+
+import static org.junit.Assert.assertArrayEquals;
+
+import org.junit.After;
+import org.junit.Before;
+import org.junit.Test;
+
+import java.io.File;
+import java.io.IOException;
+import java.net.URL;
+import java.util.ArrayList;
+import java.util.Set;
+
+/**
+ * Unit tests for some methods of {@link AsmGenerator}.
+ */
+public class AsmGeneratorTest {
+
+    private MockLog mLog;
+    private ArrayList<String> mOsJarPath;
+    private String mOsDestJar;
+    private File mTempFile;
+
+    @Before
+    public void setUp() throws Exception {
+        mLog = new MockLog();
+        URL url = this.getClass().getClassLoader().getResource("data/mock_android.jar");
+
+        mOsJarPath = new ArrayList<String>();
+        mOsJarPath.add(url.getFile());
+
+        mTempFile = File.createTempFile("mock", "jar");
+        mOsDestJar = mTempFile.getAbsolutePath();
+        mTempFile.deleteOnExit();
+    }
+
+    @After
+    public void tearDown() throws Exception {
+        if (mTempFile != null) {
+            mTempFile.delete();
+            mTempFile = null;
+        }
+    }
+
+    @Test
+    public void testClassRenaming() throws IOException, LogAbortException {
+
+        ICreateInfo ci = new ICreateInfo() {
+            @Override
+            public Class<?>[] getInjectedClasses() {
+                // classes to inject in the final JAR
+                return new Class<?>[0];
+            }
+
+            @Override
+            public String[] getDelegateMethods() {
+                return new String[0];
+            }
+
+            @Override
+            public String[] getDelegateClassNatives() {
+                return new String[0];
+            }
+
+            @Override
+            public String[] getOverriddenMethods() {
+                // methods to force override
+                return new String[0];
+            }
+
+            @Override
+            public String[] getRenamedClasses() {
+                // classes to rename (so that we can replace them)
+                return new String[] {
+                        "mock_android.view.View", "mock_android.view._Original_View",
+                        "not.an.actual.ClassName", "anoter.fake.NewClassName",
+                };
+            }
+
+            @Override
+            public String[] getDeleteReturns() {
+                 // methods deleted from their return type.
+                return new String[0];
+            }
+        };
+
+        AsmGenerator agen = new AsmGenerator(mLog, mOsDestJar, ci);
+
+        AsmAnalyzer aa = new AsmAnalyzer(mLog, mOsJarPath, agen,
+                null,                 // derived from
+                new String[] {        // include classes
+                    "**"
+                });
+        aa.analyze();
+        agen.generate();
+
+        Set<String> notRenamed = agen.getClassesNotRenamed();
+        assertArrayEquals(new String[] { "not/an/actual/ClassName" }, notRenamed.toArray());
+    }
+}
diff --git a/tools/layoutlib/create/tests/com/android/tools/layoutlib/create/ClassHasNativeVisitorTest.java b/tools/layoutlib/create/tests/com/android/tools/layoutlib/create/ClassHasNativeVisitorTest.java
new file mode 100644
index 0000000..0135c40
--- /dev/null
+++ b/tools/layoutlib/create/tests/com/android/tools/layoutlib/create/ClassHasNativeVisitorTest.java
@@ -0,0 +1,102 @@
+/*
+ * Copyright (C) 2010 The Android Open Source Project
+ *
+ * Licensed under the Apache License, Version 2.0 (the "License");
+ * you may not use this file except in compliance with the License.
+ * You may obtain a copy of the License at
+ *
+ *      http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing, software
+ * distributed under the License is distributed on an "AS IS" BASIS,
+ * WITHOUT WARRANTIES 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.tools.layoutlib.create;
+
+import static org.junit.Assert.*;
+
+import org.junit.Test;
+import org.objectweb.asm.ClassReader;
+
+import java.io.IOException;
+import java.util.ArrayList;
+
+
+/**
+ * Tests {@link ClassHasNativeVisitor}.
+ */
+public class ClassHasNativeVisitorTest {
+
+    @Test
+    public void testHasNative() throws IOException {
+        MockClassHasNativeVisitor cv = new MockClassHasNativeVisitor();
+        String className =
+                this.getClass().getCanonicalName() + "$" + ClassWithNative.class.getSimpleName();
+        ClassReader cr = new ClassReader(className);
+
+        cr.accept(cv, 0 /* flags */);
+        assertArrayEquals(new String[] { "native_method" }, cv.getMethodsFound());
+        assertTrue(cv.hasNativeMethods());
+    }
+
+    @Test
+    public void testHasNoNative() throws IOException {
+        MockClassHasNativeVisitor cv = new MockClassHasNativeVisitor();
+        String className =
+            this.getClass().getCanonicalName() + "$" + ClassWithoutNative.class.getSimpleName();
+        ClassReader cr = new ClassReader(className);
+
+        cr.accept(cv, 0 /* flags */);
+        assertArrayEquals(new String[0], cv.getMethodsFound());
+        assertFalse(cv.hasNativeMethods());
+    }
+
+    //-------
+
+    /**
+     * Overrides {@link ClassHasNativeVisitor} to collec the name of the native methods found.
+     */
+    private static class MockClassHasNativeVisitor extends ClassHasNativeVisitor {
+        private ArrayList<String> mMethodsFound = new ArrayList<String>();
+
+        public String[] getMethodsFound() {
+            return mMethodsFound.toArray(new String[mMethodsFound.size()]);
+        }
+
+        @Override
+        protected void setHasNativeMethods(boolean hasNativeMethods, String methodName) {
+            if (hasNativeMethods) {
+                mMethodsFound.add(methodName);
+            }
+            super.setHasNativeMethods(hasNativeMethods, methodName);
+        }
+    }
+
+    /**
+     * Dummy test class with a native method.
+     */
+    public static class ClassWithNative {
+        public ClassWithNative() {
+        }
+
+        public void callTheNativeMethod() {
+            native_method();
+        }
+
+        private native void native_method();
+    }
+
+    /**
+     * Dummy test class with no native method.
+     */
+    public static class ClassWithoutNative {
+        public ClassWithoutNative() {
+        }
+
+        public void someMethod() {
+        }
+    }
+}
diff --git a/tools/layoutlib/create/tests/com/android/tools/layoutlib/create/DelegateClassAdapterTest.java b/tools/layoutlib/create/tests/com/android/tools/layoutlib/create/DelegateClassAdapterTest.java
new file mode 100644
index 0000000..6e120ce
--- /dev/null
+++ b/tools/layoutlib/create/tests/com/android/tools/layoutlib/create/DelegateClassAdapterTest.java
@@ -0,0 +1,463 @@
+/*
+ * Copyright (C) 2010 The Android Open Source Project
+ *
+ * Licensed under the Apache License, Version 2.0 (the "License");
+ * you may not use this file except in compliance with the License.
+ * You may obtain a copy of the License at
+ *
+ *      http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing, software
+ * distributed under the License is distributed on an "AS IS" BASIS,
+ * WITHOUT WARRANTIES 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.tools.layoutlib.create;
+
+
+import static org.junit.Assert.assertEquals;
+import static org.junit.Assert.assertFalse;
+import static org.junit.Assert.assertNotNull;
+import static org.junit.Assert.assertSame;
+import static org.junit.Assert.assertTrue;
+import static org.junit.Assert.fail;
+
+import com.android.tools.layoutlib.create.dataclass.ClassWithNative;
+import com.android.tools.layoutlib.create.dataclass.OuterClass;
+import com.android.tools.layoutlib.create.dataclass.OuterClass.InnerClass;
+
+import org.junit.Before;
+import org.junit.Test;
+import org.objectweb.asm.ClassReader;
+import org.objectweb.asm.ClassVisitor;
+import org.objectweb.asm.ClassWriter;
+
+import java.io.IOException;
+import java.io.PrintWriter;
+import java.io.StringWriter;
+import java.lang.annotation.Annotation;
+import java.lang.reflect.Constructor;
+import java.lang.reflect.InvocationTargetException;
+import java.lang.reflect.Method;
+import java.lang.reflect.Modifier;
+import java.util.HashMap;
+import java.util.HashSet;
+import java.util.Map;
+import java.util.Map.Entry;
+import java.util.Set;
+
+public class DelegateClassAdapterTest {
+
+    private MockLog mLog;
+
+    private static final String NATIVE_CLASS_NAME = ClassWithNative.class.getCanonicalName();
+    private static final String OUTER_CLASS_NAME = OuterClass.class.getCanonicalName();
+    private static final String INNER_CLASS_NAME = OuterClass.class.getCanonicalName() + "$" +
+                                                   InnerClass.class.getSimpleName();
+
+    @Before
+    public void setUp() throws Exception {
+        mLog = new MockLog();
+        mLog.setVerbose(true); // capture debug error too
+    }
+
+    /**
+     * Tests that a class not being modified still works.
+     */
+    @SuppressWarnings("unchecked")
+    @Test
+    public void testNoOp() throws Throwable {
+        // create an instance of the class that will be modified
+        // (load the class in a distinct class loader so that we can trash its definition later)
+        ClassLoader cl1 = new ClassLoader(this.getClass().getClassLoader()) { };
+        Class<ClassWithNative> clazz1 = (Class<ClassWithNative>) cl1.loadClass(NATIVE_CLASS_NAME);
+        ClassWithNative instance1 = clazz1.newInstance();
+        assertEquals(42, instance1.add(20, 22));
+        try {
+            instance1.callNativeInstance(10, 3.1415, new Object[0] );
+            fail("Test should have failed to invoke callTheNativeMethod [1]");
+        } catch (UnsatisfiedLinkError e) {
+            // This is expected to fail since the native method is not implemented.
+        }
+
+        // Now process it but tell the delegate to not modify any method
+        ClassWriter cw = new ClassWriter(0 /*flags*/);
+
+        HashSet<String> delegateMethods = new HashSet<String>();
+        String internalClassName = NATIVE_CLASS_NAME.replace('.', '/');
+        DelegateClassAdapter cv = new DelegateClassAdapter(
+                mLog, cw, internalClassName, delegateMethods);
+
+        ClassReader cr = new ClassReader(NATIVE_CLASS_NAME);
+        cr.accept(cv, 0 /* flags */);
+
+        // Load the generated class in a different class loader and try it again
+
+        ClassLoader2 cl2 = null;
+        try {
+            cl2 = new ClassLoader2() {
+                @Override
+                public void testModifiedInstance() throws Exception {
+                    Class<?> clazz2 = loadClass(NATIVE_CLASS_NAME);
+                    Object i2 = clazz2.newInstance();
+                    assertNotNull(i2);
+                    assertEquals(42, callAdd(i2, 20, 22));
+
+                    try {
+                        callCallNativeInstance(i2, 10, 3.1415, new Object[0]);
+                        fail("Test should have failed to invoke callTheNativeMethod [2]");
+                    } catch (InvocationTargetException e) {
+                        // This is expected to fail since the native method has NOT been
+                        // overridden here.
+                        assertEquals(UnsatisfiedLinkError.class, e.getCause().getClass());
+                    }
+
+                    // Check that the native method does NOT have the new annotation
+                    Method[] m = clazz2.getDeclaredMethods();
+                    assertEquals("native_instance", m[2].getName());
+                    assertTrue(Modifier.isNative(m[2].getModifiers()));
+                    Annotation[] a = m[2].getAnnotations();
+                    assertEquals(0, a.length);
+                }
+            };
+            cl2.add(NATIVE_CLASS_NAME, cw);
+            cl2.testModifiedInstance();
+        } catch (Throwable t) {
+            throw dumpGeneratedClass(t, cl2);
+        }
+    }
+
+    /**
+     * {@link DelegateMethodAdapter2} does not support overriding constructors yet,
+     * so this should fail with an {@link UnsupportedOperationException}.
+     *
+     * Although not tested here, the message of the exception should contain the
+     * constructor signature.
+     */
+    @Test(expected=UnsupportedOperationException.class)
+    public void testConstructorsNotSupported() throws IOException {
+        ClassWriter cw = new ClassWriter(0 /*flags*/);
+
+        String internalClassName = NATIVE_CLASS_NAME.replace('.', '/');
+
+        HashSet<String> delegateMethods = new HashSet<String>();
+        delegateMethods.add("<init>");
+        DelegateClassAdapter cv = new DelegateClassAdapter(
+                mLog, cw, internalClassName, delegateMethods);
+
+        ClassReader cr = new ClassReader(NATIVE_CLASS_NAME);
+        cr.accept(cv, 0 /* flags */);
+    }
+
+    @Test
+    public void testDelegateNative() throws Throwable {
+        ClassWriter cw = new ClassWriter(0 /*flags*/);
+        String internalClassName = NATIVE_CLASS_NAME.replace('.', '/');
+
+        HashSet<String> delegateMethods = new HashSet<String>();
+        delegateMethods.add(DelegateClassAdapter.ALL_NATIVES);
+        DelegateClassAdapter cv = new DelegateClassAdapter(
+                mLog, cw, internalClassName, delegateMethods);
+
+        ClassReader cr = new ClassReader(NATIVE_CLASS_NAME);
+        cr.accept(cv, 0 /* flags */);
+
+        // Load the generated class in a different class loader and try it
+        ClassLoader2 cl2 = null;
+        try {
+            cl2 = new ClassLoader2() {
+                @Override
+                public void testModifiedInstance() throws Exception {
+                    Class<?> clazz2 = loadClass(NATIVE_CLASS_NAME);
+                    Object i2 = clazz2.newInstance();
+                    assertNotNull(i2);
+
+                    // Use reflection to access inner methods
+                    assertEquals(42, callAdd(i2, 20, 22));
+
+                     Object[] objResult = new Object[] { null };
+                     int result = callCallNativeInstance(i2, 10, 3.1415, objResult);
+                     assertEquals((int)(10 + 3.1415), result);
+                     assertSame(i2, objResult[0]);
+
+                     // Check that the native method now has the new annotation and is not native
+                     Method[] m = clazz2.getDeclaredMethods();
+                     assertEquals("native_instance", m[2].getName());
+                     assertFalse(Modifier.isNative(m[2].getModifiers()));
+                     Annotation[] a = m[2].getAnnotations();
+                     assertEquals("LayoutlibDelegate", a[0].annotationType().getSimpleName());
+                }
+            };
+            cl2.add(NATIVE_CLASS_NAME, cw);
+            cl2.testModifiedInstance();
+        } catch (Throwable t) {
+            throw dumpGeneratedClass(t, cl2);
+        }
+    }
+
+    @Test
+    public void testDelegateInner() throws Throwable {
+        // We'll delegate the "get" method of both the inner and outer class.
+        HashSet<String> delegateMethods = new HashSet<String>();
+        delegateMethods.add("get");
+        delegateMethods.add("privateMethod");
+
+        // Generate the delegate for the outer class.
+        ClassWriter cwOuter = new ClassWriter(0 /*flags*/);
+        String outerClassName = OUTER_CLASS_NAME.replace('.', '/');
+        DelegateClassAdapter cvOuter = new DelegateClassAdapter(
+                mLog, cwOuter, outerClassName, delegateMethods);
+        ClassReader cr = new ClassReader(OUTER_CLASS_NAME);
+        cr.accept(cvOuter, 0 /* flags */);
+
+        // Generate the delegate for the inner class.
+        ClassWriter cwInner = new ClassWriter(0 /*flags*/);
+        String innerClassName = INNER_CLASS_NAME.replace('.', '/');
+        DelegateClassAdapter cvInner = new DelegateClassAdapter(
+                mLog, cwInner, innerClassName, delegateMethods);
+        cr = new ClassReader(INNER_CLASS_NAME);
+        cr.accept(cvInner, 0 /* flags */);
+
+        // Load the generated classes in a different class loader and try them
+        ClassLoader2 cl2 = null;
+        try {
+            cl2 = new ClassLoader2() {
+                @Override
+                public void testModifiedInstance() throws Exception {
+
+                    // Check the outer class
+                    Class<?> outerClazz2 = loadClass(OUTER_CLASS_NAME);
+                    Object o2 = outerClazz2.newInstance();
+                    assertNotNull(o2);
+
+                    // The original Outer.get returns 1+10+20,
+                    // but the delegate makes it return 4+10+20
+                    assertEquals(4+10+20, callGet(o2, 10, 20));
+                    assertEquals(1+10+20, callGet_Original(o2, 10, 20));
+
+                    // The original Outer has a private method that is
+                    // delegated. We should be able to call both the delegate
+                    // and the original (which is now public).
+                    assertEquals("outerPrivateMethod",
+                                 callMethod(o2, "privateMethod_Original", false /*makePublic*/));
+
+                    // The original method is private, so by default we can't access it
+                    boolean gotIllegalAccessException = false;
+                    try {
+                         callMethod(o2, "privateMethod", false /*makePublic*/);
+                    } catch(IllegalAccessException e) {
+                        gotIllegalAccessException = true;
+                    }
+                    assertTrue(gotIllegalAccessException);
+                    // Try again, but now making it accessible
+                    assertEquals("outerPrivate_Delegate",
+                            callMethod(o2, "privateMethod", true /*makePublic*/));
+
+                    // Check the inner class. Since it's not a static inner class, we need
+                    // to use the hidden constructor that takes the outer class as first parameter.
+                    Class<?> innerClazz2 = loadClass(INNER_CLASS_NAME);
+                    Constructor<?> innerCons = innerClazz2.getConstructor(
+                                                                new Class<?>[] { outerClazz2 });
+                    Object i2 = innerCons.newInstance(new Object[] { o2 });
+                    assertNotNull(i2);
+
+                    // The original Inner.get returns 3+10+20,
+                    // but the delegate makes it return 6+10+20
+                    assertEquals(6+10+20, callGet(i2, 10, 20));
+                    assertEquals(3+10+20, callGet_Original(i2, 10, 20));
+                }
+            };
+            cl2.add(OUTER_CLASS_NAME, cwOuter.toByteArray());
+            cl2.add(INNER_CLASS_NAME, cwInner.toByteArray());
+            cl2.testModifiedInstance();
+        } catch (Throwable t) {
+            throw dumpGeneratedClass(t, cl2);
+        }
+    }
+
+    //-------
+
+    /**
+     * A class loader than can define and instantiate our modified classes.
+     * <p/>
+     * The trick here is that this class loader will test our <em>modified</em> version
+     * of the classes, the one with the delegate calls.
+     * <p/>
+     * Trying to do so in the original class loader generates all sort of link issues because
+     * there are 2 different definitions of the same class name. This class loader will
+     * define and load the class when requested by name and provide helpers to access the
+     * instance methods via reflection.
+     */
+    private abstract class ClassLoader2 extends ClassLoader {
+
+        private final Map<String, byte[]> mClassDefs = new HashMap<String, byte[]>();
+
+        public ClassLoader2() {
+            super(null);
+        }
+
+        public ClassLoader2 add(String className, byte[] definition) {
+            mClassDefs.put(className, definition);
+            return this;
+        }
+
+        public ClassLoader2 add(String className, ClassWriter rewrittenClass) {
+            mClassDefs.put(className, rewrittenClass.toByteArray());
+            return this;
+        }
+
+        private Set<Entry<String, byte[]>> getByteCode() {
+            return mClassDefs.entrySet();
+        }
+
+        @SuppressWarnings("unused")
+        @Override
+        protected Class<?> findClass(String name) throws ClassNotFoundException {
+            try {
+                return super.findClass(name);
+            } catch (ClassNotFoundException e) {
+
+                byte[] def = mClassDefs.get(name);
+                if (def != null) {
+                    // Load the modified ClassWithNative from its bytes representation.
+                    return defineClass(name, def, 0, def.length);
+                }
+
+                try {
+                    // Load everything else from the original definition into the new class loader.
+                    ClassReader cr = new ClassReader(name);
+                    ClassWriter cw = new ClassWriter(0);
+                    cr.accept(cw, 0);
+                    byte[] bytes = cw.toByteArray();
+                    return defineClass(name, bytes, 0, bytes.length);
+
+                } catch (IOException ioe) {
+                    throw new RuntimeException(ioe);
+                }
+            }
+        }
+
+        /**
+         * Accesses {@link OuterClass#get} or {@link InnerClass#get}via reflection.
+         */
+        public int callGet(Object instance, int a, long b) throws Exception {
+            Method m = instance.getClass().getMethod("get",
+                    new Class<?>[] { int.class, long.class } );
+
+            Object result = m.invoke(instance, new Object[] { a, b });
+            return ((Integer) result).intValue();
+        }
+
+        /**
+         * Accesses the "_Original" methods for {@link OuterClass#get}
+         * or {@link InnerClass#get}via reflection.
+         */
+        public int callGet_Original(Object instance, int a, long b) throws Exception {
+            Method m = instance.getClass().getMethod("get_Original",
+                    new Class<?>[] { int.class, long.class } );
+
+            Object result = m.invoke(instance, new Object[] { a, b });
+            return ((Integer) result).intValue();
+        }
+
+        /**
+         * Accesses the any declared method that takes no parameter via reflection.
+         */
+        @SuppressWarnings("unchecked")
+        public <T> T callMethod(Object instance, String methodName, boolean makePublic) throws Exception {
+            Method m = instance.getClass().getDeclaredMethod(methodName, (Class<?>[])null);
+
+            boolean wasAccessible = m.isAccessible();
+            if (makePublic && !wasAccessible) {
+                m.setAccessible(true);
+            }
+
+            Object result = m.invoke(instance, (Object[])null);
+
+            if (makePublic && !wasAccessible) {
+                m.setAccessible(false);
+            }
+
+            return (T) result;
+        }
+
+        /**
+         * Accesses {@link ClassWithNative#add(int, int)} via reflection.
+         */
+        public int callAdd(Object instance, int a, int b) throws Exception {
+            Method m = instance.getClass().getMethod("add",
+                    new Class<?>[] { int.class, int.class });
+
+            Object result = m.invoke(instance, new Object[] { a, b });
+            return ((Integer) result).intValue();
+        }
+
+        /**
+         * Accesses {@link ClassWithNative#callNativeInstance(int, double, Object[])}
+         * via reflection.
+         */
+        public int callCallNativeInstance(Object instance, int a, double d, Object[] o)
+                throws Exception {
+            Method m = instance.getClass().getMethod("callNativeInstance",
+                    new Class<?>[] { int.class, double.class, Object[].class });
+
+            Object result = m.invoke(instance, new Object[] { a, d, o });
+            return ((Integer) result).intValue();
+        }
+
+        public abstract void testModifiedInstance() throws Exception;
+    }
+
+    /**
+     * For debugging, it's useful to dump the content of the generated classes
+     * along with the exception that was generated.
+     *
+     * However to make it work you need to pull in the org.objectweb.asm.util.TraceClassVisitor
+     * class and associated utilities which are found in the ASM source jar. Since we don't
+     * want that dependency in the source code, we only put it manually for development and
+     * access the TraceClassVisitor via reflection if present.
+     *
+     * @param t The exception thrown by {@link ClassLoader2#testModifiedInstance()}
+     * @param cl2 The {@link ClassLoader2} instance with the generated bytecode.
+     * @return Either original {@code t} or a new wrapper {@link Throwable}
+     */
+    private Throwable dumpGeneratedClass(Throwable t, ClassLoader2 cl2) {
+        try {
+            // For debugging, dump the bytecode of the class in case of unexpected error
+            // if we can find the TraceClassVisitor class.
+            Class<?> tcvClass = Class.forName("org.objectweb.asm.util.TraceClassVisitor");
+
+            StringBuilder sb = new StringBuilder();
+            sb.append('\n').append(t.getClass().getCanonicalName());
+            if (t.getMessage() != null) {
+                sb.append(": ").append(t.getMessage());
+              }
+
+            for (Entry<String, byte[]> entry : cl2.getByteCode()) {
+                String className = entry.getKey();
+                byte[] bytes = entry.getValue();
+
+                StringWriter sw = new StringWriter();
+                PrintWriter pw = new PrintWriter(sw);
+                // next 2 lines do: TraceClassVisitor tcv = new TraceClassVisitor(pw);
+                Constructor<?> cons = tcvClass.getConstructor(new Class<?>[] { pw.getClass() });
+                Object tcv = cons.newInstance(new Object[] { pw });
+                ClassReader cr2 = new ClassReader(bytes);
+                cr2.accept((ClassVisitor) tcv, 0 /* flags */);
+
+                sb.append("\nBytecode dump: <").append(className).append(">:\n")
+                  .append(sw.toString());
+            }
+
+            // Re-throw exception with new message
+            RuntimeException ex = new RuntimeException(sb.toString(), t);
+            return ex;
+        } catch (Throwable ignore) {
+            // In case of problem, just throw the original exception as-is.
+            return t;
+        }
+    }
+
+}
diff --git a/tools/layoutlib/create/tests/com/android/tools/layoutlib/create/LogTest.java b/tools/layoutlib/create/tests/com/android/tools/layoutlib/create/LogTest.java
new file mode 100644
index 0000000..1a5f653
--- /dev/null
+++ b/tools/layoutlib/create/tests/com/android/tools/layoutlib/create/LogTest.java
@@ -0,0 +1,88 @@
+/*
+ * Copyright (C) 2008 The Android Open Source Project
+ *
+ * Licensed under the Apache License, Version 2.0 (the "License");
+ * you may not use this file except in compliance with the License.
+ * You may obtain a copy of the License at
+ *
+ *      http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing, software
+ * distributed under the License is distributed on an "AS IS" BASIS,
+ * WITHOUT WARRANTIES 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.tools.layoutlib.create;
+
+import static org.junit.Assert.*;
+
+import org.junit.After;
+import org.junit.Before;
+import org.junit.Test;
+
+public class LogTest {
+
+    private MockLog mLog;
+
+    @Before
+    public void setUp() throws Exception {
+        mLog = new MockLog();
+    }
+
+    @After
+    public void tearDown() throws Exception {
+        // pass
+    }
+
+    @Test
+    public void testDebug() {
+        assertEquals("", mLog.getOut());
+        assertEquals("", mLog.getErr());
+
+        mLog.setVerbose(false);
+        mLog.debug("Test %d", 42);
+        assertEquals("", mLog.getOut());
+
+        mLog.setVerbose(true);
+        mLog.debug("Test %d", 42);
+
+        assertEquals("Test 42\n", mLog.getOut());
+        assertEquals("", mLog.getErr());
+    }
+
+    @Test
+    public void testInfo() {
+        assertEquals("", mLog.getOut());
+        assertEquals("", mLog.getErr());
+
+        mLog.info("Test %d", 43);
+
+        assertEquals("Test 43\n", mLog.getOut());
+        assertEquals("", mLog.getErr());
+    }
+
+    @Test
+    public void testError() {
+        assertEquals("", mLog.getOut());
+        assertEquals("", mLog.getErr());
+
+        mLog.error("Test %d", 44);
+
+        assertEquals("", mLog.getOut());
+        assertEquals("Test 44\n", mLog.getErr());
+    }
+
+    @Test
+    public void testException() {
+        assertEquals("", mLog.getOut());
+        assertEquals("", mLog.getErr());
+
+        Exception e = new Exception("My Exception");
+        mLog.exception(e, "Test %d", 44);
+
+        assertEquals("", mLog.getOut());
+        assertTrue(mLog.getErr().startsWith("Test 44\njava.lang.Exception: My Exception"));
+    }
+}
diff --git a/tools/layoutlib/create/tests/com/android/tools/layoutlib/create/MockLog.java b/tools/layoutlib/create/tests/com/android/tools/layoutlib/create/MockLog.java
new file mode 100644
index 0000000..de750a3
--- /dev/null
+++ b/tools/layoutlib/create/tests/com/android/tools/layoutlib/create/MockLog.java
@@ -0,0 +1,43 @@
+/*
+ * Copyright (C) 2010 The Android Open Source Project
+ *
+ * Licensed under the Apache License, Version 2.0 (the "License");
+ * you may not use this file except in compliance with the License.
+ * You may obtain a copy of the License at
+ *
+ *      http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing, software
+ * distributed under the License is distributed on an "AS IS" BASIS,
+ * WITHOUT WARRANTIES 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.tools.layoutlib.create;
+
+
+public class MockLog extends Log {
+    StringBuilder mOut = new StringBuilder();
+    StringBuilder mErr = new StringBuilder();
+
+    public String getOut() {
+        return mOut.toString();
+    }
+
+    public String getErr() {
+        return mErr.toString();
+    }
+
+    @Override
+    protected void outPrintln(String msg) {
+        mOut.append(msg);
+        mOut.append('\n');
+    }
+
+    @Override
+    protected void errPrintln(String msg) {
+        mErr.append(msg);
+        mErr.append('\n');
+    }
+}
diff --git a/tools/layoutlib/create/tests/com/android/tools/layoutlib/create/RenameClassAdapterTest.java b/tools/layoutlib/create/tests/com/android/tools/layoutlib/create/RenameClassAdapterTest.java
new file mode 100644
index 0000000..90c6a9c
--- /dev/null
+++ b/tools/layoutlib/create/tests/com/android/tools/layoutlib/create/RenameClassAdapterTest.java
@@ -0,0 +1,120 @@
+/*
+ * Copyright (C) 2008 The Android Open Source Project
+ *
+ * Licensed under the Apache License, Version 2.0 (the "License");
+ * you may not use this file except in compliance with the License.
+ * You may obtain a copy of the License at
+ *
+ *      http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing, software
+ * distributed under the License is distributed on an "AS IS" BASIS,
+ * WITHOUT WARRANTIES 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.tools.layoutlib.create;
+
+import static org.junit.Assert.*;
+
+import org.junit.After;
+import org.junit.Before;
+import org.junit.Test;
+
+/**
+ * 
+ */
+public class RenameClassAdapterTest {
+
+    private RenameClassAdapter mOuter;
+    private RenameClassAdapter mInner;
+
+    @Before
+    public void setUp() throws Exception {
+        mOuter = new RenameClassAdapter(null, // cv
+                                         "com.pack.Old",
+                                         "org.blah.New");
+        
+        mInner = new RenameClassAdapter(null, // cv
+                                         "com.pack.Old$Inner",
+                                         "org.blah.New$Inner"); 
+    }
+
+    @After
+    public void tearDown() throws Exception {
+    }
+
+    /**
+     * Renames a type, e.g. "Lcom.package.My;"
+     * If the type doesn't need to be renamed, returns the input string as-is.
+     */
+    @Test
+    public void testRenameTypeDesc() {
+
+        // primitive types are left untouched
+        assertEquals("I", mOuter.renameTypeDesc("I"));
+        assertEquals("D", mOuter.renameTypeDesc("D"));
+        assertEquals("V", mOuter.renameTypeDesc("V"));
+
+        // object types that need no renaming are left untouched
+        assertEquals("Lcom.package.MyClass;", mOuter.renameTypeDesc("Lcom.package.MyClass;"));
+        assertEquals("Lcom.package.MyClass;", mInner.renameTypeDesc("Lcom.package.MyClass;"));
+
+        // object types that match the requirements
+        assertEquals("Lorg.blah.New;", mOuter.renameTypeDesc("Lcom.pack.Old;"));
+        assertEquals("Lorg.blah.New$Inner;", mInner.renameTypeDesc("Lcom.pack.Old$Inner;"));
+        // inner classes match the base type which is being renamed
+        assertEquals("Lorg.blah.New$Other;", mOuter.renameTypeDesc("Lcom.pack.Old$Other;"));
+        assertEquals("Lorg.blah.New$Other;", mInner.renameTypeDesc("Lcom.pack.Old$Other;"));
+
+        // arrays
+        assertEquals("[Lorg.blah.New;",  mOuter.renameTypeDesc("[Lcom.pack.Old;"));
+        assertEquals("[[Lorg.blah.New;", mOuter.renameTypeDesc("[[Lcom.pack.Old;"));
+        
+        assertEquals("[Lorg.blah.New;",  mInner.renameTypeDesc("[Lcom.pack.Old;"));
+        assertEquals("[[Lorg.blah.New;", mInner.renameTypeDesc("[[Lcom.pack.Old;"));
+    }
+
+    /**
+     * Renames an object type, e.g. "Lcom.package.MyClass;" or an array type that has an
+     * object element, e.g. "[Lcom.package.MyClass;"
+     * If the type doesn't need to be renamed, returns the internal name of the input type.
+     */
+    @Test
+    public void testRenameType() {
+        // Skip. This is actually tested by testRenameTypeDesc above.
+    }
+
+    /**
+     * Renames an internal type name, e.g. "com.package.MyClass".
+     * If the type doesn't need to be renamed, returns the input string as-is.
+     */
+    @Test
+    public void testRenameInternalType() {
+        // a descriptor is not left untouched
+        assertEquals("Lorg.blah.New;", mOuter.renameInternalType("Lcom.pack.Old;"));
+        assertEquals("Lorg.blah.New$Inner;", mOuter.renameInternalType("Lcom.pack.Old$Inner;"));
+
+        // an actual FQCN
+        assertEquals("org.blah.New", mOuter.renameInternalType("com.pack.Old"));
+        assertEquals("org.blah.New$Inner", mOuter.renameInternalType("com.pack.Old$Inner"));
+
+        assertEquals("org.blah.New$Other", mInner.renameInternalType("com.pack.Old$Other"));
+        assertEquals("org.blah.New$Other", mInner.renameInternalType("com.pack.Old$Other"));
+    }
+
+    /**
+     * Renames a method descriptor, i.e. applies renameType to all arguments and to the
+     * return value.
+     */
+    @Test
+    public void testRenameMethodDesc() {
+        assertEquals("(IDLorg.blah.New;[Lorg.blah.New$Inner;)Lorg.blah.New$Other;",
+               mOuter.renameMethodDesc("(IDLcom.pack.Old;[Lcom.pack.Old$Inner;)Lcom.pack.Old$Other;"));
+    }
+
+    
+
+}
diff --git a/tools/layoutlib/create/tests/com/android/tools/layoutlib/create/dataclass/ClassWithNative.java b/tools/layoutlib/create/tests/com/android/tools/layoutlib/create/dataclass/ClassWithNative.java
new file mode 100644
index 0000000..c314853
--- /dev/null
+++ b/tools/layoutlib/create/tests/com/android/tools/layoutlib/create/dataclass/ClassWithNative.java
@@ -0,0 +1,45 @@
+/*
+ * Copyright (C) 2010 The Android Open Source Project
+ *
+ * Licensed under the Apache License, Version 2.0 (the "License");
+ * you may not use this file except in compliance with the License.
+ * You may obtain a copy of the License at
+ *
+ *      http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing, software
+ * distributed under the License is distributed on an "AS IS" BASIS,
+ * WITHOUT WARRANTIES 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.tools.layoutlib.create.dataclass;
+
+import com.android.tools.layoutlib.create.DelegateClassAdapterTest;
+
+/**
+ * Dummy test class with a native method.
+ * The native method is not defined and any attempt to invoke it will
+ * throw an {@link UnsatisfiedLinkError}.
+ *
+ * Used by {@link DelegateClassAdapterTest}.
+ */
+public class ClassWithNative {
+    public ClassWithNative() {
+    }
+
+    public int add(int a, int b) {
+        return a + b;
+    }
+
+    // Note: it's good to have a long or double for testing parameters since they take
+    // 2 slots in the stack/locals maps.
+
+    public int callNativeInstance(int a, double d, Object[] o) {
+        return native_instance(a, d, o);
+    }
+
+    private native int native_instance(int a, double d, Object[] o);
+}
+
diff --git a/tools/layoutlib/create/tests/com/android/tools/layoutlib/create/dataclass/ClassWithNative_Delegate.java b/tools/layoutlib/create/tests/com/android/tools/layoutlib/create/dataclass/ClassWithNative_Delegate.java
new file mode 100644
index 0000000..a3d4dc6
--- /dev/null
+++ b/tools/layoutlib/create/tests/com/android/tools/layoutlib/create/dataclass/ClassWithNative_Delegate.java
@@ -0,0 +1,34 @@
+/*
+ * Copyright (C) 2010 The Android Open Source Project
+ *
+ * Licensed under the Apache License, Version 2.0 (the "License");
+ * you may not use this file except in compliance with the License.
+ * You may obtain a copy of the License at
+ *
+ *      http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing, software
+ * distributed under the License is distributed on an "AS IS" BASIS,
+ * WITHOUT WARRANTIES 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.tools.layoutlib.create.dataclass;
+
+import com.android.tools.layoutlib.create.DelegateClassAdapterTest;
+
+/**
+ * The delegate that receives the call to {@link ClassWithNative_Delegate}'s overridden methods.
+ *
+ * Used by {@link DelegateClassAdapterTest}.
+ */
+public class ClassWithNative_Delegate {
+    public static int native_instance(ClassWithNative instance, int a, double d, Object[] o) {
+        if (o != null && o.length > 0) {
+            o[0] = instance;
+        }
+        return (int)(a + d);
+    }
+}
+
diff --git a/tools/layoutlib/create/tests/com/android/tools/layoutlib/create/dataclass/OuterClass.java b/tools/layoutlib/create/tests/com/android/tools/layoutlib/create/dataclass/OuterClass.java
new file mode 100644
index 0000000..f083e76
--- /dev/null
+++ b/tools/layoutlib/create/tests/com/android/tools/layoutlib/create/dataclass/OuterClass.java
@@ -0,0 +1,53 @@
+/*
+ * Copyright (C) 2011 The Android Open Source Project
+ *
+ * Licensed under the Apache License, Version 2.0 (the "License");
+ * you may not use this file except in compliance with the License.
+ * You may obtain a copy of the License at
+ *
+ *      http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing, software
+ * distributed under the License is distributed on an "AS IS" BASIS,
+ * WITHOUT WARRANTIES 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.tools.layoutlib.create.dataclass;
+
+import com.android.tools.layoutlib.create.DelegateClassAdapterTest;
+
+/**
+ * Test class with an inner class.
+ *
+ * Used by {@link DelegateClassAdapterTest}.
+ */
+public class OuterClass {
+    private int mOuterValue = 1;
+    public OuterClass() {
+    }
+
+    // Outer.get returns 1 + a + b
+    // Note: it's good to have a long or double for testing parameters since they take
+    // 2 slots in the stack/locals maps.
+    public int get(int a, long b) {
+        return mOuterValue + a + (int) b;
+    }
+
+    public class InnerClass {
+        public InnerClass() {
+        }
+
+        // Inner.get returns 2 + 1 + a + b
+        public int get(int a, long b) {
+            return 2 + mOuterValue + a + (int) b;
+        }
+    }
+
+    @SuppressWarnings("unused")
+    private String privateMethod() {
+        return "outerPrivateMethod";
+    }
+}
+
diff --git a/tools/layoutlib/create/tests/com/android/tools/layoutlib/create/dataclass/OuterClass_Delegate.java b/tools/layoutlib/create/tests/com/android/tools/layoutlib/create/dataclass/OuterClass_Delegate.java
new file mode 100644
index 0000000..774be8e
--- /dev/null
+++ b/tools/layoutlib/create/tests/com/android/tools/layoutlib/create/dataclass/OuterClass_Delegate.java
@@ -0,0 +1,34 @@
+/*
+ * Copyright (C) 2011 The Android Open Source Project
+ *
+ * Licensed under the Apache License, Version 2.0 (the "License");
+ * you may not use this file except in compliance with the License.
+ * You may obtain a copy of the License at
+ *
+ *      http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing, software
+ * distributed under the License is distributed on an "AS IS" BASIS,
+ * WITHOUT WARRANTIES 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.tools.layoutlib.create.dataclass;
+
+import com.android.tools.layoutlib.create.DelegateClassAdapterTest;
+
+/**
+ * Used by {@link DelegateClassAdapterTest}.
+ */
+public class OuterClass_Delegate {
+    // The delegate override of Outer.get returns 4 + a + b
+    public static int get(OuterClass instance, int a, long b) {
+        return 4 + a + (int) b;
+    }
+
+    public static String privateMethod(OuterClass instance) {
+        return "outerPrivate_Delegate";
+    }
+}
+
diff --git a/tools/layoutlib/create/tests/com/android/tools/layoutlib/create/dataclass/OuterClass_InnerClass_Delegate.java b/tools/layoutlib/create/tests/com/android/tools/layoutlib/create/dataclass/OuterClass_InnerClass_Delegate.java
new file mode 100644
index 0000000..b472220
--- /dev/null
+++ b/tools/layoutlib/create/tests/com/android/tools/layoutlib/create/dataclass/OuterClass_InnerClass_Delegate.java
@@ -0,0 +1,30 @@
+/*
+ * Copyright (C) 2011 The Android Open Source Project
+ *
+ * Licensed under the Apache License, Version 2.0 (the "License");
+ * you may not use this file except in compliance with the License.
+ * You may obtain a copy of the License at
+ *
+ *      http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing, software
+ * distributed under the License is distributed on an "AS IS" BASIS,
+ * WITHOUT WARRANTIES 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.tools.layoutlib.create.dataclass;
+
+import com.android.tools.layoutlib.create.DelegateClassAdapterTest;
+import com.android.tools.layoutlib.create.dataclass.OuterClass.InnerClass;
+
+/**
+ * Used by {@link DelegateClassAdapterTest}.
+ */
+public class OuterClass_InnerClass_Delegate {
+    // The delegate override of Inner.get return 6 + a + b
+    public static int get(OuterClass outer, InnerClass inner, int a, long b) {
+        return 6 + a + (int) b;
+    }
+}
diff --git a/tools/layoutlib/create/tests/data/mock_android.jar b/tools/layoutlib/create/tests/data/mock_android.jar
new file mode 100644
index 0000000..a7ea74f
--- /dev/null
+++ b/tools/layoutlib/create/tests/data/mock_android.jar
Binary files differ
diff --git a/tools/layoutlib/create/tests/data/mock_android.jardesc b/tools/layoutlib/create/tests/data/mock_android.jardesc
new file mode 100644
index 0000000..95f7591
--- /dev/null
+++ b/tools/layoutlib/create/tests/data/mock_android.jardesc
@@ -0,0 +1,18 @@
+<?xml version="1.0" encoding="WINDOWS-1252" standalone="no"?>
+<jardesc>
+    <jar path="C:/ralf/google/src/raphael-lapdroid/device/tools/layoutlib/create/tests/data/mock_android.jar"/>
+    <options buildIfNeeded="true" compress="true" descriptionLocation="/layoutlib_create/tests/data/mock_android.jardesc" exportErrors="true" exportWarnings="true" includeDirectoryEntries="false" overwrite="false" saveDescription="true" storeRefactorings="false" useSourceFolders="false"/>
+    <storedRefactorings deprecationInfo="true" structuralOnly="false"/>
+    <selectedProjects/>
+    <manifest generateManifest="true" manifestLocation="" manifestVersion="1.0" reuseManifest="false" saveManifest="false" usesManifest="true">
+        <sealing sealJar="false">
+            <packagesToSeal/>
+            <packagesToUnSeal/>
+        </sealing>
+    </manifest>
+    <selectedElements exportClassFiles="true" exportJavaFiles="false" exportOutputFolder="false">
+        <javaElement handleIdentifier="=layoutlib_create/tests&lt;mock_android.widget"/>
+        <javaElement handleIdentifier="=layoutlib_create/tests&lt;mock_android.view"/>
+        <javaElement handleIdentifier="=layoutlib_create/tests&lt;mock_android.dummy"/>
+    </selectedElements>
+</jardesc>
diff --git a/tools/layoutlib/create/tests/mock_android/dummy/InnerTest.java b/tools/layoutlib/create/tests/mock_android/dummy/InnerTest.java
new file mode 100644
index 0000000..e355ead
--- /dev/null
+++ b/tools/layoutlib/create/tests/mock_android/dummy/InnerTest.java
@@ -0,0 +1,90 @@
+/*
+ * Copyright (C) 2008 The Android Open Source Project
+ *
+ * Licensed under the Eclipse Public License, Version 1.0 (the "License");
+ * you may not use this file except in compliance with the License.
+ * You may obtain a copy of the License at
+ *
+ *      http://www.eclipse.org/org/documents/epl-v10.php
+ *
+ * Unless required by applicable law or agreed to in writing, software
+ * distributed under the License is distributed on an "AS IS" BASIS,
+ * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+ * See the License for the specific language governing permissions and
+ * limitations under the License.
+ */
+
+package mock_android.dummy;
+
+import java.util.ArrayList;
+import java.util.Collection;
+import java.util.Iterator;
+
+public class InnerTest {
+
+    private int mSomeField;
+    private MyStaticInnerClass mInnerInstance;
+    private MyIntEnum mTheIntEnum;
+    private MyGenerics1<int[][], InnerTest, MyIntEnum, float[]> mGeneric1;
+
+    public class NotStaticInner2 extends NotStaticInner1 {
+
+    }
+
+    public class NotStaticInner1 {
+
+        public void someThing() {
+            mSomeField = 2;
+            mInnerInstance = null;
+        }
+
+    }
+
+    private static class MyStaticInnerClass {
+
+    }
+    
+    private static class DerivingClass extends InnerTest {
+        
+    }
+    
+    // enums are a kind of inner static class
+    public enum MyIntEnum {
+        VALUE0(0),
+        VALUE1(1),
+        VALUE2(2);
+
+        MyIntEnum(int myInt) {
+            this.myInt = myInt;
+        }
+        final int myInt;
+    }
+    
+    public static class MyGenerics1<T, U, V, W> {
+        public MyGenerics1() {
+            int a = 1;
+        }
+    }
+    
+    public <X> void genericMethod1(X a, X[] a) {
+    }
+
+    public <X, Y> void genericMethod2(X a, List<Y> b) {
+    }
+
+    public <X, Y> void genericMethod3(X a, List<Y extends InnerTest> b) {
+    }
+
+    public <T extends InnerTest> void genericMethod4(T[] a, Collection<T> b, Collection<?> c) {
+        Iterator<T> i = b.iterator();
+    }
+
+    public void someMethod(InnerTest self) {
+        mSomeField = self.mSomeField;
+        MyStaticInnerClass m = new MyStaticInnerClass();
+        mInnerInstance = m;
+        mTheIntEnum = null;
+        mGeneric1 = new MyGenerics1();
+        genericMethod(new DerivingClass[0], new ArrayList<DerivingClass>(), new ArrayList<InnerTest>());
+    }
+}
diff --git a/tools/layoutlib/create/tests/mock_android/view/View.java b/tools/layoutlib/create/tests/mock_android/view/View.java
new file mode 100644
index 0000000..a80a98d
--- /dev/null
+++ b/tools/layoutlib/create/tests/mock_android/view/View.java
@@ -0,0 +1,21 @@
+/*
+ * Copyright (C) 2008 The Android Open Source Project
+ *
+ * Licensed under the Eclipse Public License, Version 1.0 (the "License");
+ * you may not use this file except in compliance with the License.
+ * You may obtain a copy of the License at
+ *
+ *      http://www.eclipse.org/org/documents/epl-v10.php
+ *
+ * Unless required by applicable law or agreed to in writing, software
+ * distributed under the License is distributed on an "AS IS" BASIS,
+ * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+ * See the License for the specific language governing permissions and
+ * limitations under the License.
+ */
+
+package mock_android.view;
+
+public class View {
+
+}
diff --git a/tools/layoutlib/create/tests/mock_android/view/ViewGroup.java b/tools/layoutlib/create/tests/mock_android/view/ViewGroup.java
new file mode 100644
index 0000000..466470f
--- /dev/null
+++ b/tools/layoutlib/create/tests/mock_android/view/ViewGroup.java
@@ -0,0 +1,29 @@
+/*
+ * Copyright (C) 2008 The Android Open Source Project
+ *
+ * Licensed under the Eclipse Public License, Version 1.0 (the "License");
+ * you may not use this file except in compliance with the License.
+ * You may obtain a copy of the License at
+ *
+ *      http://www.eclipse.org/org/documents/epl-v10.php
+ *
+ * Unless required by applicable law or agreed to in writing, software
+ * distributed under the License is distributed on an "AS IS" BASIS,
+ * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+ * See the License for the specific language governing permissions and
+ * limitations under the License.
+ */
+
+package mock_android.view;
+
+public class ViewGroup extends View {
+
+    public class MarginLayoutParams extends LayoutParams {
+
+    }
+
+    public class LayoutParams {
+
+    }
+
+}
diff --git a/tools/layoutlib/create/tests/mock_android/widget/LinearLayout.java b/tools/layoutlib/create/tests/mock_android/widget/LinearLayout.java
new file mode 100644
index 0000000..3870a63
--- /dev/null
+++ b/tools/layoutlib/create/tests/mock_android/widget/LinearLayout.java
@@ -0,0 +1,27 @@
+/*
+ * Copyright (C) 2008 The Android Open Source Project
+ *
+ * Licensed under the Eclipse Public License, Version 1.0 (the "License");
+ * you may not use this file except in compliance with the License.
+ * You may obtain a copy of the License at
+ *
+ *      http://www.eclipse.org/org/documents/epl-v10.php
+ *
+ * Unless required by applicable law or agreed to in writing, software
+ * distributed under the License is distributed on an "AS IS" BASIS,
+ * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+ * See the License for the specific language governing permissions and
+ * limitations under the License.
+ */
+
+package mock_android.widget;
+
+import mock_android.view.ViewGroup;
+
+public class LinearLayout extends ViewGroup {
+
+    public class LayoutParams extends mock_android.view.ViewGroup.LayoutParams {
+
+    }
+
+}
diff --git a/tools/layoutlib/create/tests/mock_android/widget/TableLayout.java b/tools/layoutlib/create/tests/mock_android/widget/TableLayout.java
new file mode 100644
index 0000000..e455e7d
--- /dev/null
+++ b/tools/layoutlib/create/tests/mock_android/widget/TableLayout.java
@@ -0,0 +1,27 @@
+/*
+ * Copyright (C) 2008 The Android Open Source Project
+ *
+ * Licensed under the Eclipse Public License, Version 1.0 (the "License");
+ * you may not use this file except in compliance with the License.
+ * You may obtain a copy of the License at
+ *
+ *      http://www.eclipse.org/org/documents/epl-v10.php
+ *
+ * Unless required by applicable law or agreed to in writing, software
+ * distributed under the License is distributed on an "AS IS" BASIS,
+ * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+ * See the License for the specific language governing permissions and
+ * limitations under the License.
+ */
+
+package mock_android.widget;
+
+import mock_android.view.ViewGroup;
+
+public class TableLayout extends ViewGroup {
+
+    public class LayoutParams extends MarginLayoutParams {
+
+    }
+
+}