/* ModifierMatcher Copyright (C) 1999-2002 Jochen Hoenicke. * * This program is free software; you can redistribute it and/or modify * it under the terms of the GNU General Public License as published by * the Free Software Foundation; either version 2, or (at your option) * any later version. * * This program is distributed in the hope that it will be useful, * but WITHOUT ANY WARRANTY; without even the implied warranty of * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the * GNU General Public License for more details. * * You should have received a copy of the GNU General Public License * along with this program; see the file COPYING. If not, write to * the Free Software Foundation, 675 Mass Ave, Cambridge, MA 02139, USA. * * $Id$ */ package net.sf.jode.obfuscator.modules; import net.sf.jode.obfuscator.IdentifierMatcher; import net.sf.jode.obfuscator.Identifier; import net.sf.jode.obfuscator.ClassIdentifier; import net.sf.jode.obfuscator.FieldIdentifier; import net.sf.jode.obfuscator.MethodIdentifier; import net.sf.jode.obfuscator.OptionHandler; import java.lang.reflect.Modifier; ///#def COLLECTIONS java.util import java.util.Collection; import java.util.Iterator; ///#enddef public class ModifierMatcher implements IdentifierMatcher, OptionHandler, Cloneable { static final int PUBLIC = Modifier.PUBLIC; static final int PROTECTED = Modifier.PROTECTED; static final int PRIVATE = Modifier.PRIVATE; int[] andMasks; int[] xorMasks; public static ModifierMatcher denyAll = new ModifierMatcher(new int[0], new int[0]); public static ModifierMatcher allowAll = new ModifierMatcher(0, 0); /* Invariants: * \forall i: ~andMasks[i] & xorMasks[i] == 0 * \forall i: entries wo. i does not imply entry nr. i */ public ModifierMatcher() { this(0,0); } private ModifierMatcher(int[] ands, int[] xors) { andMasks = ands; xorMasks = xors; } public ModifierMatcher(int and, int xor) { andMasks = new int[] { and }; xorMasks = new int[] { xor }; } public void setOption(String option, Collection values) { ModifierMatcher mm = this; if (option.equals("access")) { for(Iterator i = values.iterator(); i.hasNext();) { String str = (String) i.next(); boolean less = str.charAt(0) == '<'; boolean greater = str.charAt(0) == '>'; if (less || greater) str = str.substring(1); str = str.toUpperCase(); if (less) { int access = str.equals("PROTECTED") ? PUBLIC : str.equals("PACKAGE") ? PROTECTED : str.equals("PRIVATE") ? 0 : -1; if (access == -1) throw new IllegalArgumentException ("Unknown access modifier " + str); mm = mm.forbidAccess(access, true); } else { int access = str.equals("PUBLIC") ? PUBLIC : str.equals("PROTECTED") ? PROTECTED : str.equals("PACKAGE") ? 0 : str.equals("PRIVATE") ? PRIVATE : -1; if (access == -1) throw new IllegalArgumentException ("Unknown access " + str); mm = mm.forceAccess(access, greater); } } } else if (option.equals("modifier")) { for(Iterator i = values.iterator(); i.hasNext();) { String str = (String) i.next(); boolean negate = str.charAt(0) == '!'; if (negate) str = str.substring(1); str = str.toUpperCase(); int modif = str.equals("ABSTRACT") ? Modifier.ABSTRACT : str.equals("FINAL") ? Modifier.FINAL : str.equals("INTERFACE") ? Modifier.INTERFACE : str.equals("NATIVE") ? Modifier.NATIVE : str.equals("STATIC") ? Modifier.STATIC ///#ifdef JDK12 : str.equals("STRICT") ? Modifier.STRICT ///#endif : str.equals("SYNCHRONIZED") ? Modifier.SYNCHRONIZED : str.equals("TRANSIENT") ? Modifier.TRANSIENT : str.equals("VOLATILE") ? Modifier.VOLATILE : -1; if (modif == -1) throw new IllegalArgumentException ("Unknown modifier " + str); if (negate) mm = mm.forbidModifier(modif); else mm = mm.forceModifier(modif); } } else throw new IllegalArgumentException("Invalid option `"+option+"'."); andMasks = mm.andMasks; xorMasks = mm.xorMasks; } private static boolean implies(int and1, int xor1, int and2, int xor2) { return ((and1 & and2) == and2 && (xor1 & and2) == xor2); } private boolean implies(int and, int xor) { for (int i=0; i < andMasks.length; i++) { if (!implies(andMasks[i], xorMasks[i], and, xor)) return false; } return true; } private boolean impliedBy(int and, int xor) { for (int i=0; i< andMasks.length; i++) { if (implies(and, xor, andMasks[i], xorMasks[i])) return true; } return false; } private boolean implies(ModifierMatcher mm) { for (int i=0; i < andMasks.length; i++) { if (!mm.impliedBy(andMasks[i], xorMasks[i])) return false; } return true; } public ModifierMatcher and(ModifierMatcher mm) { if (implies(mm)) return this; if (mm.implies(this)) return mm; ModifierMatcher result = denyAll; for (int i=0; i< andMasks.length; i++) result = result.or(mm.and(andMasks[i], xorMasks[i])); return result; } public ModifierMatcher or(ModifierMatcher mm) { if (implies(mm)) return mm; if (mm.implies(this)) return this; ModifierMatcher result = this; for (int i=0; i < mm.andMasks.length; i++) result = result.or(mm.andMasks[i], mm.xorMasks[i]); return result; } private ModifierMatcher and(int and, int xor) { if (this.implies(and, xor)) return this; int newCount = 0; next_i: for (int i=0; i < andMasks.length; i++) { if (implies(and, xor, andMasks[i], xorMasks[i])) continue next_i; for (int j=0; j < andMasks.length; j++) { if (j != i && implies(and | andMasks[j], xor | xorMasks[j], andMasks[i], xorMasks[i])) continue next_i; } newCount++; } if (newCount == 0) return new ModifierMatcher(and, xor); int[] ands = new int[newCount]; int[] xors = new int[newCount]; int index = 0; next_i: for (int i=0; i < newCount; i++) { if (implies(and, xor, andMasks[i], xorMasks[i])) continue next_i; for (int j=0; j < andMasks.length; j++) { if (j != i && implies(and | andMasks[j], xor | xorMasks[j], andMasks[i], xorMasks[i])) continue next_i; } ands[index] = andMasks[i] | and; xors[index] = xorMasks[i] | xor; index++; } return new ModifierMatcher(ands, xors); } private ModifierMatcher or(int and, int xor) { int matchIndex = -1; if (this == denyAll) return new ModifierMatcher(and, xor); for (int i=0; i< andMasks.length; i++) { if (implies(and, xor, andMasks[i], xorMasks[i])) return this; if (implies(andMasks[i], xorMasks[i], and, xor)) { matchIndex = i; break; } } int[] ands, xors; if (matchIndex == -1) { matchIndex = andMasks.length; ands = new int[matchIndex+1]; xors = new int[matchIndex+1]; System.arraycopy(andMasks, 0, ands, 0, matchIndex); System.arraycopy(xorMasks, 0, xors, 0, matchIndex); } else { ands = (int[]) andMasks.clone(); xors = (int[]) xorMasks.clone(); } ands[matchIndex] = and; xors[matchIndex] = xor; return new ModifierMatcher(ands, xors); } /** * Creates a new ModifierMatcher, that matches only modifiers, we * also match and also forces the access rights, to be accessModif * (or less restrictive). * @param accessModif the access modifier. Use 0 for package access, * or Modifier.PRIVATE/PROTECTED/PUBLIC. * @param andAbove allow to be less restrictive. * @return a new modifier matcher that will also use the given accesses. */ public ModifierMatcher forceAccess(int accessModif, boolean andAbove) { if (andAbove) { if (accessModif == Modifier.PRIVATE) return this; if (accessModif == 0) return this.and(Modifier.PRIVATE, 0); ModifierMatcher result = this.and(Modifier.PUBLIC, PUBLIC); if (accessModif == Modifier.PROTECTED) return result .or(this.and(Modifier.PROTECTED, Modifier.PROTECTED)); if (accessModif == Modifier.PUBLIC) return result; throw new IllegalArgumentException(""+accessModif); } else { if (accessModif == 0) return this.and(Modifier.PRIVATE | Modifier.PROTECTED | Modifier.PUBLIC, 0); else return this.and(accessModif, accessModif); } } public ModifierMatcher forbidAccess(int accessModif, boolean andAbove) { if (andAbove) { if (accessModif == Modifier.PRIVATE) // This forbids all access. return denyAll; if (accessModif == 0) return this.and(Modifier.PRIVATE, Modifier.PRIVATE); if (accessModif == Modifier.PROTECTED) return this.and(Modifier.PROTECTED | Modifier.PUBLIC, 0); if (accessModif == Modifier.PUBLIC) return this.and(Modifier.PUBLIC, 0); throw new IllegalArgumentException(""+accessModif); } else { if (accessModif == 0) { return this.and(Modifier.PRIVATE, Modifier.PRIVATE) .or(this.and(Modifier.PROTECTED, Modifier.PROTECTED)) .or(this.and(Modifier.PUBLIC, Modifier.PUBLIC)); } else return this.and(accessModif, 0); } } public final ModifierMatcher forceModifier(int modifier) { return this.and(modifier, modifier); } public final ModifierMatcher forbidModifier(int modifier) { return this.and(modifier, 0); } public final boolean matches(int modifiers) { for (int i=0; i< andMasks.length; i++) if ((modifiers & andMasks[i]) == xorMasks[i]) return true; return false; } public final boolean matches(Identifier ident) { int modifiers; /* XXX NEW INTERFACE OR ANOTHER METHOD IN IDENTIFIER? */ if (ident instanceof ClassIdentifier) modifiers = ((ClassIdentifier) ident).getModifiers(); else if (ident instanceof MethodIdentifier) modifiers = ((MethodIdentifier) ident).getModifiers(); else if (ident instanceof FieldIdentifier) modifiers = ((FieldIdentifier) ident).getModifiers(); else return false; return matches(modifiers); } public final boolean matchesSub(Identifier ident, String name) { return true; } public final String getNextComponent(Identifier ident) { return null; } public Object clone() { try { return super.clone(); } catch (CloneNotSupportedException ex) { throw new IncompatibleClassChangeError(getClass().getName()); } } }