AI 146829: am: CL 146744 am: CL 146720 ADT Jar Stubber: generate stubbed jar.
  This also reorganizes some source so it's 90% refactoring.
  There will be more filtering to do in another CL.
  Original author: raphael
  Merged from: //branches/cupcake/...
  Original author: android-build

Automated import of CL 146829
diff --git a/tools/mkstubs/src/com/android/mkstubs/Main.java b/tools/mkstubs/src/com/android/mkstubs/Main.java
index 017b2f1..5c6e209 100644
--- a/tools/mkstubs/src/com/android/mkstubs/Main.java
+++ b/tools/mkstubs/src/com/android/mkstubs/Main.java
@@ -146,20 +146,15 @@
      
         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");
+        SourceGenerator src_gen = new SourceGenerator();
+        File dst_src_dir = new File(p.getOutputJarPath() + "_sources");
         dst_src_dir.mkdir();
-        gen.generateSource(dst_src_dir, classes, p.getExclusions());
+        src_gen.generateSource(dst_src_dir, classes, p.getExclusions());
         
+        // dump the stubbed jar
+        StubGenerator stub_gen = new StubGenerator();
+        File dst_jar = new File(p.getOutputJarPath());
+        stub_gen.generateStubbedJar(dst_jar, 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/AsmGenerator.java b/tools/mkstubs/src/com/android/mkstubs/SourceGenerator.java
similarity index 90%
rename from tools/mkstubs/src/com/android/mkstubs/AsmGenerator.java
rename to tools/mkstubs/src/com/android/mkstubs/SourceGenerator.java
index 3446b00..3eb19d6 100644
--- a/tools/mkstubs/src/com/android/mkstubs/AsmGenerator.java
+++ b/tools/mkstubs/src/com/android/mkstubs/SourceGenerator.java
@@ -16,7 +16,7 @@
 
 package com.android.mkstubs;
 
-import com.android.mkstubs.sourcer.JavaSourcer;
+import com.android.mkstubs.sourcer.ClassSourcer;
 import com.android.mkstubs.sourcer.Output;
 
 import org.objectweb.asm.ClassReader;
@@ -33,7 +33,7 @@
 /**
  * 
  */
-class AsmGenerator {
+class SourceGenerator {
 
     /**
      * Generate source for the stubbed classes, mostly for debug purposes.
@@ -51,7 +51,7 @@
             FileWriter fw = null;
             try {
                 fw = createWriter(baseDir, name);
-                dumpClass(fw, cr, exclusions);
+                visitClassSource(fw, cr, exclusions);
             } finally {
                 fw.close();
             }
@@ -79,10 +79,10 @@
      * Generate a source equivalent to the stubbed version of the class reader,
      * minus all exclusions
      */
-    void dumpClass(Writer fw, ClassReader cr, List<String> exclusions) {
+    void visitClassSource(Writer fw, ClassReader cr, List<String> exclusions) {
         System.out.println("Dump " + cr.getClassName());
         
-        ClassVisitor javaWriter = new JavaSourcer(new Output(fw));
+        ClassVisitor javaWriter = new ClassSourcer(new Output(fw));
         ClassVisitor filter = new FilterClassAdapter(javaWriter, exclusions);
         cr.accept(filter, 0 /*flags*/);
     }
diff --git a/tools/mkstubs/src/com/android/mkstubs/StubGenerator.java b/tools/mkstubs/src/com/android/mkstubs/StubGenerator.java
new file mode 100644
index 0000000..79855ac
--- /dev/null
+++ b/tools/mkstubs/src/com/android/mkstubs/StubGenerator.java
@@ -0,0 +1,103 @@
+/*
+ * 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.stubber.ClassStubber;
+
+import org.objectweb.asm.ClassReader;
+import org.objectweb.asm.ClassVisitor;
+import org.objectweb.asm.ClassWriter;
+
+import java.io.File;
+import java.io.FileOutputStream;
+import java.io.IOException;
+import java.util.List;
+import java.util.Map;
+import java.util.TreeMap;
+import java.util.Map.Entry;
+import java.util.jar.JarEntry;
+import java.util.jar.JarOutputStream;
+
+/**
+ * 
+ */
+class StubGenerator {
+
+    /**
+     * Generate source for the stubbed classes, mostly for debug purposes.
+     * @throws IOException 
+     */
+    public void generateStubbedJar(File destJar,
+            Map<String, ClassReader> classes,
+            List<String> exclusions) throws IOException {
+
+        TreeMap<String, byte[]> all = new TreeMap<String, byte[]>();
+
+        for (Entry<String, ClassReader> entry : classes.entrySet()) {
+            ClassReader cr = entry.getValue();
+            
+            byte[] b = visitClassStubber(cr, exclusions);
+            String name = classNameToEntryPath(cr.getClassName());
+            all.put(name, b);
+        }
+
+        createJar(new FileOutputStream(destJar), all);
+
+        System.out.println(String.format("Wrote %s", destJar.getPath()));
+    }
+
+    /**
+     * 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");
+    }
+
+    /**
+     * 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();
+    }
+    
+    byte[] visitClassStubber(ClassReader cr, List<String> exclusions) {
+        System.out.println("Stub " + cr.getClassName());
+
+        // Rewrite the new class from scratch, without reusing the constant pool from the
+        // original class reader.
+        ClassWriter cw = new ClassWriter(ClassWriter.COMPUTE_MAXS);
+
+        ClassVisitor stubWriter = new ClassStubber(cw);
+        ClassVisitor filter = new FilterClassAdapter(stubWriter, exclusions);
+        cr.accept(filter, 0 /*flags*/);
+        return cw.toByteArray();
+    }
+}
diff --git a/tools/mkstubs/src/com/android/mkstubs/sourcer/JavaSourcer.java b/tools/mkstubs/src/com/android/mkstubs/sourcer/ClassSourcer.java
similarity index 97%
rename from tools/mkstubs/src/com/android/mkstubs/sourcer/JavaSourcer.java
rename to tools/mkstubs/src/com/android/mkstubs/sourcer/ClassSourcer.java
index fb06c68..189e1a0 100644
--- a/tools/mkstubs/src/com/android/mkstubs/sourcer/JavaSourcer.java
+++ b/tools/mkstubs/src/com/android/mkstubs/sourcer/ClassSourcer.java
@@ -27,13 +27,13 @@
 /**
  * A class visitor that rewrites a java source
  */
-public class JavaSourcer implements ClassVisitor {
+public class ClassSourcer implements ClassVisitor {
 
     private final Output mOutput;
     private final AccessSourcer mAccessSourcer;
     private String mClassName;
 
-    public JavaSourcer(Output output) {
+    public ClassSourcer(Output output) {
         mOutput = output;
         mAccessSourcer = new AccessSourcer(mOutput);
     }
diff --git a/tools/mkstubs/src/com/android/mkstubs/stubber/ClassStubber.java b/tools/mkstubs/src/com/android/mkstubs/stubber/ClassStubber.java
new file mode 100644
index 0000000..dea0a52
--- /dev/null
+++ b/tools/mkstubs/src/com/android/mkstubs/stubber/ClassStubber.java
@@ -0,0 +1,86 @@
+/*
+ * 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.stubber;
+
+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;
+
+/**
+ * 
+ */
+public class ClassStubber extends ClassAdapter {
+
+    public ClassStubber(ClassVisitor cv) {
+        super(cv);
+    }
+
+    @Override
+    public void visit(int version, int access,
+            String name,
+            String signature,
+            String superName,
+            String[] interfaces) {
+        super.visit(version, access, name, signature, superName, interfaces);
+    }
+    
+    @Override
+    public void visitEnd() {
+        super.visitEnd();
+    }
+    
+    @Override
+    public AnnotationVisitor visitAnnotation(String desc, boolean visible) {
+        return super.visitAnnotation(desc, visible);
+    }
+    
+    @Override
+    public void visitAttribute(Attribute attr) {
+        super.visitAttribute(attr);
+    }
+    
+    @Override
+    public MethodVisitor visitMethod(int access, String name, String desc, String signature,
+            String[] exceptions) {
+        MethodVisitor mw = super.visitMethod(access, name, desc, signature, exceptions);
+        return new MethodStubber(mw, access, name, desc, signature, exceptions);
+    }
+    
+    @Override
+    public FieldVisitor visitField(int access, String name, String desc, String signature,
+            Object value) {
+        return super.visitField(access, name, desc, signature, value);
+    }
+    
+    @Override
+    public void visitInnerClass(String name, String outerName, String innerName, int access) {
+        super.visitInnerClass(name, outerName, innerName, access);
+    }
+    
+    @Override
+    public void visitOuterClass(String owner, String name, String desc) {
+        super.visitOuterClass(owner, name, desc);
+    }
+    
+    @Override
+    public void visitSource(String source, String debug) {
+        super.visitSource(source, debug);
+    }
+}
diff --git a/tools/mkstubs/src/com/android/mkstubs/stubber/MethodStubber.java b/tools/mkstubs/src/com/android/mkstubs/stubber/MethodStubber.java
new file mode 100644
index 0000000..3e200cd
--- /dev/null
+++ b/tools/mkstubs/src/com/android/mkstubs/stubber/MethodStubber.java
@@ -0,0 +1,179 @@
+/*
+ * 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.stubber;
+
+import org.objectweb.asm.AnnotationVisitor;
+import org.objectweb.asm.Attribute;
+import org.objectweb.asm.Label;
+import org.objectweb.asm.MethodAdapter;
+import org.objectweb.asm.MethodVisitor;
+import org.objectweb.asm.Opcodes;
+
+/**
+ * 
+ */
+public class MethodStubber extends MethodAdapter {
+
+    public MethodStubber(MethodVisitor mw,
+            int access, String name, String desc, String signature, String[] exceptions) {
+        super(mw);
+    }
+    
+    @Override
+    public void visitCode() {
+        Label l0 = new Label();
+        mv.visitLabel(l0);
+        mv.visitLineNumber(36, l0);
+        mv.visitTypeInsn(Opcodes.NEW, "java/lang/RuntimeException");
+        mv.visitInsn(Opcodes.DUP);
+        mv.visitLdcInsn("stub");
+        mv.visitMethodInsn(
+                Opcodes.INVOKESPECIAL,          // opcode
+                "java/lang/RuntimeException",   // owner
+                "<init>",                       // name
+                "(Ljava/lang/String;)V");       // desc
+        mv.visitInsn(Opcodes.ATHROW);
+        Label l1 = new Label();
+        mv.visitLabel(l1);
+        mv.visitLocalVariable(
+                "this",                                         // name
+                "Lcom/android/mkstubs/stubber/MethodStubber;",  // desc
+                null,                                           // signature
+                l0,                                             // label start
+                l1,                                             // label end
+                0);                                             // index
+        mv.visitMaxs(3, 1); // maxStack, maxLocals
+    }
+    
+    @Override
+    public void visitEnd() {
+        super.visitEnd();
+    }
+
+    @Override
+    public AnnotationVisitor visitAnnotation(String desc, boolean visible) {
+        return super.visitAnnotation(desc, visible);
+    }
+    
+    @Override
+    public AnnotationVisitor visitAnnotationDefault() {
+        return super.visitAnnotationDefault();
+    }
+ 
+    @Override
+    public void visitAttribute(Attribute attr) {
+        super.visitAttribute(attr);
+    }
+    
+    @Override
+    public AnnotationVisitor visitParameterAnnotation(int parameter, String desc, boolean visible) {
+        return super.visitParameterAnnotation(parameter, desc, visible);
+    }
+
+    // -- stuff that gets skipped
+    
+    @Override
+    public void visitFieldInsn(int opcode, String owner, String name, String desc) {
+        // skip
+    }
+    
+    @Override
+    public void visitFrame(int type, int local, Object[] local2, int stack, Object[] stack2) {
+        // skip
+    }
+    
+    @Override
+    public void visitIincInsn(int var, int increment) {
+        // skip
+    }
+    
+    @Override
+    public void visitInsn(int opcode) {
+        // skip
+    }
+    
+    @Override
+    public void visitIntInsn(int opcode, int operand) {
+        // skip
+    }
+    
+    @Override
+    public void visitJumpInsn(int opcode, Label label) {
+        // skip
+    }
+    
+    @Override
+    public void visitLabel(Label label) {
+        // skip
+    }
+    
+    @Override
+    public void visitLdcInsn(Object cst) {
+        // skip
+    }
+    
+    @Override
+    public void visitLineNumber(int line, Label start) {
+        // skip
+    }
+    
+    @Override
+    public void visitLocalVariable(String name, String desc, String signature,
+            Label start, Label end, int index) {
+        // skip
+    }
+    
+    @Override
+    public void visitLookupSwitchInsn(Label dflt, int[] keys, Label[] labels) {
+        // skip
+    }
+    
+    @Override
+    public void visitMaxs(int maxStack, int maxLocals) {
+        // skip
+    }
+    
+    @Override
+    public void visitMethodInsn(int opcode, String owner, String name, String desc) {
+        // skip
+    }
+    
+    @Override
+    public void visitMultiANewArrayInsn(String desc, int dims) {
+        // skip
+    }
+    
+    @Override
+    public void visitTableSwitchInsn(int min, int max, Label dflt, Label[] labels) {
+        // skip
+    }
+    
+    @Override
+    public void visitTryCatchBlock(Label start, Label end, Label handler, String type) {
+        // skip
+    }
+    
+    @Override
+    public void visitTypeInsn(int opcode, String type) {
+        // skip
+    }
+    
+    @Override
+    public void visitVarInsn(int opcode, int var) {
+        // skip
+    }
+}
diff --git a/tools/mkstubs/tests/com/android/mkstubs/AsmGeneratorTest.java b/tools/mkstubs/tests/com/android/mkstubs/SourceGeneratorTest.java
similarity index 87%
rename from tools/mkstubs/tests/com/android/mkstubs/AsmGeneratorTest.java
rename to tools/mkstubs/tests/com/android/mkstubs/SourceGeneratorTest.java
index dd079d5..c413d12 100644
--- a/tools/mkstubs/tests/com/android/mkstubs/AsmGeneratorTest.java
+++ b/tools/mkstubs/tests/com/android/mkstubs/SourceGeneratorTest.java
@@ -29,13 +29,13 @@
 /**
  * 
  */
-public class AsmGeneratorTest {
+public class SourceGeneratorTest {
 
-    private AsmGenerator mGen;
+    private SourceGenerator mGen;
 
     @Before
     public void setUp() throws Exception {
-        mGen = new AsmGenerator();
+        mGen = new SourceGenerator();
     }
 
     @After
@@ -48,7 +48,7 @@
         StringWriter sw = new StringWriter();
         ClassReader cr = new ClassReader("data/TestBaseClass");
         
-        mGen.dumpClass(sw, cr, new ArrayList<String>());
+        mGen.visitClassSource(sw, cr, new ArrayList<String>());
         
         String s = sw.toString();
         Assert.assertNotNull(s);
diff --git a/tools/mkstubs/tests/com/android/mkstubs/sourcer/JavaSourcerTest.java b/tools/mkstubs/tests/com/android/mkstubs/sourcer/ClassSourcerTest.java
similarity index 95%
rename from tools/mkstubs/tests/com/android/mkstubs/sourcer/JavaSourcerTest.java
rename to tools/mkstubs/tests/com/android/mkstubs/sourcer/ClassSourcerTest.java
index 250da2a..87a4ae8 100644
--- a/tools/mkstubs/tests/com/android/mkstubs/sourcer/JavaSourcerTest.java
+++ b/tools/mkstubs/tests/com/android/mkstubs/sourcer/ClassSourcerTest.java
@@ -27,7 +27,7 @@
 /**
  * 
  */
-public class JavaSourcerTest extends TestHelper {
+public class ClassSourcerTest extends TestHelper {
 
     /**
      * @throws java.lang.Exception
@@ -48,7 +48,7 @@
         StringWriter sw = new StringWriter();
         ClassReader cr = new ClassReader("data/TestBaseClass");
         
-        JavaSourcer jw = new JavaSourcer(new Output(sw));
+        ClassSourcer jw = new ClassSourcer(new Output(sw));
         cr.accept(jw, 0);
         
         assertSourceEquals(
@@ -78,7 +78,7 @@
         StringWriter sw = new StringWriter();
         ClassReader cr = new ClassReader("data/TestInnerClass");
         
-        JavaSourcer jw = new JavaSourcer(new Output(sw));
+        ClassSourcer jw = new ClassSourcer(new Output(sw));
         cr.accept(jw, 0);
         
         assertSourceEquals(
@@ -106,7 +106,7 @@
         StringWriter sw = new StringWriter();
         ClassReader cr = new ClassReader("data/TestTemplateClass");
         
-        JavaSourcer jw = new JavaSourcer(new Output(sw));
+        ClassSourcer jw = new ClassSourcer(new Output(sw));
         cr.accept(jw, 0);
         
         assertSourceEquals(