View Javadoc

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  
17  package com.tonicsystems.jarjar;
18  
19  import com.tonicsystems.jarjar.util.*;
20  import java.io.*;
21  import java.util.*;
22  import org.objectweb.asm.*;
23  import org.objectweb.asm.Type;
24  import org.objectweb.asm.commons.*;
25  
26  // TODO: this can probably be refactored into JarClassVisitor, etc.
27  class KeepProcessor extends Remapper implements JarProcessor
28  {
29      private final ClassVisitor cv = new RemappingClassAdapter(new EmptyClassVisitor(), this);
30      private final List<Wildcard> wildcards;
31      private final List<String> roots = new ArrayList<String>();
32      private final Map<String, Set<String>> depend = new HashMap<String, Set<String>>();
33      
34      public KeepProcessor(List<Keep> patterns) {
35          wildcards = PatternElement.createWildcards(patterns);
36      }
37  
38      public boolean isEnabled() {
39          return !wildcards.isEmpty();
40      }
41  
42      public Set<String> getExcludes() {
43          Set<String> closure = new HashSet<String>();
44          closureHelper(closure, roots);
45          Set<String> removable = new HashSet<String>(depend.keySet());
46          removable.removeAll(closure);
47          return removable;
48      }
49  
50      private void closureHelper(Set<String> closure, Collection<String> process) {
51          if (process == null)
52              return;
53          for (String name : process) {
54              if (closure.add(name))
55                  closureHelper(closure, depend.get(name));
56          }
57      }
58  
59      private Set<String> curSet;
60      private byte[] buf = new byte[0x2000];
61  
62      public boolean process(EntryStruct struct) throws IOException {
63          try {
64              if (struct.name.endsWith(".class")) {
65                  String name = struct.name.substring(0, struct.name.length() - 6);
66                  for (Wildcard wildcard : wildcards)
67                      if (wildcard.matches(name))
68                          roots.add(name);
69                  depend.put(name, curSet = new HashSet<String>());
70                  new ClassReader(new ByteArrayInputStream(struct.data)).accept(cv,
71                      ClassReader.EXPAND_FRAMES);
72                  curSet.remove(name);
73              }
74          } catch (Exception e) {
75            System.err.println("Error reading " + struct.name + ": " + e.getMessage());
76          }
77          return true;
78      }
79  
80      public String map(String key) {
81          if (key.startsWith("java/"))
82              return null;
83          curSet.add(key);
84          return null;
85      }
86  
87      public Object mapValue(Object value) {
88          if (value instanceof String) {
89              String s = (String)value;
90              if (PackageRemapper.isArrayForName(s)) {
91                  mapDesc(s.replace('.', '/'));
92              } else if (isForName(s)) {
93                  map(s.replace('.', '/'));
94              }
95              return value;
96          } else {
97              return super.mapValue(value);
98          }
99      }
100 
101     // TODO: use this for package remapping too?
102     private static boolean isForName(String value) {
103         if (value.equals(""))
104             return false;
105         for (int i = 0, len = value.length(); i < len; i++) {
106             char c = value.charAt(i);
107             if (c != '.' && !Character.isJavaIdentifierPart(c))
108                 return false;
109         }
110         return true;
111     }
112 }