Home | History | Annotate | Download | only in mkstubs
      1 /*
      2  * Copyright (C) 2009 The Android Open Source Project
      3  *
      4  * Licensed under the Apache License, Version 2.0 (the "License");
      5  * you may not use this file except in compliance with the License.
      6  * You may obtain a copy of the License at
      7  *
      8  *      http://www.apache.org/licenses/LICENSE-2.0
      9  *
     10  * Unless required by applicable law or agreed to in writing, software
     11  * distributed under the License is distributed on an "AS IS" BASIS,
     12  * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
     13  * See the License for the specific language governing permissions and
     14  * limitations under the License.
     15  */
     16 
     17 package com.android.mkstubs;
     18 
     19 import com.android.mkstubs.Main.Logger;
     20 import com.android.mkstubs.stubber.ClassStubber;
     21 
     22 import org.objectweb.asm.ClassReader;
     23 import org.objectweb.asm.ClassVisitor;
     24 import org.objectweb.asm.ClassWriter;
     25 
     26 import java.io.File;
     27 import java.io.FileOutputStream;
     28 import java.io.IOException;
     29 import java.util.Map;
     30 import java.util.Map.Entry;
     31 import java.util.TreeMap;
     32 import java.util.jar.JarEntry;
     33 import java.util.jar.JarOutputStream;
     34 
     35 /**
     36  * Given a set of already filtered classes, this filters out all private members,
     37  * stubs the remaining classes and then generates a Jar out of them.
     38  * <p/>
     39  * This is an helper extracted for convenience. Callers just need to use
     40  * {@link #generateStubbedJar(File, Map, Filter)}.
     41  */
     42 class StubGenerator {
     43 
     44     private Logger mLog;
     45 
     46     public StubGenerator(Logger log) {
     47         mLog = log;
     48     }
     49 
     50     /**
     51      * Generate source for the stubbed classes, mostly for debug purposes.
     52      * @throws IOException
     53      */
     54     public void generateStubbedJar(File destJar,
     55             Map<String, ClassReader> classes,
     56             Filter filter) throws IOException {
     57 
     58         TreeMap<String, byte[]> all = new TreeMap<String, byte[]>();
     59 
     60         for (Entry<String, ClassReader> entry : classes.entrySet()) {
     61             ClassReader cr = entry.getValue();
     62 
     63             byte[] b = visitClassStubber(cr, filter);
     64             String name = classNameToEntryPath(cr.getClassName());
     65             all.put(name, b);
     66         }
     67 
     68         createJar(new FileOutputStream(destJar), all);
     69 
     70         mLog.debug("Wrote %s", destJar.getPath());
     71     }
     72 
     73     /**
     74      * Utility method that converts a fully qualified java name into a JAR entry path
     75      * e.g. for the input "android.view.View" it returns "android/view/View.class"
     76      */
     77     String classNameToEntryPath(String className) {
     78         return className.replaceAll("\\.", "/").concat(".class");
     79     }
     80 
     81     /**
     82      * Writes the JAR file.
     83      *
     84      * @param outStream The file output stream were to write the JAR.
     85      * @param all The map of all classes to output.
     86      * @throws IOException if an I/O error has occurred
     87      */
     88     void createJar(FileOutputStream outStream, Map<String,byte[]> all) throws IOException {
     89         JarOutputStream jar = new JarOutputStream(outStream);
     90         for (Entry<String, byte[]> entry : all.entrySet()) {
     91             String name = entry.getKey();
     92             JarEntry jar_entry = new JarEntry(name);
     93             jar.putNextEntry(jar_entry);
     94             jar.write(entry.getValue());
     95             jar.closeEntry();
     96         }
     97         jar.flush();
     98         jar.close();
     99     }
    100 
    101     byte[] visitClassStubber(ClassReader cr, Filter filter) {
    102         mLog.debug("Stub " + cr.getClassName());
    103 
    104         // Rewrite the new class from scratch, without reusing the constant pool from the
    105         // original class reader.
    106         ClassWriter cw = new ClassWriter(ClassWriter.COMPUTE_MAXS);
    107 
    108         ClassVisitor stubWriter = new ClassStubber(cw);
    109         ClassVisitor classFilter = new FilterClassAdapter(stubWriter, filter, mLog);
    110         cr.accept(classFilter, 0 /*flags*/);
    111         return cw.toByteArray();
    112     }
    113 }
    114