1/** 2 * Copyright 2007 Google Inc. 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 17package com.tonicsystems.jarjar; 18 19import org.objectweb.asm.*; 20import org.objectweb.asm.commons.*; 21import java.util.*; 22import java.util.regex.Pattern; 23 24class PackageRemapper extends Remapper 25{ 26 private static final String RESOURCE_SUFFIX = "RESOURCE"; 27 28 private static final Pattern ARRAY_FOR_NAME_PATTERN 29 = Pattern.compile("\\[L[\\p{javaJavaIdentifierPart}\\.]+?;"); 30 31 private final List<Wildcard> wildcards; 32 private final Map<String, String> typeCache = new HashMap<String, String>(); 33 private final Map<String, String> pathCache = new HashMap<String, String>(); 34 private final Map<Object, String> valueCache = new HashMap<Object, String>(); 35 private final boolean verbose; 36 37 public PackageRemapper(List<Rule> ruleList, boolean verbose) { 38 this.verbose = verbose; 39 wildcards = PatternElement.createWildcards(ruleList); 40 } 41 42 // also used by KeepProcessor 43 static boolean isArrayForName(String value) { 44 return ARRAY_FOR_NAME_PATTERN.matcher(value).matches(); 45 } 46 47 public String map(String key) { 48 String s = typeCache.get(key); 49 if (s == null) { 50 s = replaceHelper(key); 51 if (key.equals(s)) 52 s = null; 53 typeCache.put(key, s); 54 } 55 return s; 56 } 57 58 public String mapPath(String path) { 59 String s = pathCache.get(path); 60 if (s == null) { 61 s = path; 62 int slash = s.lastIndexOf('/'); 63 String end; 64 if (slash < 0) { 65 end = s; 66 s = RESOURCE_SUFFIX; 67 } else { 68 end = s.substring(slash + 1); 69 s = s.substring(0, slash + 1) + RESOURCE_SUFFIX; 70 } 71 boolean absolute = s.startsWith("/"); 72 if (absolute) s = s.substring(1); 73 74 s = replaceHelper(s); 75 76 if (absolute) s = "/" + s; 77 if (s.indexOf(RESOURCE_SUFFIX) < 0) 78 return path; 79 s = s.substring(0, s.length() - RESOURCE_SUFFIX.length()) + end; 80 pathCache.put(path, s); 81 } 82 return s; 83 } 84 85 public Object mapValue(Object value) { 86 if (value instanceof String) { 87 String s = valueCache.get(value); 88 if (s == null) { 89 s = (String)value; 90 if (isArrayForName(s)) { 91 String desc1 = s.replace('.', '/'); 92 String desc2 = mapDesc(desc1); 93 if (!desc2.equals(desc1)) 94 return desc2.replace('/', '.'); 95 } else { 96 s = mapPath(s); 97 if (s.equals(value)) { 98 boolean hasDot = s.indexOf('.') >= 0; 99 boolean hasSlash = s.indexOf('/') >= 0; 100 if (!(hasDot && hasSlash)) { 101 if (hasDot) { 102 s = replaceHelper(s.replace('.', '/')).replace('/', '.'); 103 } else { 104 s = replaceHelper(s); 105 } 106 } 107 } 108 } 109 valueCache.put(value, s); 110 } 111 // TODO: add back class name to verbose message 112 if (verbose && !s.equals(value)) 113 System.err.println("Changed \"" + value + "\" -> \"" + s + "\""); 114 return s; 115 } else { 116 return super.mapValue(value); 117 } 118 } 119 120 private String replaceHelper(String value) { 121 for (Wildcard wildcard : wildcards) { 122 String test = wildcard.replace(value); 123 if (test != null) 124 return test; 125 } 126 return value; 127 } 128} 129