AI 146498: am: CL 145983 am: CL 145911 ADT #1778786: tool to generate stubbed jar file.
This is only a preliminary CL. More will follow but this is
a good start, with the following caveats:
What it does:
- take an input jar, a list of includes, a list of excludes.
- generate actual Java source for the filtered classes.
What it doesn't do yet:
- some more work on filtering inner elements (methods, etc.)
- properly generate inner classes.
- hide synthetic fields.
- some classes body are missing
- directly generate a stubbed bytecode/jar rather than source.
I'll likely want to keep the source generator for debugging
purposes or if we want to integrate with a build system instead.
- classpath will be changed in the final CL to refer to the external
ASM lib rather than the project. I need the source for debugging
rigth now.
- will review comments before submitting.
Original author: raphael
Merged from: //branches/cupcake/...
Original author: android-build
Automated import of CL 146498
diff --git a/tools/mkstubs/.classpath b/tools/mkstubs/.classpath
new file mode 100644
index 0000000..a6b4c47
--- /dev/null
+++ b/tools/mkstubs/.classpath
@@ -0,0 +1,9 @@
+<?xml version="1.0" encoding="UTF-8"?>
+<classpath>
+ <classpathentry kind="src" path="src"/>
+ <classpathentry kind="src" path="tests"/>
+ <classpathentry kind="con" path="org.eclipse.jdt.launching.JRE_CONTAINER/org.eclipse.jdt.internal.debug.ui.launcher.StandardVMType/J2SE-1.5"/>
+ <classpathentry kind="con" path="org.eclipse.jdt.junit.JUNIT_CONTAINER/4"/>
+ <classpathentry combineaccessrules="false" kind="src" path="/asm3"/>
+ <classpathentry kind="output" path="bin"/>
+</classpath>
diff --git a/tools/mkstubs/.project b/tools/mkstubs/.project
new file mode 100644
index 0000000..bef013e
--- /dev/null
+++ b/tools/mkstubs/.project
@@ -0,0 +1,18 @@
+<?xml version="1.0" encoding="UTF-8"?>
+<projectDescription>
+ <name>MkStubs</name>
+ <comment></comment>
+ <projects>
+ <project>asm3</project>
+ </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/mkstubs/Android.mk b/tools/mkstubs/Android.mk
new file mode 100644
index 0000000..a12bf18
--- /dev/null
+++ b/tools/mkstubs/Android.mk
@@ -0,0 +1,28 @@
+#
+# Copyright (C) 2009 The Android Open Source Project
+#
+# Licensed under the Apache License, Version 2.0 (the "License");
+# you may not use this file except in compliance with the License.
+# You may obtain a copy of the License at
+#
+# http://www.apache.org/licenses/LICENSE-2.0
+#
+# Unless required by applicable law or agreed to in writing, software
+# distributed under the License is distributed on an "AS IS" BASIS,
+# WITHOUT 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-3.1
+
+LOCAL_MODULE := mkstubs
+
+include $(BUILD_HOST_JAVA_LIBRARY)
+
diff --git a/tools/mkstubs/manifest.txt b/tools/mkstubs/manifest.txt
new file mode 100644
index 0000000..c0ca8e6
--- /dev/null
+++ b/tools/mkstubs/manifest.txt
@@ -0,0 +1 @@
+Main-Class: com.android.mkstubs.Main
diff --git a/tools/mkstubs/src/com/android/mkstubs/AsmAnalyzer.java b/tools/mkstubs/src/com/android/mkstubs/AsmAnalyzer.java
new file mode 100644
index 0000000..c023cf2
--- /dev/null
+++ b/tools/mkstubs/src/com/android/mkstubs/AsmAnalyzer.java
@@ -0,0 +1,137 @@
+/*
+ * Copyright (C) 2009 The Android Open Source Project
+ *
+ * Licensed under the Apache License, Version 2.0 (the "License");
+ * you may not use this file except in compliance with the License.
+ * You may obtain a copy of the License at
+ *
+ * http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing, software
+ * distributed under the License is distributed on an "AS IS" BASIS,
+ * WITHOUT WARRANTIES 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.mkstubs;
+
+import org.objectweb.asm.ClassReader;
+
+import java.io.IOException;
+import java.util.ArrayList;
+import java.util.Enumeration;
+import java.util.HashSet;
+import java.util.Iterator;
+import java.util.Map;
+import java.util.Set;
+import java.util.TreeMap;
+import java.util.zip.ZipEntry;
+import java.util.zip.ZipFile;
+
+/**
+ *
+ */
+class AsmAnalyzer {
+
+ /**
+ * 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> parseInputJar(String inputJarPath) throws IOException {
+ TreeMap<String, ClassReader> classes = new TreeMap<String, ClassReader>();
+
+ ZipFile zip = new ZipFile(inputJarPath);
+ 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 = classReaderToAsmName(cr);
+ classes.put(className, cr);
+ }
+ }
+
+ return classes;
+ }
+
+ /**
+ * Utility that returns the fully qualified ASM class name for a ClassReader.
+ * E.g. it returns something like android/view/View.
+ */
+ static String classReaderToAsmName(ClassReader classReader) {
+ if (classReader == null) {
+ return null;
+ } else {
+ return classReader.getClassName();
+ }
+ }
+
+ public void filter(
+ Map<String, ClassReader> classes,
+ ArrayList<String> inclusions,
+ ArrayList<String> exclusions) {
+
+ ArrayList<String> inPrefix = new ArrayList<String>();
+ HashSet <String> inFull = new HashSet <String>();
+ ArrayList<String> exPrefix = new ArrayList<String>();
+ HashSet <String> exFull = new HashSet <String>();
+
+ for (String in : inclusions) {
+ if (in.endsWith("*")) {
+ inPrefix.add(in.substring(0, in.length() - 1));
+ } else {
+ inFull.add(in);
+ }
+ }
+
+ for (String ex : exclusions) {
+ if (ex.endsWith("*")) {
+ exPrefix.add(ex.substring(0, ex.length() - 1));
+ } else {
+ exFull.add(ex);
+ }
+ }
+
+
+ Set<String> keys = classes.keySet();
+ for(Iterator<String> it = keys.iterator(); it.hasNext(); ) {
+ String key = it.next();
+
+
+ // Check if it can be included.
+ boolean keep = inFull.contains(key);
+ if (!keep) {
+ // Check for a prefix inclusion
+ for (String prefix : inPrefix) {
+ if (key.startsWith(prefix)) {
+ keep = true;
+ break;
+ }
+ }
+ }
+
+ if (keep) {
+ // check for a full exclusion
+ keep = !exFull.contains(key);
+ }
+ if (keep) {
+ // or check for prefix exclusion
+ for (String prefix : exPrefix) {
+ if (key.startsWith(prefix)) {
+ keep = false;
+ break;
+ }
+ }
+ }
+
+ // remove if we don't keep it
+ if (!keep) {
+ System.out.println("- Remove class " + key);
+ it.remove();
+ }
+ }
+ }
+
+}
diff --git a/tools/mkstubs/src/com/android/mkstubs/AsmGenerator.java b/tools/mkstubs/src/com/android/mkstubs/AsmGenerator.java
new file mode 100644
index 0000000..3446b00
--- /dev/null
+++ b/tools/mkstubs/src/com/android/mkstubs/AsmGenerator.java
@@ -0,0 +1,90 @@
+/*
+ * Copyright (C) 2009 The Android Open Source Project
+ *
+ * Licensed under the Apache License, Version 2.0 (the "License");
+ * you may not use this file except in compliance with the License.
+ * You may obtain a copy of the License at
+ *
+ * http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing, software
+ * distributed under the License is distributed on an "AS IS" BASIS,
+ * WITHOUT WARRANTIES 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.mkstubs;
+
+import com.android.mkstubs.sourcer.JavaSourcer;
+import com.android.mkstubs.sourcer.Output;
+
+import org.objectweb.asm.ClassReader;
+import org.objectweb.asm.ClassVisitor;
+
+import java.io.File;
+import java.io.FileWriter;
+import java.io.IOException;
+import java.io.Writer;
+import java.util.List;
+import java.util.Map;
+import java.util.Map.Entry;
+
+/**
+ *
+ */
+class AsmGenerator {
+
+ /**
+ * Generate source for the stubbed classes, mostly for debug purposes.
+ * @throws IOException
+ */
+ public void generateSource(File baseDir,
+ Map<String, ClassReader> classes,
+ List<String> exclusions) throws IOException {
+
+ for (Entry<String, ClassReader> entry : classes.entrySet()) {
+ ClassReader cr = entry.getValue();
+
+ String name = classNameToJavaPath(cr.getClassName());
+
+ FileWriter fw = null;
+ try {
+ fw = createWriter(baseDir, name);
+ dumpClass(fw, cr, exclusions);
+ } finally {
+ fw.close();
+ }
+ }
+ }
+
+ FileWriter createWriter(File baseDir, String name) throws IOException {
+ File f = new File(baseDir, name);
+ f.getParentFile().mkdirs();
+
+ System.out.println("Writing " + f.getPath());
+
+ return new FileWriter(f);
+ }
+
+ /**
+ * 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.java"
+ */
+ String classNameToJavaPath(String className) {
+ return className.replace('.', '/').concat(".java");
+ }
+
+ /**
+ * Generate a source equivalent to the stubbed version of the class reader,
+ * minus all exclusions
+ */
+ void dumpClass(Writer fw, ClassReader cr, List<String> exclusions) {
+ System.out.println("Dump " + cr.getClassName());
+
+ ClassVisitor javaWriter = new JavaSourcer(new Output(fw));
+ ClassVisitor filter = new FilterClassAdapter(javaWriter, exclusions);
+ cr.accept(filter, 0 /*flags*/);
+ }
+
+}
diff --git a/tools/mkstubs/src/com/android/mkstubs/FilterClassAdapter.java b/tools/mkstubs/src/com/android/mkstubs/FilterClassAdapter.java
new file mode 100644
index 0000000..71c55f8
--- /dev/null
+++ b/tools/mkstubs/src/com/android/mkstubs/FilterClassAdapter.java
@@ -0,0 +1,139 @@
+/*
+ * Copyright (C) 2009 The Android Open Source Project
+ *
+ * Licensed under the Apache License, Version 2.0 (the "License");
+ * you may not use this file except in compliance with the License.
+ * You may obtain a copy of the License at
+ *
+ * http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing, software
+ * distributed under the License is distributed on an "AS IS" BASIS,
+ * WITHOUT WARRANTIES 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.mkstubs;
+
+import org.objectweb.asm.AnnotationVisitor;
+import org.objectweb.asm.Attribute;
+import org.objectweb.asm.ClassAdapter;
+import org.objectweb.asm.ClassVisitor;
+import org.objectweb.asm.FieldVisitor;
+import org.objectweb.asm.MethodVisitor;
+import org.objectweb.asm.Opcodes;
+
+import java.util.List;
+
+/**
+ * A class visitor that filters out all the referenced exclusions
+ */
+class FilterClassAdapter extends ClassAdapter {
+
+ private final List<String> mExclusions;
+
+ public FilterClassAdapter(ClassVisitor writer, List<String> exclusions) {
+ super(writer);
+ mExclusions = exclusions;
+ }
+
+ @Override
+ public void visit(int version, int access, String name, String signature,
+ String superName, String[] interfaces) {
+
+ // TODO filter super type
+ // TODO filter interfaces
+
+ super.visit(version, access, name, signature, superName, interfaces);
+ }
+
+ @Override
+ public void visitEnd() {
+ super.visitEnd();
+ }
+
+ /**
+ * Visits a field.
+ *
+ * {@inheritDoc}
+ *
+ * Examples:
+ * name = mArg
+ * desc = Ljava/Lang/String;
+ * signature = null (not a template) or template type
+ */
+ @Override
+ public FieldVisitor visitField(int access, String name, String desc,
+ String signature, Object value) {
+ // exclude private fields
+ if ((access & Opcodes.ACC_PRIVATE) != 0) {
+ return null;
+ }
+
+ // TODO filter on name
+
+ return super.visitField(access, name, desc, signature, value);
+ }
+
+ /**
+ * Visits a method.
+ *
+ * {@inheritDoc}
+ *
+ * Examples:
+ * name = <init>
+ * desc = ()V
+ * signature = null (not a template) or template type
+ */
+ @Override
+ public MethodVisitor visitMethod(int access, String name, String desc,
+ String signature, String[] exceptions) {
+
+ // exclude private methods
+ if ((access & Opcodes.ACC_PRIVATE) != 0) {
+ return null;
+ }
+
+ // TODO filter exceptions: error if filtered exception is being used
+
+ // TODO filter on name; error if filtered desc or signatures is being used
+
+ return super.visitMethod(access, name, desc, signature, exceptions);
+ }
+
+ @Override
+ public AnnotationVisitor visitAnnotation(String desc, boolean visible) {
+
+ // Filter on desc type
+ return super.visitAnnotation(desc, visible);
+ }
+
+ @Override
+ public void visitAttribute(Attribute attr) {
+ // pass
+ }
+
+ @Override
+ public void visitInnerClass(String name, String outerName, String innerName, int access) {
+
+ // exclude private methods
+ if ((access & Opcodes.ACC_PRIVATE) != 0) {
+ return;
+ }
+
+ // TODO filter on name
+
+ super.visitInnerClass(name, outerName, innerName, access);
+ }
+
+ @Override
+ public void visitOuterClass(String owner, String name, String desc) {
+ // TODO Auto-generated method stub
+ }
+
+ @Override
+ public void visitSource(String source, String debug) {
+ // pass
+ }
+}
diff --git a/tools/mkstubs/src/com/android/mkstubs/Main.java b/tools/mkstubs/src/com/android/mkstubs/Main.java
new file mode 100644
index 0000000..017b2f1
--- /dev/null
+++ b/tools/mkstubs/src/com/android/mkstubs/Main.java
@@ -0,0 +1,165 @@
+/*
+ * Copyright (C) 2009 The Android Open Source Project
+ *
+ * Licensed under the Apache License, Version 2.0 (the "License");
+ * you may not use this file except in compliance with the License.
+ * You may obtain a copy of the License at
+ *
+ * http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing, software
+ * distributed under the License is distributed on an "AS IS" BASIS,
+ * WITHOUT WARRANTIES 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.mkstubs;
+
+import org.objectweb.asm.ClassReader;
+
+import java.io.BufferedReader;
+import java.io.File;
+import java.io.FileReader;
+import java.io.IOException;
+import java.util.ArrayList;
+import java.util.Map;
+
+
+/**
+ *
+ */
+public class Main {
+
+ static class Params {
+ private String mInputJarPath;
+ private String mOutputJarPath;
+ private ArrayList<String> mInclusions = new ArrayList<String>();
+ private ArrayList<String> mExclusions = new ArrayList<String>();
+
+ public Params(String inputJarPath, String outputJarPath) {
+ mInputJarPath = inputJarPath;
+ mOutputJarPath = outputJarPath;
+ }
+
+ public String getInputJarPath() {
+ return mInputJarPath;
+ }
+
+ public String getOutputJarPath() {
+ return mOutputJarPath;
+ }
+
+ public ArrayList<String> getExclusions() {
+ return mExclusions;
+ }
+
+ public ArrayList<String> getInclusions() {
+ return mInclusions;
+ }
+ }
+
+ /**
+ * @param args
+ */
+ public static void main(String[] args) {
+
+ Main m = new Main();
+ try {
+ Params p = m.processArgs(args);
+ m.process(p);
+ } catch (IOException e) {
+ e.printStackTrace();
+ }
+ }
+
+ private Params processArgs(String[] args) throws IOException {
+
+ if (args.length < 2) {
+ usage();
+ }
+
+ Params p = new Params(args[0], args[1]);
+
+ for (int i = 2; i < args.length; i++) {
+ String s = args[i];
+ if (s.startsWith("@")) {
+ addStringsFromFile(p, s.substring(1));
+ } else if (s.startsWith("-")) {
+ p.getExclusions().add(s.substring(1));
+ } else if (s.startsWith("+")) {
+ p.getInclusions().add(s.substring(1));
+ }
+ }
+
+ return p;
+ }
+
+ private void addStringsFromFile(Params p, String inputFile)
+ throws IOException {
+ BufferedReader br = null;
+ try {
+ br = new BufferedReader(new FileReader(inputFile));
+ String line;
+ while ((line = br.readLine()) != null) {
+ line = line.trim();
+ if (line.length() == 0) {
+ continue;
+ }
+ char mode = line.charAt(0);
+ line = line.substring(1).trim();
+
+ if (line.length() > 0) {
+ // Keep all class names in ASM path-like format, e.g. android/view/View
+ line = line.replace('.', '/');
+ if (mode == '-') {
+ p.getExclusions().add(line);
+ } else if (mode == '+') {
+ p.getInclusions().add(line);
+ }
+ }
+ }
+ } finally {
+ br.close();
+ }
+ }
+
+ private void usage() {
+ System.out.println("Usage: mkstub input.jar output.jar [excluded-class @excluded-classes-file ...]");
+
+ System.out.println("Include syntax:\n" +
+ "+com.package.* : whole package, with glob\n" +
+ "+com.package.Class[$Inner] or ...Class*: whole classes with optional glob\n" +
+ "Inclusion is not supported at method/field level.\n\n");
+
+ System.out.println("Exclude syntax:\n" +
+ "-com.package.* : whole package, with glob\n" +
+ "-com.package.Class[$Inner] or ...Class*: whole classes with optional glob\n" +
+ "-com.package.Class#method: whole method or field\n" +
+ "-com.package.Class#method(IILjava/lang/String;)V: specific method with signature.\n\n");
+ System.exit(1);
+ }
+
+ private void process(Params p) throws IOException {
+ AsmAnalyzer aa = new AsmAnalyzer();
+ Map<String, ClassReader> classes = aa.parseInputJar(p.getInputJarPath());
+
+ aa.filter(classes, p.getInclusions(), p.getExclusions());
+
+ AsmGenerator gen = new AsmGenerator();
+
+ // dump as Java source files, mostly for debugging
+ File dst_src_dir = new File(p.getOutputJarPath() + File.separator + "sources");
+ dst_src_dir.mkdir();
+ gen.generateSource(dst_src_dir, classes, p.getExclusions());
+
+ }
+
+ /** @deprecated debug only */
+ private void displayClasses(Map<String, ClassReader> classes) {
+ for(String className : classes.keySet()) {
+ System.out.println("Found " + className);
+ }
+ }
+
+}
diff --git a/tools/mkstubs/src/com/android/mkstubs/sourcer/AccessSourcer.java b/tools/mkstubs/src/com/android/mkstubs/sourcer/AccessSourcer.java
new file mode 100644
index 0000000..757dcea
--- /dev/null
+++ b/tools/mkstubs/src/com/android/mkstubs/sourcer/AccessSourcer.java
@@ -0,0 +1,104 @@
+/*
+ * Copyright (C) 2009 The Android Open Source Project
+ *
+ * Licensed under the Apache License, Version 2.0 (the "License");
+ * you may not use this file except in compliance with the License.
+ * You may obtain a copy of the License at
+ *
+ * http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing, software
+ * distributed under the License is distributed on an "AS IS" BASIS,
+ * WITHOUT WARRANTIES 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.mkstubs.sourcer;
+
+import org.objectweb.asm.Opcodes;
+
+/**
+ *
+ */
+class AccessSourcer {
+
+ private final Output mOutput;
+
+ public static int IS_CLASS = 1;
+ public static int IS_FIELD = 2;
+ public static int IS_METHOD = 4;
+
+ private enum Flag {
+ ACC_PUBLIC(Opcodes.ACC_PUBLIC , IS_CLASS | IS_FIELD | IS_METHOD),
+ ACC_PRIVATE(Opcodes.ACC_PRIVATE , IS_CLASS | IS_FIELD | IS_METHOD),
+ ACC_PROTECTED(Opcodes.ACC_PROTECTED , IS_CLASS | IS_FIELD | IS_METHOD),
+ ACC_STATIC(Opcodes.ACC_STATIC , IS_FIELD | IS_METHOD),
+ ACC_FINAL(Opcodes.ACC_FINAL , IS_CLASS | IS_FIELD | IS_METHOD),
+ ACC_SUPER(Opcodes.ACC_SUPER , IS_CLASS),
+ ACC_SYNCHRONIZED(Opcodes.ACC_SYNCHRONIZED , IS_METHOD),
+ ACC_VOLATILE(Opcodes.ACC_VOLATILE , IS_FIELD),
+ ACC_BRIDGE(Opcodes.ACC_BRIDGE , IS_METHOD),
+ ACC_VARARGS(Opcodes.ACC_VARARGS , IS_METHOD),
+ ACC_TRANSIENT(Opcodes.ACC_TRANSIENT , IS_FIELD),
+ ACC_NATIVE(Opcodes.ACC_NATIVE , IS_METHOD),
+ ACC_INTERFACE(Opcodes.ACC_INTERFACE , IS_CLASS),
+ ACC_ABSTRACT(Opcodes.ACC_ABSTRACT , IS_CLASS | IS_METHOD),
+ ACC_STRICT(Opcodes.ACC_STRICT , IS_METHOD),
+ ACC_SYNTHETIC(Opcodes.ACC_SYNTHETIC , IS_CLASS | IS_FIELD | IS_METHOD),
+ ACC_ANNOTATION(Opcodes.ACC_ANNOTATION , IS_CLASS),
+ ACC_ENUM(Opcodes.ACC_ENUM , IS_CLASS),
+ ACC_DEPRECATED(Opcodes.ACC_DEPRECATED , IS_CLASS | IS_FIELD | IS_METHOD)
+ ;
+
+ private final int mValue;
+ private final int mFilter;
+
+ private Flag(int value, int filter) {
+ mValue = value;
+ mFilter = filter;
+ }
+
+ public int getValue() {
+ return mValue;
+ }
+
+ public int getFilter() {
+ return mFilter;
+ }
+
+ /** Transforms "ACC_PUBLIC" into "public" */
+ @Override
+ public String toString() {
+ return super.toString().substring(4).toLowerCase();
+ }
+ }
+
+
+ public AccessSourcer(Output output) {
+ mOutput = output;
+ }
+
+ /**
+ * Generates a list of access keywords, e.g. "public final".
+ *
+ * @param access The access mode, e.g. 33 or 18
+ * @param filter One of {@link #IS_CLASS}, {@link #IS_FIELD} or {@link #IS_METHOD}, which
+ * indicates the validity context.
+ */
+ public void write(int access, int filter) {
+
+ boolean need_sep = false;
+
+ for (Flag f : Flag.values()) {
+ if ((f.getFilter() & filter) != 0 && (access & f.getValue()) != 0) {
+ if (need_sep) {
+ mOutput.write(" ");
+ }
+ mOutput.write(f.toString());
+ need_sep = true;
+ }
+ }
+
+ }
+}
diff --git a/tools/mkstubs/src/com/android/mkstubs/sourcer/AnnotationSourcer.java b/tools/mkstubs/src/com/android/mkstubs/sourcer/AnnotationSourcer.java
new file mode 100644
index 0000000..bbf1cb2
--- /dev/null
+++ b/tools/mkstubs/src/com/android/mkstubs/sourcer/AnnotationSourcer.java
@@ -0,0 +1,82 @@
+/*
+ * Copyright (C) 2009 The Android Open Source Project
+ *
+ * Licensed under the Apache License, Version 2.0 (the "License");
+ * you may not use this file except in compliance with the License.
+ * You may obtain a copy of the License at
+ *
+ * http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing, software
+ * distributed under the License is distributed on an "AS IS" BASIS,
+ * WITHOUT WARRANTIES 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.mkstubs.sourcer;
+
+import org.objectweb.asm.AnnotationVisitor;
+
+/**
+ *
+ */
+class AnnotationSourcer implements AnnotationVisitor {
+
+ private final String mOpenChar;
+ private final String mCloseChar;
+ private final Output mOutput;
+ private boolean mNeedClose;
+
+ public AnnotationSourcer(Output output) {
+ this(output, false /*isArray*/);
+ }
+
+ public AnnotationSourcer(Output output, boolean isArray) {
+ mOutput = output;
+ mOpenChar = isArray ? "[" : "(";
+ mCloseChar = isArray ? "]" : ")";
+ }
+
+ public void visit(String name, Object value) {
+ startOpen();
+
+ if (name != null) {
+ mOutput.write("%s=", name);
+ }
+ if (value != null) {
+ mOutput.write(name.toString());
+ }
+ }
+
+ private void startOpen() {
+ if (!mNeedClose) {
+ mNeedClose = true;
+ mOutput.write(mOpenChar);
+ }
+ }
+
+ public void visitEnd() {
+ if (mNeedClose) {
+ mOutput.write(mCloseChar);
+ }
+ mOutput.write("\n");
+ }
+
+ public AnnotationVisitor visitAnnotation(String name, String desc) {
+ startOpen();
+
+ mOutput.write("@%s", name);
+ return this;
+ }
+
+ public AnnotationVisitor visitArray(String name) {
+ startOpen();
+ return new AnnotationSourcer(mOutput, true /*isArray*/);
+ }
+
+ public void visitEnum(String name, String desc, String value) {
+ mOutput.write("/* annotation enum not supported: %s */\n", name);
+ }
+
+}
diff --git a/tools/mkstubs/src/com/android/mkstubs/sourcer/FieldSourcer.java b/tools/mkstubs/src/com/android/mkstubs/sourcer/FieldSourcer.java
new file mode 100644
index 0000000..4f9c229
--- /dev/null
+++ b/tools/mkstubs/src/com/android/mkstubs/sourcer/FieldSourcer.java
@@ -0,0 +1,73 @@
+/*
+ * Copyright (C) 2009 The Android Open Source Project
+ *
+ * Licensed under the Apache License, Version 2.0 (the "License");
+ * you may not use this file except in compliance with the License.
+ * You may obtain a copy of the License at
+ *
+ * http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing, software
+ * distributed under the License is distributed on an "AS IS" BASIS,
+ * WITHOUT WARRANTIES 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.mkstubs.sourcer;
+
+import org.objectweb.asm.AnnotationVisitor;
+import org.objectweb.asm.Attribute;
+import org.objectweb.asm.FieldVisitor;
+import org.objectweb.asm.signature.SignatureReader;
+
+/**
+ *
+ */
+class FieldSourcer implements FieldVisitor {
+
+ private final Output mOutput;
+ private final int mAccess;
+ private final String mName;
+ private final String mDesc;
+ private final String mSignature;
+
+ public FieldSourcer(Output output, int access, String name, String desc, String signature) {
+ mOutput = output;
+ mAccess = access;
+ mName = name;
+ mDesc = desc;
+ mSignature = signature;
+ }
+
+ public AnnotationVisitor visitAnnotation(String desc, boolean visible) {
+ mOutput.write("@%s", desc);
+ return new AnnotationSourcer(mOutput);
+ }
+
+ public void visitAttribute(Attribute attr) {
+ mOutput.write("%s /* non-standard attribute */ ", attr.type);
+ }
+
+ public void visitEnd() {
+ // Need to write type and field name after the annotations and attributes.
+
+ AccessSourcer as = new AccessSourcer(mOutput);
+ as.write(mAccess, AccessSourcer.IS_FIELD);
+
+ if (mSignature == null) {
+ mOutput.write(" %s", mOutput.decodeDesc(mDesc));
+ } else {
+ mOutput.write(" ");
+ SignatureReader sigReader = new SignatureReader(mSignature);
+ SignatureSourcer sigSourcer = new SignatureSourcer();
+ sigReader.acceptType(sigSourcer);
+ mOutput.write(sigSourcer.toString());
+ }
+
+ mOutput.write(" %s", mName);
+
+ mOutput.write(";\n");
+ }
+
+}
diff --git a/tools/mkstubs/src/com/android/mkstubs/sourcer/JavaSourcer.java b/tools/mkstubs/src/com/android/mkstubs/sourcer/JavaSourcer.java
new file mode 100644
index 0000000..fb06c68
--- /dev/null
+++ b/tools/mkstubs/src/com/android/mkstubs/sourcer/JavaSourcer.java
@@ -0,0 +1,139 @@
+/*
+ * Copyright (C) 2009 The Android Open Source Project
+ *
+ * Licensed under the Apache License, Version 2.0 (the "License");
+ * you may not use this file except in compliance with the License.
+ * You may obtain a copy of the License at
+ *
+ * http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing, software
+ * distributed under the License is distributed on an "AS IS" BASIS,
+ * WITHOUT WARRANTIES 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.mkstubs.sourcer;
+
+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;
+import org.objectweb.asm.signature.SignatureReader;
+
+/**
+ * A class visitor that rewrites a java source
+ */
+public class JavaSourcer implements ClassVisitor {
+
+ private final Output mOutput;
+ private final AccessSourcer mAccessSourcer;
+ private String mClassName;
+
+ public JavaSourcer(Output output) {
+ mOutput = output;
+ mAccessSourcer = new AccessSourcer(mOutput);
+ }
+
+ /* Examples:
+ * name = com/foo/MyClass
+ * signature = null (if not generic)
+ * superName = java/lang/Object
+ * interfaces = [ java/lang/Runnable ... ]
+ */
+ public void visit(int version, int access, String name, String signature,
+ String superName, String[] interfaces) {
+
+ String pkg = name.substring(0, name.lastIndexOf('/')).replace('/', '.');
+ mClassName = name.substring(name.lastIndexOf('/') + 1);
+
+ mOutput.write("package %s;\n", pkg);
+
+ // dump access keywords. Note: do not dump "super" here
+ mAccessSourcer.write(access & ~Opcodes.ACC_SUPER, AccessSourcer.IS_CLASS);
+
+ // write class name
+ mOutput.write(" class %s", mClassName);
+
+ if (signature != null) {
+ // write template formal definition and super type
+ SignatureReader sigReader = new SignatureReader(signature);
+ SignatureSourcer sigSourcer = new SignatureSourcer();
+ sigReader.accept(sigSourcer);
+
+ if (sigSourcer.hasFormalsContent()) {
+ mOutput.write(sigSourcer.formalsToString());
+ }
+
+ mOutput.write(" extends %s", sigSourcer.getSuperClass().toString());
+
+ } else {
+ // write non-generic super type
+ mOutput.write(" extends %s", superName.replace('/', '.'));
+ }
+
+ // write interfaces defined, if any
+ if (interfaces != null && interfaces.length > 0) {
+ mOutput.write(" implements ");
+ boolean need_sep = false;
+ for (String i : interfaces) {
+ if (need_sep) {
+ mOutput.write(", ");
+ }
+ mOutput.write(i.replace('/', '.'));
+ need_sep = true;
+ }
+ }
+
+ // open class body
+ mOutput.write(" {\n");
+ }
+
+ public void visitEnd() {
+ mOutput.write("}\n");
+ }
+
+ public AnnotationVisitor visitAnnotation(String desc, boolean visible) {
+ mOutput.write("@%s", desc);
+ return new AnnotationSourcer(mOutput);
+ }
+
+ public void visitAttribute(Attribute attr) {
+ mOutput.write("%s /* non-standard class attribute */ ", attr.type);
+ }
+
+
+ public FieldVisitor visitField(int access, String name, String desc, String signature,
+ Object value) {
+ // skip synthetic fields
+ if ((access & Opcodes.ACC_SYNTHETIC) != 0) {
+ return null;
+ }
+
+ return new FieldSourcer(mOutput, access, name, desc, signature);
+ }
+
+ public MethodVisitor visitMethod(int access, String name, String desc, String signature,
+ String[] exceptions) {
+
+ // Visit the method and dump its stub.
+ return new MethodSourcer(mOutput, mClassName, access, name, desc, signature, exceptions);
+ }
+
+ public void visitInnerClass(String name, String outerName, String innerName, int access) {
+ // Skip inner classes. This just indicates there's an inner class definition but
+ // they are visited at the top level as separate classes.
+ }
+
+ public void visitOuterClass(String owner, String name, String desc) {
+ // Skip outer classes.
+ }
+
+ public void visitSource(String source, String debug) {
+ // Skip source information.
+ }
+
+}
diff --git a/tools/mkstubs/src/com/android/mkstubs/sourcer/MethodSourcer.java b/tools/mkstubs/src/com/android/mkstubs/sourcer/MethodSourcer.java
new file mode 100644
index 0000000..d474e4b
--- /dev/null
+++ b/tools/mkstubs/src/com/android/mkstubs/sourcer/MethodSourcer.java
@@ -0,0 +1,235 @@
+/*
+ * Copyright (C) 2009 The Android Open Source Project
+ *
+ * Licensed under the Apache License, Version 2.0 (the "License");
+ * you may not use this file except in compliance with the License.
+ * You may obtain a copy of the License at
+ *
+ * http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing, software
+ * distributed under the License is distributed on an "AS IS" BASIS,
+ * WITHOUT WARRANTIES 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.mkstubs.sourcer;
+
+import org.objectweb.asm.AnnotationVisitor;
+import org.objectweb.asm.Attribute;
+import org.objectweb.asm.Label;
+import org.objectweb.asm.MethodVisitor;
+import org.objectweb.asm.Type;
+import org.objectweb.asm.signature.SignatureReader;
+
+import java.util.ArrayList;
+
+/**
+ *
+ */
+class MethodSourcer implements MethodVisitor {
+
+ private final Output mOutput;
+ private final int mAccess;
+ private final String mClassName;
+ private final String mName;
+ private final String mDesc;
+ private final String mSignature;
+ private final String[] mExceptions;
+ private boolean mNeedDeclaration;
+ private boolean mIsConstructor;
+
+ public MethodSourcer(Output output, String className, int access, String name,
+ String desc, String signature, String[] exceptions) {
+ mOutput = output;
+ mClassName = className;
+ mAccess = access;
+ mName = name;
+ mDesc = desc;
+ mSignature = signature;
+ mExceptions = exceptions;
+
+ mNeedDeclaration = true;
+ mIsConstructor = "<init>".equals(name);
+ }
+
+ private void writeHeader() {
+ if (!mNeedDeclaration) {
+ return;
+ }
+
+ AccessSourcer as = new AccessSourcer(mOutput);
+ as.write(mAccess, AccessSourcer.IS_METHOD);
+
+ // preprocess the signature to get the return type and the arguments
+ SignatureSourcer sigSourcer = null;
+ if (mSignature != null) {
+ SignatureReader sigReader = new SignatureReader(mSignature);
+ sigSourcer = new SignatureSourcer();
+ sigReader.accept(sigSourcer);
+
+ if (sigSourcer.hasFormalsContent()) {
+ // dump formal template parameter definitions
+ mOutput.write(" %s", sigSourcer.formalsToString());
+ }
+ }
+
+ // output return type (constructor have no return type)
+ if (!mIsConstructor) {
+ // The signature overrides desc, if present
+ if (sigSourcer == null || sigSourcer.getReturnType() == null) {
+ mOutput.write(" %s", Type.getReturnType(mDesc).getClassName());
+
+ } else {
+ mOutput.write(" %s", sigSourcer.getReturnType().toString());
+ }
+ }
+
+ // output name
+ mOutput.write(" %s(", mIsConstructor ? mClassName : mName);
+
+ // output arguments. The signature overrides desc, if present
+ if (mSignature == null) {
+ Type[] types = Type.getArgumentTypes(mDesc);
+
+ for(int i = 0; i < types.length; i++) {
+ if (i > 0) {
+ mOutput.write(", ");
+ }
+ mOutput.write("%s arg%d", types[i].getClassName(), i);
+ }
+ } else {
+ ArrayList<SignatureSourcer> params = sigSourcer.getParameters();
+
+ for(int i = 0; i < params.size(); i++) {
+ if (i > 0) {
+ mOutput.write(", ");
+ }
+ mOutput.write("%s arg%d", params.get(i).toString(), i);
+ }
+ }
+ mOutput.write(")");
+
+ // output throwable exceptions
+ if (mExceptions != null && mExceptions.length > 0) {
+ mOutput.write(" throws ");
+
+ for (int i = 0; i < mExceptions.length; i++) {
+ if (i > 0) {
+ mOutput.write(", ");
+ }
+ mOutput.write(mExceptions[i].replace('/', '.'));
+ }
+ }
+
+ mOutput.write(" {\n");
+
+ mNeedDeclaration = false;
+ }
+
+ public void visitCode() {
+ writeHeader();
+
+ // write the stub itself
+ mOutput.write("throw new RuntimeException(\"Stub\");");
+ }
+
+ public void visitEnd() {
+ writeHeader();
+ mOutput.write("\n}\n");
+ }
+
+ public AnnotationVisitor visitAnnotation(String desc, boolean visible) {
+ mOutput.write("@%s", desc);
+ return new AnnotationSourcer(mOutput);
+ }
+
+ public AnnotationVisitor visitAnnotationDefault() {
+ // pass
+ return null;
+ }
+
+ public void visitAttribute(Attribute attr) {
+ mOutput.write("%s /* non-standard method attribute */ ", attr.type);
+ }
+
+ public void visitFieldInsn(int opcode, String owner, String name, String desc) {
+ // pass
+ }
+
+ public void visitFrame(int type, int local, Object[] local2, int stack, Object[] stack2) {
+ // pass
+ }
+
+ public void visitIincInsn(int var, int increment) {
+ // pass
+ }
+
+ public void visitInsn(int opcode) {
+ // pass
+ }
+
+ public void visitIntInsn(int opcode, int operand) {
+ // pass
+ }
+
+ public void visitJumpInsn(int opcode, Label label) {
+ // pass
+ }
+
+ public void visitLabel(Label label) {
+ // pass
+ }
+
+ public void visitLdcInsn(Object cst) {
+ // pass
+ }
+
+ public void visitLineNumber(int line, Label start) {
+ // pass
+ }
+
+ public void visitLocalVariable(String name, String desc, String signature,
+ Label start, Label end, int index) {
+ // pass
+ }
+
+ public void visitLookupSwitchInsn(Label dflt, int[] keys, Label[] labels) {
+ // pass
+ }
+
+ public void visitMaxs(int maxStack, int maxLocals) {
+ // pass
+ }
+
+ public void visitMethodInsn(int opcode, String owner, String name, String desc) {
+ // pass
+ }
+
+ public void visitMultiANewArrayInsn(String desc, int dims) {
+ // pass
+ }
+
+ public AnnotationVisitor visitParameterAnnotation(int parameter, String desc, boolean visible) {
+ // pass
+ return null;
+ }
+
+ public void visitTableSwitchInsn(int min, int max, Label dflt, Label[] labels) {
+ // pass
+ }
+
+ public void visitTryCatchBlock(Label start, Label end, Label handler, String type) {
+ // pass
+ }
+
+ public void visitTypeInsn(int opcode, String type) {
+ // pass
+ }
+
+ public void visitVarInsn(int opcode, int var) {
+ // pass
+ }
+
+}
diff --git a/tools/mkstubs/src/com/android/mkstubs/sourcer/Output.java b/tools/mkstubs/src/com/android/mkstubs/sourcer/Output.java
new file mode 100644
index 0000000..837cf95
--- /dev/null
+++ b/tools/mkstubs/src/com/android/mkstubs/sourcer/Output.java
@@ -0,0 +1,57 @@
+/*
+ * Copyright (C) 2009 The Android Open Source Project
+ *
+ * Licensed under the Apache License, Version 2.0 (the "License");
+ * you may not use this file except in compliance with the License.
+ * You may obtain a copy of the License at
+ *
+ * http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing, software
+ * distributed under the License is distributed on an "AS IS" BASIS,
+ * WITHOUT WARRANTIES 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.mkstubs.sourcer;
+
+import org.objectweb.asm.Type;
+
+import java.io.IOException;
+import java.io.Writer;
+
+/**
+ *
+ */
+public class Output {
+
+ private final Writer mWriter;
+
+ public Output(Writer writer) {
+ mWriter = writer;
+ }
+
+ public void write(String format, Object... args) {
+ try {
+ mWriter.write(String.format(format, args));
+ } catch (IOException e) {
+ throw new RuntimeException(e);
+ }
+ }
+
+ public void write(char c) {
+ write(Character.toString(c));
+ }
+
+ public void write(StringBuilder sb) {
+ write(sb.toString());
+ }
+
+
+ public String decodeDesc(String desc) {
+ return Type.getType(desc).getClassName();
+ }
+
+
+}
diff --git a/tools/mkstubs/src/com/android/mkstubs/sourcer/SignatureSourcer.java b/tools/mkstubs/src/com/android/mkstubs/sourcer/SignatureSourcer.java
new file mode 100644
index 0000000..6202528
--- /dev/null
+++ b/tools/mkstubs/src/com/android/mkstubs/sourcer/SignatureSourcer.java
@@ -0,0 +1,268 @@
+/*
+ * Copyright (C) 2009 The Android Open Source Project
+ *
+ * Licensed under the Apache License, Version 2.0 (the "License");
+ * you may not use this file except in compliance with the License.
+ * You may obtain a copy of the License at
+ *
+ * http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing, software
+ * distributed under the License is distributed on an "AS IS" BASIS,
+ * WITHOUT WARRANTIES 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.mkstubs.sourcer;
+
+import org.objectweb.asm.Type;
+import org.objectweb.asm.signature.SignatureReader;
+import org.objectweb.asm.signature.SignatureVisitor;
+import org.objectweb.asm.signature.SignatureWriter;
+
+import java.util.ArrayList;
+
+/**
+ * Note: most of the implementation is a duplicate of
+ * ASM's SignatureWriter with some slight variations.
+ * <p/>
+ * Note: When processing a method's signature, the signature order is the
+ * reverse of the source order, e.g. it is (parameters)return-type where
+ * we want to generate "return-type method-name (parameters)".
+ * So in this case the return-type and parameters are not output directly
+ * but are instead accumulated in internal variables.
+ */
+class SignatureSourcer implements SignatureVisitor {
+
+ /**
+ * Buffer used to construct the signature.
+ */
+ private final StringBuilder mBuf = new StringBuilder();
+
+ /**
+ * Buffer used to construct the formals signature.
+ */
+ private final StringBuilder mFormalsBuf = new StringBuilder();
+
+ /**
+ * Indicates if the signature is currently processing formal type parameters.
+ */
+ private boolean mWritingFormals;
+
+ /**
+ * Stack used to keep track of class types that have arguments. Each element
+ * of this stack is a boolean encoded in one bit. The top of the stack is
+ * the lowest order bit. Pushing false = *2, pushing true = *2+1, popping =
+ * /2.
+ */
+ private int mArgumentStack;
+
+ private SignatureSourcer mReturnType;
+
+ private SignatureSourcer mSuperClass;
+
+ private ArrayList<SignatureSourcer> mParameters = new ArrayList<SignatureSourcer>();
+
+
+
+ /**
+ * Constructs a new {@link SignatureWriter} object.
+ */
+ public SignatureSourcer() {
+ }
+
+ private StringBuilder getBuf() {
+ if (mWritingFormals) {
+ return mFormalsBuf;
+ } else {
+ return mBuf;
+ }
+ }
+
+ /**
+ * Contains the whole signature type when called by
+ * {@link SignatureReader#acceptType(SignatureVisitor)} or just the formals if
+ * called by {@link SignatureReader#accept(SignatureVisitor)}.
+ */
+ @Override
+ public String toString() {
+ return mBuf.toString();
+ }
+
+ /**
+ * Will be non-null if a return type was processed
+ * by {@link SignatureReader#accept(SignatureVisitor)}
+ */
+ public SignatureSourcer getReturnType() {
+ return mReturnType;
+ }
+
+ /**
+ * Will be non-empty if a parameters were processed
+ * by {@link SignatureReader#accept(SignatureVisitor)}
+ */
+ public ArrayList<SignatureSourcer> getParameters() {
+ return mParameters;
+ }
+
+ /**
+ * True if the signature contains formal type parameters, which are available
+ * via {@link #formalsToString()} after calling {@link SignatureReader#accept(SignatureVisitor)}
+ */
+ public boolean hasFormalsContent() {
+ return mFormalsBuf.length() > 0;
+ }
+
+ public String formalsToString() {
+ return mFormalsBuf.toString();
+ }
+
+ /**
+ * Will be non-null if a super class was processed
+ * by {@link SignatureReader#accept(SignatureVisitor)}
+ */
+ public SignatureSourcer getSuperClass() {
+ return mSuperClass;
+ }
+
+ // ------------------------------------------------------------------------
+ // Implementation of the SignatureVisitor interface
+ // ------------------------------------------------------------------------
+
+ public void visitFormalTypeParameter(final String name) {
+ if (!mWritingFormals) {
+ mWritingFormals = true;
+ getBuf().append('<');
+ } else {
+ getBuf().append(", ");
+ }
+ getBuf().append(name);
+ getBuf().append(" extends ");
+ }
+
+ public SignatureVisitor visitClassBound() {
+ // we don't differentiate between visiting a sub class or interface type
+ return this;
+ }
+
+ public SignatureVisitor visitInterfaceBound() {
+ // we don't differentiate between visiting a sub class or interface type
+ return this;
+ }
+
+ public SignatureVisitor visitSuperclass() {
+ endFormals();
+ SignatureSourcer sourcer = new SignatureSourcer();
+ assert mSuperClass == null;
+ mSuperClass = sourcer;
+ return sourcer;
+ }
+
+ public SignatureVisitor visitInterface() {
+ return this;
+ }
+
+ public SignatureVisitor visitParameterType() {
+ endFormals();
+ SignatureSourcer sourcer = new SignatureSourcer();
+ mParameters.add(sourcer);
+ return sourcer;
+ }
+
+ public SignatureVisitor visitReturnType() {
+ endFormals();
+ SignatureSourcer sourcer = new SignatureSourcer();
+ assert mReturnType == null;
+ mReturnType = sourcer;
+ return sourcer;
+ }
+
+ public SignatureVisitor visitExceptionType() {
+ getBuf().append('^');
+ return this;
+ }
+
+ public void visitBaseType(final char descriptor) {
+ getBuf().append(Type.getType(Character.toString(descriptor)).getClassName());
+ }
+
+ public void visitTypeVariable(final String name) {
+ getBuf().append(name.replace('/', '.'));
+ }
+
+ public SignatureVisitor visitArrayType() {
+ getBuf().append('[');
+ return this;
+ }
+
+ public void visitClassType(final String name) {
+ getBuf().append(name.replace('/', '.'));
+ mArgumentStack *= 2;
+ }
+
+ public void visitInnerClassType(final String name) {
+ endArguments();
+ getBuf().append('.');
+ getBuf().append(name.replace('/', '.'));
+ mArgumentStack *= 2;
+ }
+
+ public void visitTypeArgument() {
+ if (mArgumentStack % 2 == 0) {
+ ++mArgumentStack;
+ getBuf().append('<');
+ } else {
+ getBuf().append(", ");
+ }
+ getBuf().append('*');
+ }
+
+ public SignatureVisitor visitTypeArgument(final char wildcard) {
+ if (mArgumentStack % 2 == 0) {
+ ++mArgumentStack;
+ getBuf().append('<');
+ } else {
+ getBuf().append(", ");
+ }
+ if (wildcard != '=') {
+ if (wildcard == '+') {
+ getBuf().append("? extends ");
+ } else if (wildcard == '-') {
+ getBuf().append("? super ");
+ } else {
+ // can this happen?
+ getBuf().append(wildcard);
+ }
+ }
+ return this;
+ }
+
+ public void visitEnd() {
+ endArguments();
+ }
+
+ // ------------------------------------------------------------------------
+ // Utility methods
+ // ------------------------------------------------------------------------
+
+ /**
+ * Ends the formal type parameters section of the signature.
+ */
+ private void endFormals() {
+ if (mWritingFormals) {
+ getBuf().append('>');
+ mWritingFormals = false;
+ }
+ }
+
+ /**
+ * Ends the type arguments of a class or inner class type.
+ */
+ private void endArguments() {
+ if (mArgumentStack % 2 != 0) {
+ getBuf().append('>');
+ }
+ mArgumentStack /= 2;
+ }
+}
diff --git a/tools/mkstubs/tests/com/android/mkstubs/AsmGeneratorTest.java b/tools/mkstubs/tests/com/android/mkstubs/AsmGeneratorTest.java
new file mode 100644
index 0000000..dd079d5
--- /dev/null
+++ b/tools/mkstubs/tests/com/android/mkstubs/AsmGeneratorTest.java
@@ -0,0 +1,56 @@
+/*
+ * Copyright (C) 2009 The Android Open Source Project
+ *
+ * Licensed under the Apache License, Version 2.0 (the "License");
+ * you may not use this file except in compliance with the License.
+ * You may obtain a copy of the License at
+ *
+ * http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing, software
+ * distributed under the License is distributed on an "AS IS" BASIS,
+ * WITHOUT WARRANTIES 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.mkstubs;
+
+
+import org.junit.After;
+import org.junit.Assert;
+import org.junit.Before;
+import org.junit.Test;
+import org.objectweb.asm.ClassReader;
+
+import java.io.StringWriter;
+import java.util.ArrayList;
+
+/**
+ *
+ */
+public class AsmGeneratorTest {
+
+ private AsmGenerator mGen;
+
+ @Before
+ public void setUp() throws Exception {
+ mGen = new AsmGenerator();
+ }
+
+ @After
+ public void tearDown() throws Exception {
+ }
+
+
+ @Test
+ public void testDumpClass() throws Exception {
+ StringWriter sw = new StringWriter();
+ ClassReader cr = new ClassReader("data/TestBaseClass");
+
+ mGen.dumpClass(sw, cr, new ArrayList<String>());
+
+ String s = sw.toString();
+ Assert.assertNotNull(s);
+ }
+}
diff --git a/tools/mkstubs/tests/com/android/mkstubs/FilterClassAdapterTest.java b/tools/mkstubs/tests/com/android/mkstubs/FilterClassAdapterTest.java
new file mode 100644
index 0000000..d27c1d5
--- /dev/null
+++ b/tools/mkstubs/tests/com/android/mkstubs/FilterClassAdapterTest.java
@@ -0,0 +1,33 @@
+/*
+ * Copyright (C) 2009 The Android Open Source Project
+ *
+ * Licensed under the Apache License, Version 2.0 (the "License");
+ * you may not use this file except in compliance with the License.
+ * You may obtain a copy of the License at
+ *
+ * http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing, software
+ * distributed under the License is distributed on an "AS IS" BASIS,
+ * WITHOUT WARRANTIES 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.mkstubs;
+
+
+import org.junit.After;
+import org.junit.Before;
+
+public class FilterClassAdapterTest {
+
+ @Before
+ public void setUp() throws Exception {
+ }
+
+ @After
+ public void tearDown() throws Exception {
+ }
+
+}
diff --git a/tools/mkstubs/tests/com/android/mkstubs/sourcer/AccessSourcerTest.java b/tools/mkstubs/tests/com/android/mkstubs/sourcer/AccessSourcerTest.java
new file mode 100644
index 0000000..58022a9
--- /dev/null
+++ b/tools/mkstubs/tests/com/android/mkstubs/sourcer/AccessSourcerTest.java
@@ -0,0 +1,63 @@
+/*
+ * Copyright (C) 2009 The Android Open Source Project
+ *
+ * Licensed under the Apache License, Version 2.0 (the "License");
+ * you may not use this file except in compliance with the License.
+ * You may obtain a copy of the License at
+ *
+ * http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing, software
+ * distributed under the License is distributed on an "AS IS" BASIS,
+ * WITHOUT WARRANTIES 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.mkstubs.sourcer;
+
+
+import org.junit.After;
+import org.junit.Assert;
+import org.junit.Before;
+import org.junit.Test;
+import org.objectweb.asm.Opcodes;
+
+import java.io.StringWriter;
+
+public class AccessSourcerTest {
+
+ private StringWriter mWriter;
+ private AccessSourcer mSourcer;
+
+
+ @Before
+ public void setUp() throws Exception {
+ mWriter = new StringWriter();
+ mSourcer = new AccessSourcer(new Output(mWriter));
+ }
+
+ @After
+ public void tearDown() throws Exception {
+ mWriter = null;
+ mSourcer = null;
+ }
+
+ @Test
+ public void testAbstractPublic() throws Exception {
+ mSourcer.write(Opcodes.ACC_ABSTRACT | Opcodes.ACC_PUBLIC, AccessSourcer.IS_CLASS);
+
+ String s = mWriter.toString();
+ Assert.assertEquals("public abstract", s);
+ }
+
+ @Test
+ public void testPrivateFinalStatic() throws Exception {
+ mSourcer.write(Opcodes.ACC_PRIVATE | Opcodes.ACC_FINAL | Opcodes.ACC_STATIC,
+ AccessSourcer.IS_METHOD);
+
+ String s = mWriter.toString();
+ Assert.assertEquals("private static final", s);
+ }
+
+}
diff --git a/tools/mkstubs/tests/com/android/mkstubs/sourcer/FieldSourcerTest.java b/tools/mkstubs/tests/com/android/mkstubs/sourcer/FieldSourcerTest.java
new file mode 100644
index 0000000..a45a47b
--- /dev/null
+++ b/tools/mkstubs/tests/com/android/mkstubs/sourcer/FieldSourcerTest.java
@@ -0,0 +1,75 @@
+/*
+ * Copyright (C) 2009 The Android Open Source Project
+ *
+ * Licensed under the Apache License, Version 2.0 (the "License");
+ * you may not use this file except in compliance with the License.
+ * You may obtain a copy of the License at
+ *
+ * http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing, software
+ * distributed under the License is distributed on an "AS IS" BASIS,
+ * WITHOUT WARRANTIES 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.mkstubs.sourcer;
+
+
+import org.junit.After;
+import org.junit.Assert;
+import org.junit.Before;
+import org.junit.Test;
+import org.objectweb.asm.Opcodes;
+
+import java.io.StringWriter;
+
+/**
+ *
+ */
+public class FieldSourcerTest {
+
+ private StringWriter mWriter;
+
+ @Before
+ public void setUp() throws Exception {
+ mWriter = new StringWriter();
+ }
+
+ @After
+ public void tearDown() throws Exception {
+ mWriter = null;
+ }
+
+ @Test
+ public void testStringField() throws Exception {
+
+ FieldSourcer fs = new FieldSourcer(new Output(mWriter),
+ Opcodes.ACC_PUBLIC, // access
+ "mArg", // name
+ "Ljava/lang/String;", // desc
+ null // signature
+ );
+ fs.visitEnd();
+
+ String s = mWriter.toString();
+ Assert.assertEquals("public java.lang.String mArg;\n", s);
+ }
+
+ @Test
+ public void testTemplateTypeField() throws Exception {
+
+ FieldSourcer fs = new FieldSourcer(new Output(mWriter),
+ Opcodes.ACC_PRIVATE | Opcodes.ACC_FINAL, // access
+ "mList", // name
+ "Ljava/util/ArrayList;", // desc
+ "Ljava/util/ArrayList<Ljava/lang/String;>;" // signature
+ );
+ fs.visitEnd();
+
+ String s = mWriter.toString();
+ Assert.assertEquals("private final java.util.ArrayList<java.lang.String> mList;\n", s);
+ }
+
+}
diff --git a/tools/mkstubs/tests/com/android/mkstubs/sourcer/JavaSourcerTest.java b/tools/mkstubs/tests/com/android/mkstubs/sourcer/JavaSourcerTest.java
new file mode 100644
index 0000000..250da2a
--- /dev/null
+++ b/tools/mkstubs/tests/com/android/mkstubs/sourcer/JavaSourcerTest.java
@@ -0,0 +1,139 @@
+/*
+ * Copyright (C) 2009 The Android Open Source Project
+ *
+ * Licensed under the Apache License, Version 2.0 (the "License");
+ * you may not use this file except in compliance with the License.
+ * You may obtain a copy of the License at
+ *
+ * http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing, software
+ * distributed under the License is distributed on an "AS IS" BASIS,
+ * WITHOUT WARRANTIES 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.mkstubs.sourcer;
+
+
+import org.junit.After;
+import org.junit.Before;
+import org.junit.Test;
+import org.objectweb.asm.ClassReader;
+
+import java.io.StringWriter;
+
+/**
+ *
+ */
+public class JavaSourcerTest extends TestHelper {
+
+ /**
+ * @throws java.lang.Exception
+ */
+ @Before
+ public void setUp() throws Exception {
+ }
+
+ /**
+ * @throws java.lang.Exception
+ */
+ @After
+ public void tearDown() throws Exception {
+ }
+
+ @Test
+ public void testBaseClassSource() throws Exception {
+ StringWriter sw = new StringWriter();
+ ClassReader cr = new ClassReader("data/TestBaseClass");
+
+ JavaSourcer jw = new JavaSourcer(new Output(sw));
+ cr.accept(jw, 0);
+
+ assertSourceEquals(
+ "package data;\n" +
+ "public class TestBaseClass extends java.lang.Object implements java.lang.Runnable {\n" +
+ "\n" +
+ " private final java.lang.String mArg;\n" +
+ " \n" +
+ " public TestBaseClass() {\n" +
+ " throw new RuntimeException(\"Stub\");" +
+ " }\n" +
+ " public TestBaseClass(java.lang.String arg0) {\n" +
+ " throw new RuntimeException(\"Stub\");" +
+ " }\n" +
+ " public java.lang.String getArg() {\n" +
+ " throw new RuntimeException(\"Stub\");" +
+ " }\n" +
+ " public void run() {\n" +
+ " throw new RuntimeException(\"Stub\");" +
+ " }\n" +
+ "}",
+ sw.toString());
+ }
+
+ @Test
+ public void testInnerClassSource() throws Exception {
+ StringWriter sw = new StringWriter();
+ ClassReader cr = new ClassReader("data/TestInnerClass");
+
+ JavaSourcer jw = new JavaSourcer(new Output(sw));
+ cr.accept(jw, 0);
+
+ assertSourceEquals(
+ "package data;\n" +
+ "public class TestInnerClass extends java.lang.Object {\n" +
+ " private final java.lang.String mArg;\n" +
+ " public TestInnerClass() {\n" +
+ " throw new RuntimeException(\"Stub\");\n" +
+ " }\n" +
+ " public TestInnerClass(java.lang.String arg0) {\n" +
+ " throw new RuntimeException(\"Stub\");\n" +
+ " }\n" +
+ " public java.lang.String getArg() {\n" +
+ " throw new RuntimeException(\"Stub\");\n" +
+ " }\n" +
+ " public data.TestInnerClass$InnerPubClass getInnerPubClass() {\n" +
+ " throw new RuntimeException(\"Stub\");\n" +
+ " }\n" +
+ "}",
+ sw.toString());
+ }
+
+ @Test
+ public void testTemplateClassSource() throws Exception {
+ StringWriter sw = new StringWriter();
+ ClassReader cr = new ClassReader("data/TestTemplateClass");
+
+ JavaSourcer jw = new JavaSourcer(new Output(sw));
+ cr.accept(jw, 0);
+
+ assertSourceEquals(
+ "package data;\n" +
+ "public class TestTemplateClass<T extends java.io.InputStream, U extends java.lang.Object> extends java.lang.Object {\n" +
+ " private final java.util.Map<T, U> mMap_T_U;\n" +
+ " public java.util.Map<java.util.ArrayList<T>, java.util.Map<java.lang.String, java.util.ArrayList<U>>> mMap_T_S_U;\n" +
+ " public TestTemplateClass() {\n" +
+ " throw new RuntimeException(\"Stub\");\n" +
+ " }\n" +
+ " public java.util.Map<T, U> getMap_T_U() {\n" +
+ " throw new RuntimeException(\"Stub\");\n" +
+ " }\n" +
+ " public java.util.Map<java.util.ArrayList<T>, java.util.Map<java.lang.String, java.util.ArrayList<U>>> getMap_T_S_U() {\n" +
+ " throw new RuntimeException(\"Stub\");\n" +
+ " }\n" +
+ " public void draw(java.util.List<? extends org.w3c.dom.css.Rect> arg0) {\n" +
+ " throw new RuntimeException(\"Stub\");\n" +
+ " }\n" +
+ " public static <T extends java.lang.Comparable<? super T>> void sort(java.util.List<T> arg0) {\n" +
+ " throw new RuntimeException(\"Stub\");\n" +
+ " }\n" +
+ " public <X extends T, Y extends java.lang.Object> void getMap(java.util.List<T> arg0, java.util.Map<T, U> arg1, java.util.Map<X, java.util.Set<? super Y>> arg2) {\n" +
+ " throw new RuntimeException(\"Stub\");\n" +
+ " }\n" +
+ "}",
+ sw.toString());
+ }
+
+}
diff --git a/tools/mkstubs/tests/com/android/mkstubs/sourcer/MethodSourcerTest.java b/tools/mkstubs/tests/com/android/mkstubs/sourcer/MethodSourcerTest.java
new file mode 100644
index 0000000..ca3d2da
--- /dev/null
+++ b/tools/mkstubs/tests/com/android/mkstubs/sourcer/MethodSourcerTest.java
@@ -0,0 +1,94 @@
+/*
+ * Copyright (C) 2009 The Android Open Source Project
+ *
+ * Licensed under the Apache License, Version 2.0 (the "License");
+ * you may not use this file except in compliance with the License.
+ * You may obtain a copy of the License at
+ *
+ * http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing, software
+ * distributed under the License is distributed on an "AS IS" BASIS,
+ * WITHOUT WARRANTIES 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.mkstubs.sourcer;
+
+
+import org.junit.After;
+import org.junit.Before;
+import org.junit.Test;
+import org.objectweb.asm.Opcodes;
+
+import java.io.StringWriter;
+
+/**
+ *
+ */
+public class MethodSourcerTest extends TestHelper {
+
+ private StringWriter mWriter;
+ private Output mOutput;
+
+ @Before
+ public void setUp() throws Exception {
+ mWriter = new StringWriter();
+ mOutput = new Output(mWriter);
+ }
+
+ @After
+ public void tearDown() throws Exception {
+ mWriter = null;
+ }
+
+ @Test
+ public void testVoid() {
+ MethodSourcer m = new MethodSourcer(mOutput,
+ "foo", //classname
+ Opcodes.ACC_PUBLIC, //access
+ "testVoid", //name
+ "()V", //desc
+ null, //signature
+ null); //exception
+ m.visitEnd();
+
+ assertSourceEquals(
+ "public void testVoid() { }",
+ mWriter.toString());
+ }
+
+ @Test
+ public void testVoidThrow() {
+ MethodSourcer m = new MethodSourcer(mOutput,
+ "foo", //classname
+ Opcodes.ACC_PUBLIC, //access
+ "testVoid", //name
+ "()V", //desc
+ null, //signature
+ new String[] { "java/lang/Exception" }); //exception
+ m.visitEnd();
+
+ assertSourceEquals(
+ "public void testVoid() throws java.lang.Exception { }",
+ mWriter.toString());
+ }
+
+ @Test
+ public void testReturnMap() {
+ MethodSourcer m = new MethodSourcer(mOutput,
+ "foo", //classname
+ Opcodes.ACC_PUBLIC, //access
+ "getMap_T_U", //name
+ "()Ljava/util/Map;", //desc
+ "()Ljava/util/Map<TT;TU;>;", //signature
+ null); //exception
+ m.visitEnd();
+
+ assertSourceEquals(
+ "public java.util.Map<T, U> getMap_T_U() { }",
+ mWriter.toString());
+ }
+
+}
diff --git a/tools/mkstubs/tests/com/android/mkstubs/sourcer/SignatureSourcerTest.java b/tools/mkstubs/tests/com/android/mkstubs/sourcer/SignatureSourcerTest.java
new file mode 100644
index 0000000..5d9f26f
--- /dev/null
+++ b/tools/mkstubs/tests/com/android/mkstubs/sourcer/SignatureSourcerTest.java
@@ -0,0 +1,146 @@
+/*
+ * Copyright (C) 2009 The Android Open Source Project
+ *
+ * Licensed under the Apache License, Version 2.0 (the "License");
+ * you may not use this file except in compliance with the License.
+ * You may obtain a copy of the License at
+ *
+ * http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing, software
+ * distributed under the License is distributed on an "AS IS" BASIS,
+ * WITHOUT WARRANTIES 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.mkstubs.sourcer;
+
+
+import org.junit.After;
+import org.junit.Assert;
+import org.junit.Before;
+import org.junit.Test;
+import org.objectweb.asm.signature.SignatureReader;
+
+import java.util.ArrayList;
+
+/**
+ *
+ */
+public class SignatureSourcerTest {
+
+ private SignatureSourcer mSourcer;
+
+ @Before
+ public void setUp() throws Exception {
+ mSourcer = new SignatureSourcer();
+ }
+
+ @After
+ public void tearDown() throws Exception {
+ }
+
+ @Test
+ public void testReturnMapNoArgs() {
+ SignatureReader reader = new SignatureReader(
+ "()Ljava/util/Map<Ljava/util/ArrayList<TT;>;Ljava/util/Map<Ljava/lang/String;Ljava/util/ArrayList<TU;>;>;>;");
+ reader.accept(mSourcer);
+ String result = mSourcer.getReturnType().toString();
+
+ Assert.assertEquals(
+ "java.util.Map<java.util.ArrayList<T>, java.util.Map<java.lang.String, java.util.ArrayList<U>>>",
+ result);
+ }
+
+ @Test
+ public void testReturnVoid() {
+ SignatureReader reader = new SignatureReader(
+ "(Ljava/util/List<+Lorg/w3c/dom/css/Rect;>;)V");
+ reader.accept(mSourcer);
+ String result = mSourcer.getReturnType().toString();
+
+ Assert.assertEquals(
+ "void",
+ result);
+ }
+
+ @Test
+ public void testSimpleArg() {
+ SignatureReader reader = new SignatureReader(
+ "(Ljava/util/List<+Lorg/w3c/dom/css/Rect;>;)V");
+ reader.accept(mSourcer);
+
+ ArrayList<SignatureSourcer> params = mSourcer.getParameters();
+ Assert.assertNotNull(params);
+
+ String[] array = toStringArray(params);
+
+ Assert.assertArrayEquals(
+ new String[] { "java.util.List<? extends org.w3c.dom.css.Rect>" },
+ array);
+ }
+
+ @Test
+ public void testFormalParameters1() {
+ SignatureReader reader = new SignatureReader("<X:TT;Y:Ljava/lang/Object;>()V");
+ reader.accept(mSourcer);
+
+ Assert.assertTrue(mSourcer.hasFormalsContent());
+
+ String result = mSourcer.formalsToString();
+ Assert.assertEquals(
+ "<X extends T, Y extends java.lang.Object>",
+ result);
+ }
+
+ @Test
+ public void testFormalParameters2() {
+ SignatureReader reader = new SignatureReader("<T::Ljava/lang/Comparable<-TT;>;>(Ljava/util/List<TT;>;)V");
+ reader.accept(mSourcer);
+
+ Assert.assertTrue(mSourcer.hasFormalsContent());
+
+ String result = mSourcer.formalsToString();
+ Assert.assertEquals(
+ "<T extends java.lang.Comparable<? super T>>",
+ result);
+ }
+
+ @Test
+ public void testManyArgs() {
+ SignatureReader reader = new SignatureReader(
+ "<X:TT;Y:Ljava/lang/Object;>(Ljava/util/List<TT;>;Ljava/util/Map<TT;TU;>;Ljava/util/Map<TX;Ljava/util/Set<-TY;>;>;)V");
+ reader.accept(mSourcer);
+
+ Assert.assertTrue(mSourcer.hasFormalsContent());
+ String formals = mSourcer.formalsToString();
+ Assert.assertEquals(
+ "<X extends T, Y extends java.lang.Object>",
+ formals);
+
+ String result = mSourcer.getReturnType().toString();
+ Assert.assertEquals(
+ "void",
+ result);
+
+ ArrayList<SignatureSourcer> params = mSourcer.getParameters();
+ Assert.assertNotNull(params);
+
+ String[] array = toStringArray(params);
+
+ Assert.assertArrayEquals(
+ new String[] { "java.util.List<T>",
+ "java.util.Map<T, U>",
+ "java.util.Map<X, java.util.Set<? super Y>>" },
+ array);
+ }
+
+ private String[] toStringArray(ArrayList<?> params) {
+ String[] array = new String[params.size()];
+ for (int i = 0; i < params.size(); i++) {
+ array[i] = params.get(i).toString();
+ }
+ return array;
+ }
+}
diff --git a/tools/mkstubs/tests/com/android/mkstubs/sourcer/TestHelper.java b/tools/mkstubs/tests/com/android/mkstubs/sourcer/TestHelper.java
new file mode 100644
index 0000000..57bdee0
--- /dev/null
+++ b/tools/mkstubs/tests/com/android/mkstubs/sourcer/TestHelper.java
@@ -0,0 +1,37 @@
+/*
+ * Copyright (C) 2009 The Android Open Source Project
+ *
+ * Licensed under the Apache License, Version 2.0 (the "License");
+ * you may not use this file except in compliance with the License.
+ * You may obtain a copy of the License at
+ *
+ * http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing, software
+ * distributed under the License is distributed on an "AS IS" BASIS,
+ * WITHOUT WARRANTIES 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.mkstubs.sourcer;
+
+import org.junit.Assert;
+
+/**
+ *
+ */
+abstract class TestHelper {
+
+ /**
+ * Test source equality after normalizing all whitespace.
+ */
+ public void assertSourceEquals(String expected, String actual) {
+ String en = expected.replaceAll("[\\s]+", " ").trim();
+ String an = actual.replaceAll( "[\\s]+", " ").trim();
+
+ Assert.assertEquals(
+ String.format("Source comparison failure: expected:<%s> but was:<%s>", expected, actual),
+ en, an);
+ }
+}
diff --git a/tools/mkstubs/tests/data/TestBaseClass.java b/tools/mkstubs/tests/data/TestBaseClass.java
new file mode 100644
index 0000000..2b1b438
--- /dev/null
+++ b/tools/mkstubs/tests/data/TestBaseClass.java
@@ -0,0 +1,42 @@
+/*
+ * Copyright (C) 2009 The Android Open Source Project
+ *
+ * Licensed under the Apache License, Version 2.0 (the "License");
+ * you may not use this file except in compliance with the License.
+ * You may obtain a copy of the License at
+ *
+ * http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing, software
+ * distributed under the License is distributed on an "AS IS" BASIS,
+ * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+ * See the License for the specific language governing permissions and
+ * limitations under the License.
+ */
+
+package data;
+
+
+/**
+ *
+ */
+public class TestBaseClass implements Runnable {
+
+ private final String mArg;
+
+ public TestBaseClass() {
+ throw new RuntimeException("Stub");
+ }
+
+ public TestBaseClass(String arg) {
+ mArg = arg;
+ }
+
+ public String getArg() {
+ return mArg;
+ }
+
+ @SuppressWarnings("unused")
+ public void run() {
+ }
+}
diff --git a/tools/mkstubs/tests/data/TestInnerClass.java b/tools/mkstubs/tests/data/TestInnerClass.java
new file mode 100644
index 0000000..e19b969
--- /dev/null
+++ b/tools/mkstubs/tests/data/TestInnerClass.java
@@ -0,0 +1,52 @@
+/*
+ * Copyright (C) 2009 The Android Open Source Project
+ *
+ * Licensed under the Apache License, Version 2.0 (the "License");
+ * you may not use this file except in compliance with the License.
+ * You may obtain a copy of the License at
+ *
+ * http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing, software
+ * distributed under the License is distributed on an "AS IS" BASIS,
+ * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+ * See the License for the specific language governing permissions and
+ * limitations under the License.
+ */
+
+package data;
+
+
+/**
+ *
+ */
+public class TestInnerClass {
+
+ private final String mArg;
+
+ private class InnerPrivClass {
+ }
+
+ public class InnerPubClass {
+ }
+
+ private static final class InnerStaticClass {
+ }
+
+ public TestInnerClass() {
+ mArg = null;
+ }
+
+ public TestInnerClass(String arg) {
+ mArg = arg;
+ }
+
+ public String getArg() {
+ return mArg;
+ }
+
+ public InnerPubClass getInnerPubClass() {
+ return new InnerPubClass();
+ }
+
+}
diff --git a/tools/mkstubs/tests/data/TestTemplateClass.java b/tools/mkstubs/tests/data/TestTemplateClass.java
new file mode 100644
index 0000000..bcc04c0
--- /dev/null
+++ b/tools/mkstubs/tests/data/TestTemplateClass.java
@@ -0,0 +1,55 @@
+/*
+ * Copyright (C) 2009 The Android Open Source Project
+ *
+ * Licensed under the Apache License, Version 2.0 (the "License");
+ * you may not use this file except in compliance with the License.
+ * You may obtain a copy of the License at
+ *
+ * http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing, software
+ * distributed under the License is distributed on an "AS IS" BASIS,
+ * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+ * See the License for the specific language governing permissions and
+ * limitations under the License.
+ */
+
+package data;
+
+import org.w3c.dom.css.Rect;
+
+import java.io.InputStream;
+import java.util.ArrayList;
+import java.util.List;
+import java.util.Map;
+import java.util.Set;
+
+/**
+ *
+ */
+public class TestTemplateClass<T extends InputStream, U> {
+
+ private final Map<T, U> mMap_T_U = null;
+
+ public Map<ArrayList<T>, Map<String, ArrayList<U>>> mMap_T_S_U = null;
+
+ public TestTemplateClass() {
+ }
+
+ public Map<T, U> getMap_T_U() {
+ return mMap_T_U;
+ }
+
+ public Map<ArrayList<T>, Map<String, ArrayList<U>>> getMap_T_S_U() {
+ return mMap_T_S_U;
+ }
+
+ public void draw(List<? extends Rect> shape) {
+ }
+
+ public static <T extends Comparable<? super T>> void sort(List<T> list) {
+ }
+
+ public <X extends T, Y> void getMap(List<T> list, Map<T, U> tu, Map<X, Set<? super Y>> xy) {
+ }
+}