Home | History | Annotate | Download | only in proguard
      1 /*
      2  * ProGuard -- shrinking, optimization, obfuscation, and preverification
      3  *             of Java bytecode.
      4  *
      5  * Copyright (c) 2002-2014 Eric Lafortune (eric (at) graphics.cornell.edu)
      6  *
      7  * This program is free software; you can redistribute it and/or modify it
      8  * under the terms of the GNU General Public License as published by the Free
      9  * Software Foundation; either version 2 of the License, or (at your option)
     10  * any later version.
     11  *
     12  * This program is distributed in the hope that it will be useful, but WITHOUT
     13  * ANY WARRANTY; without even the implied warranty of MERCHANTABILITY or
     14  * FITNESS FOR A PARTICULAR PURPOSE. See the GNU General Public License for
     15  * more details.
     16  *
     17  * You should have received a copy of the GNU General Public License along
     18  * with this program; if not, write to the Free Software Foundation, Inc.,
     19  * 59 Temple Place, Suite 330, Boston, MA 02111-1307 USA
     20  */
     21 package proguard;
     22 
     23 import proguard.classfile.ClassPool;
     24 import proguard.classfile.util.ClassUtil;
     25 import proguard.io.*;
     26 
     27 import java.io.IOException;
     28 import java.util.*;
     29 
     30 /**
     31  * This class writes the output class files.
     32  *
     33  * @author Eric Lafortune
     34  */
     35 public class OutputWriter
     36 {
     37     private final Configuration configuration;
     38 
     39 
     40     /**
     41      * Creates a new OutputWriter to write output class files as specified by
     42      * the given configuration.
     43      */
     44     public OutputWriter(Configuration configuration)
     45     {
     46         this.configuration = configuration;
     47     }
     48 
     49 
     50     /**
     51      * Writes the given class pool to class files, based on the current
     52      * configuration.
     53      */
     54     public void execute(ClassPool programClassPool) throws IOException
     55     {
     56         ClassPath programJars = configuration.programJars;
     57 
     58         int firstInputIndex = 0;
     59         int lastInputIndex  = 0;
     60 
     61         // Go over all program class path entries.
     62         for (int index = 0; index < programJars.size(); index++)
     63         {
     64             // Is it an input entry?
     65             ClassPathEntry entry = programJars.get(index);
     66             if (!entry.isOutput())
     67             {
     68                 // Remember the index of the last input entry.
     69                 lastInputIndex = index;
     70             }
     71             else
     72             {
     73                 // Check if this the last output entry in a series.
     74                 int nextIndex = index + 1;
     75                 if (nextIndex == programJars.size() ||
     76                     !programJars.get(nextIndex).isOutput())
     77                 {
     78                     // Write the processed input entries to the output entries.
     79                     writeOutput(programClassPool,
     80                                 programJars,
     81                                 firstInputIndex,
     82                                 lastInputIndex + 1,
     83                                 nextIndex);
     84 
     85                     // Start with the next series of input entries.
     86                     firstInputIndex = nextIndex;
     87                 }
     88             }
     89         }
     90     }
     91 
     92 
     93     /**
     94      * Transfers the specified input jars to the specified output jars.
     95      */
     96     private void writeOutput(ClassPool programClassPool,
     97                              ClassPath classPath,
     98                              int       fromInputIndex,
     99                              int       fromOutputIndex,
    100                              int       toOutputIndex)
    101     throws IOException
    102     {
    103         try
    104         {
    105             // Construct the writer that can write jars, wars, ears, zips, and
    106             // directories, cascading over the specified output entries.
    107             DataEntryWriter writer =
    108                 DataEntryWriterFactory.createDataEntryWriter(classPath,
    109                                                              fromOutputIndex,
    110                                                              toOutputIndex);
    111 
    112             // The writer will be used to write possibly obfuscated class files.
    113             DataEntryReader classRewriter =
    114                 new ClassRewriter(programClassPool, writer);
    115 
    116             // The writer will also be used to write resource files.
    117             DataEntryReader resourceCopier =
    118                 new DataEntryCopier(writer);
    119 
    120             DataEntryReader resourceRewriter = resourceCopier;
    121 
    122             // Adapt resource file contents and names, if necessary.
    123             if (configuration.obfuscate)
    124             {
    125                 // Wrap the resource writer with a filter and a data entry
    126                 // rewriter, if required.
    127                 if (configuration.adaptResourceFileContents != null)
    128                 {
    129                     resourceRewriter =
    130                         new NameFilter(configuration.adaptResourceFileContents,
    131                         new NameFilter("META-INF/MANIFEST.MF,META-INF/*.SF",
    132                             new ManifestRewriter(programClassPool, writer),
    133                             new DataEntryRewriter(programClassPool, writer)),
    134                         resourceRewriter);
    135                 }
    136 
    137                 // Wrap the resource writer with a filter and a data entry
    138                 // renamer, if required.
    139                 if (configuration.adaptResourceFileNames != null)
    140                 {
    141                     Map packagePrefixMap = createPackagePrefixMap(programClassPool);
    142 
    143                     resourceRewriter =
    144                         new NameFilter(configuration.adaptResourceFileNames,
    145                         new DataEntryObfuscator(programClassPool,
    146                                                 packagePrefixMap,
    147                                                 resourceRewriter),
    148                         resourceRewriter);
    149                 }
    150             }
    151 
    152             DataEntryReader directoryRewriter = null;
    153 
    154             // Wrap the directory writer with a filter and a data entry renamer,
    155             // if required.
    156             if (configuration.keepDirectories != null)
    157             {
    158                 Map packagePrefixMap = createPackagePrefixMap(programClassPool);
    159 
    160                 directoryRewriter =
    161                     new NameFilter(configuration.keepDirectories,
    162                     new DataEntryRenamer(packagePrefixMap,
    163                                          resourceCopier,
    164                                          resourceCopier));
    165             }
    166 
    167             // Create the reader that can write class files and copy directories
    168             // and resource files to the main writer.
    169             DataEntryReader reader =
    170                 new ClassFilter(    classRewriter,
    171                 new DirectoryFilter(directoryRewriter,
    172                                     resourceRewriter));
    173 
    174             // Go over the specified input entries and write their processed
    175             // versions.
    176             new InputReader(configuration).readInput("  Copying resources from program ",
    177                                                      classPath,
    178                                                      fromInputIndex,
    179                                                      fromOutputIndex,
    180                                                      reader);
    181 
    182             // Close all output entries.
    183             writer.close();
    184         }
    185         catch (IOException ex)
    186         {
    187             throw (IOException)new IOException("Can't write [" + classPath.get(fromOutputIndex).getName() + "] (" + ex.getMessage() + ")").initCause(ex);
    188         }
    189     }
    190 
    191 
    192     /**
    193      * Creates a map of old package prefixes to new package prefixes, based on
    194      * the given class pool.
    195      */
    196     private static Map createPackagePrefixMap(ClassPool classPool)
    197     {
    198         Map packagePrefixMap = new HashMap();
    199 
    200         Iterator iterator = classPool.classNames();
    201         while (iterator.hasNext())
    202         {
    203             String className     = (String)iterator.next();
    204             String packagePrefix = ClassUtil.internalPackagePrefix(className);
    205 
    206             String mappedNewPackagePrefix = (String)packagePrefixMap.get(packagePrefix);
    207             if (mappedNewPackagePrefix == null ||
    208                 !mappedNewPackagePrefix.equals(packagePrefix))
    209             {
    210                 String newClassName     = classPool.getClass(className).getName();
    211                 String newPackagePrefix = ClassUtil.internalPackagePrefix(newClassName);
    212 
    213                 packagePrefixMap.put(packagePrefix, newPackagePrefix);
    214             }
    215         }
    216 
    217         return packagePrefixMap;
    218     }
    219 }
    220