001: /*
002: * ProGuard -- shrinking, optimization, obfuscation, and preverification
003: * of Java bytecode.
004: *
005: * Copyright (c) 2002-2007 Eric Lafortune (eric@graphics.cornell.edu)
006: *
007: * This program is free software; you can redistribute it and/or modify it
008: * under the terms of the GNU General Public License as published by the Free
009: * Software Foundation; either version 2 of the License, or (at your option)
010: * any later version.
011: *
012: * This program is distributed in the hope that it will be useful, but WITHOUT
013: * ANY WARRANTY; without even the implied warranty of MERCHANTABILITY or
014: * FITNESS FOR A PARTICULAR PURPOSE. See the GNU General Public License for
015: * more details.
016: *
017: * You should have received a copy of the GNU General Public License along
018: * with this program; if not, write to the Free Software Foundation, Inc.,
019: * 59 Temple Place, Suite 330, Boston, MA 02111-1307 USA
020: */
021: package proguard.obfuscate;
022:
023: import java.io.*;
024:
025: /**
026: * This class can parse mapping files and invoke a processor for each of the
027: * mapping entries.
028: *
029: * @author Eric Lafortune
030: */
031: public class MappingReader {
032: private final File mappingFile;
033:
034: public MappingReader(File mappingFile) {
035: this .mappingFile = mappingFile;
036: }
037:
038: /**
039: * Reads the mapping file, presenting all of the encountered mapping entries
040: * to the given processor.
041: */
042: public void pump(MappingProcessor mappingProcessor)
043: throws IOException {
044: LineNumberReader reader = new LineNumberReader(
045: new BufferedReader(new FileReader(mappingFile)));
046: try {
047: String className = null;
048:
049: // Read the subsequent class mappings and class member mappings.
050: while (true) {
051: String line = reader.readLine();
052:
053: if (line == null) {
054: break;
055: }
056:
057: // The distinction between a class mapping and a class
058: // member mapping is the initial whitespace.
059: if (!line.startsWith(" ")) {
060: // Process the class mapping and remember the class's
061: // old name.
062: className = processClassMapping(line,
063: mappingProcessor);
064: } else if (className != null) {
065: // Process the class member mapping, in the context of the
066: // current old class name.
067: processClassMemberMapping(className, line,
068: mappingProcessor);
069: }
070: }
071: } catch (IOException ex) {
072: throw new IOException("Can't process mapping file ("
073: + ex.getMessage() + ")");
074: } finally {
075: try {
076: reader.close();
077: } catch (IOException ex) {
078: // This shouldn't happen.
079: }
080: }
081: }
082:
083: /**
084: * Parses the given line with a class mapping and processes the
085: * results with the given mapping processor. Returns the old class name,
086: * or null if any subsequent class member lines can be ignored.
087: */
088: private String processClassMapping(String line,
089: MappingProcessor mappingProcessor) {
090: // See if we can parse "___ -> ___:", containing the original
091: // class name and the new class name.
092:
093: line = line.trim();
094:
095: int arrowIndex = line.indexOf("->");
096: if (arrowIndex < 0) {
097: return null;
098: }
099:
100: int colonIndex = line.indexOf(':', arrowIndex + 2);
101: if (colonIndex < 0) {
102: return null;
103: }
104:
105: // Extract the elements.
106: String className = line.substring(0, arrowIndex).trim();
107: String newClassName = line
108: .substring(arrowIndex + 2, colonIndex).trim();
109:
110: // Process this class name mapping.
111: boolean interested = mappingProcessor.processClassMapping(
112: className, newClassName);
113:
114: return interested ? className : null;
115: }
116:
117: /**
118: * Parses the given line with a class member mapping and processes the
119: * results with the given mapping processor.
120: */
121: private void processClassMemberMapping(String className,
122: String line, MappingProcessor mappingProcessor) {
123: // See if we can parse " ___:___:___ ___ -> ___",
124: // containing the optional line numbers, the return type, the original
125: // field/method name (including arguments), and the new method name.
126:
127: line = line.trim();
128:
129: int colonIndex1 = line.indexOf(':');
130: int colonIndex2 = line.indexOf(':', colonIndex1 + 1);
131:
132: int spaceIndex = line.indexOf(' ', colonIndex2 + 1);
133: if (spaceIndex < 0) {
134: return;
135: }
136:
137: int arrowIndex = line.indexOf("->", spaceIndex + 1);
138: if (arrowIndex < 1) {
139: return;
140: }
141:
142: int firstLineNumber = colonIndex1 < 0 ? 0 : Integer
143: .parseInt(line.substring(0, colonIndex1).trim());
144:
145: int lastLineNumber = colonIndex1 < 0 || colonIndex2 < 0 ? 0
146: : Integer.parseInt(line.substring(colonIndex1 + 1,
147: colonIndex2).trim());
148:
149: // Extract the elements.
150: String type = line.substring(colonIndex2 + 1, spaceIndex)
151: .trim();
152: String name = line.substring(spaceIndex + 1, arrowIndex).trim();
153: String newName = line.substring(arrowIndex + 2).trim();
154:
155: // Process this class member mapping.
156: if (type.length() > 0 && name.length() > 0) {
157: if (name.charAt(name.length() - 1) != ')') {
158: mappingProcessor.processFieldMapping(className, type,
159: name, newName);
160: } else {
161: mappingProcessor.processMethodMapping(className,
162: firstLineNumber, lastLineNumber, type, name,
163: newName);
164: }
165: }
166: }
167: }
|