Home | History | Annotate | Download | only in jarjar
      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/") || key.startsWith("javax/"))
     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 }
    113