Home | History | Annotate | Download | only in obfuscate
      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.obfuscate;
     22 
     23 import java.io.*;
     24 
     25 
     26 /**
     27  * This class can parse mapping files and invoke a processor for each of the
     28  * mapping entries.
     29  *
     30  * @author Eric Lafortune
     31  */
     32 public class MappingReader
     33 {
     34     private final File mappingFile;
     35 
     36 
     37     public MappingReader(File mappingFile)
     38     {
     39         this.mappingFile = mappingFile;
     40     }
     41 
     42 
     43     /**
     44      * Reads the mapping file, presenting all of the encountered mapping entries
     45      * to the given processor.
     46      */
     47     public void pump(MappingProcessor mappingProcessor) throws IOException
     48     {
     49         LineNumberReader reader = new LineNumberReader(
     50                                   new BufferedReader(
     51                                   new FileReader(mappingFile)));
     52         try
     53         {
     54             String className = null;
     55 
     56             // Read the subsequent class mappings and class member mappings.
     57             while (true)
     58             {
     59                 String line = reader.readLine();
     60 
     61                 if (line == null)
     62                 {
     63                     break;
     64                 }
     65 
     66                 line = line.trim();
     67 
     68                 // The distinction between a class mapping and a class
     69                 // member mapping is the initial whitespace.
     70                 if (line.endsWith(":"))
     71                 {
     72                     // Process the class mapping and remember the class's
     73                     // old name.
     74                     className = processClassMapping(line, mappingProcessor);
     75                 }
     76                 else if (className != null)
     77                 {
     78                     // Process the class member mapping, in the context of the
     79                     // current old class name.
     80                     processClassMemberMapping(className, line, mappingProcessor);
     81                 }
     82             }
     83         }
     84         catch (IOException ex)
     85         {
     86             throw new IOException("Can't process mapping file (" + ex.getMessage() + ")");
     87         }
     88         finally
     89         {
     90             try
     91             {
     92                 reader.close();
     93             }
     94             catch (IOException ex)
     95             {
     96                 // This shouldn't happen.
     97             }
     98         }
     99     }
    100 
    101 
    102     /**
    103      * Parses the given line with a class mapping and processes the
    104      * results with the given mapping processor. Returns the old class name,
    105      * or null if any subsequent class member lines can be ignored.
    106      */
    107     private String processClassMapping(String           line,
    108                                        MappingProcessor mappingProcessor)
    109     {
    110         // See if we can parse "___ -> ___:", containing the original
    111         // class name and the new class name.
    112 
    113         int arrowIndex = line.indexOf("->");
    114         if (arrowIndex < 0)
    115         {
    116             return null;
    117         }
    118 
    119         int colonIndex = line.indexOf(':', arrowIndex + 2);
    120         if (colonIndex < 0)
    121         {
    122             return null;
    123         }
    124 
    125         // Extract the elements.
    126         String className    = line.substring(0, arrowIndex).trim();
    127         String newClassName = line.substring(arrowIndex + 2, colonIndex).trim();
    128 
    129         // Process this class name mapping.
    130         boolean interested = mappingProcessor.processClassMapping(className, newClassName);
    131 
    132         return interested ? className : null;
    133     }
    134 
    135 
    136     /**
    137      * Parses the given line with a class member mapping and processes the
    138      * results with the given mapping processor.
    139      */
    140     private void processClassMemberMapping(String           className,
    141                                            String           line,
    142                                            MappingProcessor mappingProcessor)
    143     {
    144         // See if we can parse "___:___:___ ___(___) -> ___",
    145         // containing the optional line numbers, the return type, the original
    146         // field/method name, optional arguments, and the new field/method name.
    147 
    148         int colonIndex1    =                           line.indexOf(':');
    149         int colonIndex2    = colonIndex1    < 0 ? -1 : line.indexOf(':', colonIndex1    + 1);
    150         int spaceIndex     =                           line.indexOf(' ', colonIndex2    + 2);
    151         int argumentIndex1 =                           line.indexOf('(', spaceIndex     + 1);
    152         int argumentIndex2 = argumentIndex1 < 0 ? -1 : line.indexOf(')', argumentIndex1 + 1);
    153         int arrowIndex     =                           line.indexOf("->", Math.max(spaceIndex, argumentIndex2) + 1);
    154 
    155         if (spaceIndex < 0 ||
    156             arrowIndex < 0)
    157         {
    158             return;
    159         }
    160 
    161         // Extract the elements.
    162         String type    = line.substring(colonIndex2 + 1, spaceIndex).trim();
    163         String name    = line.substring(spaceIndex + 1, argumentIndex1 >= 0 ? argumentIndex1 : arrowIndex).trim();
    164         String newName = line.substring(arrowIndex + 2).trim();
    165 
    166         // Process this class member mapping.
    167         if (type.length()    > 0 &&
    168             name.length()    > 0 &&
    169             newName.length() > 0)
    170         {
    171             // Is it a field or a method?
    172             if (argumentIndex2 < 0)
    173             {
    174                 mappingProcessor.processFieldMapping(className, type, name, newName);
    175             }
    176             else
    177             {
    178                 int firstLineNumber = 0;
    179                 int lastLineNumber  = 0;
    180 
    181                 if (colonIndex2 > 0)
    182                 {
    183                     firstLineNumber = Integer.parseInt(line.substring(0, colonIndex1).trim());
    184                     lastLineNumber  = Integer.parseInt(line.substring(colonIndex1 + 1, colonIndex2).trim());
    185                 }
    186 
    187                 String arguments = line.substring(argumentIndex1 + 1, argumentIndex2).trim();
    188 
    189                 mappingProcessor.processMethodMapping(className,
    190                                                       firstLineNumber,
    191                                                       lastLineNumber,
    192                                                       type,
    193                                                       name,
    194                                                       arguments,
    195                                                       newName);
    196             }
    197         }
    198     }
    199 }
    200