Initial revision

git-svn-id: https://svn.code.sf.net/p/jode/code/trunk@995 379699f6-c40d-0410-875b-85095c16579e
stable
jochen 25 years ago
parent 59c6451fa8
commit 8c31bdd062
  1. 6
      jode/jode/obfuscator/IdentifierMatcher.java
  2. 58
      jode/jode/obfuscator/LocalIdentifier.java
  3. 233
      jode/jode/obfuscator/ModifierMatcher.java
  4. 126
      jode/jode/obfuscator/NameSwapper.java
  5. 31
      jode/jode/obfuscator/Renamer.java
  6. 63
      jode/jode/obfuscator/StrongRenamer.java
  7. 60
      jode/jode/obfuscator/TranslationTable.java
  8. 198
      jode/jode/util/Bucket.java
  9. 876
      jode/jode/util/HashMap.java
  10. 238
      jode/jode/util/HashSet.java
  11. 12
      jode/test/Base.j
  12. 29
      jode/test/Child.j

@ -0,0 +1,6 @@
package jode.obfuscator;
public interface IdentifierMatcher {
public boolean matches(Identifier ident);
}

@ -0,0 +1,58 @@
/* LocalIdentifier Copyright (C) 1999 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 jode.obfuscator;
public class LocalIdentifier extends Identifier {
String name;
String type;
public LocalIdentifier(String name, String type, MethodIdentifier mIdent) {
super(name);
this.name = name;
this.type = type;
}
public String getName() {
return name;
}
public String getType() {
return type;
}
public void applyPreserveRule(IdentifierMatcher preserveRule) {
}
public Identifier getParent() {
return null;
}
public String getFullName() {
return name;
}
public String getFullAlias() {
return getAlias();
}
public boolean conflicting(String newAlias) {
return false;
}
}

@ -0,0 +1,233 @@
package jode.obfuscator;
import java.lang.reflect.Modifier;
public class ModifierMatcher implements IdentifierMatcher, 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
*/
private ModifierMatcher(int[] ands, int[] xors) {
andMasks = ands;
xorMasks = xors;
}
public ModifierMatcher(int and, int xor) {
andMasks = new int[] { and };
xorMasks = new int[] { xor };
}
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;
for (int i=0; i < newCount; i++) {
int bothAnd = andMasks[i] & and;
if ((xorMasks[i] & bothAnd) == (and & bothAnd)) {
ands[index++] = andMasks[i] | and;
xors[index++] = xorMasks[i] | xor;
}
}
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).info.getModifiers();
else if (ident instanceof MethodIdentifier)
modifiers = ((MethodIdentifier) ident).info.getModifiers();
else if (ident instanceof FieldIdentifier)
modifiers = ((FieldIdentifier) ident).info.getModifiers();
else
return false;
return matches(modifiers);
}
public Object clone() {
try {
return super.clone();
} catch (CloneNotSupportedException ex) {
throw new IncompatibleClassChangeError(getClass().getName());
}
}
}

@ -0,0 +1,126 @@
/* NameSwapper Copyright (C) 1999 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 jode.obfuscator;
import java.util.Random;
///#ifdef JDK12
///import java.util.Collection;
///import java.util.Set;
///import java.util.HashSet;
///import java.util.Iterator;
///#else
import jode.util.Collection;
import jode.util.Set;
import jode.util.HashSet;
import jode.util.Iterator;
///#endif
public class NameSwapper implements Renamer {
private Random rand;
private Set packs, clazzes, methods, fields, locals;
public NameSwapper(boolean swapAll, long seed) {
if (swapAll) {
packs = clazzes = methods = fields = locals = new HashSet();
} else {
packs = new HashSet();
clazzes = new HashSet();
methods = new HashSet();
fields = new HashSet();
locals = new HashSet();
}
}
public NameSwapper(boolean swapAll) {
this(swapAll, System.currentTimeMillis());
}
private void addName(Collection coll, String name) {
coll.add(name);
}
private String getName(Collection coll) {
///#ifdef JDK12
/// int pos = rand.nextInt(coll.size());
///#else
int pos = rand.nextInt() % coll.size();
///#endif
Iterator i = coll.iterator();
while (pos > 0)
i.next();
return (String) i.next();
}
public final void addPackageName(String name) {
addName(packs, name);
}
public final void addClassName(String name) {
addName(clazzes, name);
}
public final void addMethodName(String name) {
addName(methods, name);
}
public final void addFieldName(String name) {
addName(fields, name);
}
public final void addLocalName(String name) {
addName(locals, name);
}
public final String getPackageName() {
return getName(packs);
}
public final String getClassName() {
return getName(clazzes);
}
public final String getMethodName() {
return getName(methods);
}
public final String getFieldName() {
return getName(fields);
}
public final String getLocalName() {
return getName(locals);
}
public String generateName(Identifier ident, String lastName) {
Collection coll = null;
if (ident instanceof PackageIdentifier)
coll = packs;
else if (ident instanceof ClassIdentifier)
coll = clazzes;
else if (ident instanceof MethodIdentifier)
coll = methods;
else if (ident instanceof FieldIdentifier)
coll = fields;
// else if (ident instanceof LocalIdentifier)
// coll = locals;
return getName(coll);
}
}

@ -0,0 +1,31 @@
/* Renamer Copyright (C) 1999 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 jode.obfuscator;
public interface Renamer {
/**
* Generates a new name.
* @param ident the identifier that should be renamed.
* @param lastName the last name generated for this identifier, or
* null, if no name was generated yet.
*/
public String generateName(Identifier ident, String lastName);
}

@ -0,0 +1,63 @@
/* StrongRenamer Copyright (C) 1999 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 jode.obfuscator;
public class StrongRenamer implements Renamer {
String charSetStart;
String charSetCont;
public StrongRenamer(String charSetStart, String charSetCont) {
this.charSetStart = charSetStart;
this.charSetCont = charSetCont;
}
public String generateName(Identifier ident, String lastName) {
if (lastName == null)
return charSetStart.substring(0,1);
char firstCont = charSetCont.charAt(0);
int pos = lastName.length() - 1;
StringBuffer sb = new StringBuffer(lastName.length() + 1);
while (pos > 0) {
int index = charSetCont.indexOf(lastName.charAt(pos)) + 1;
if (index < charSetCont.length()) {
sb.append(lastName.substring(0, pos));
sb.append(charSetCont.charAt(index));
for (int i = lastName.length() - pos - 1; i-- > 0; )
sb.append(firstCont);
return sb.toString();
}
pos --;
}
int index = charSetStart.indexOf(lastName.charAt(pos)) + 1;
if (index < charSetStart.length()) {
sb.append(charSetStart.charAt(index));
for (int i = lastName.length() - 1; i-- > 0; )
sb.append(firstCont);
return sb.toString();
} else {
sb.append(charSetStart.charAt(0));
for (int i = lastName.length(); i-- > 0; )
sb.append(firstCont);
}
return sb.toString();
}
}

@ -0,0 +1,60 @@
package jode.obfuscator;
///#ifdef JDK12
///import java.util.Map;
///import java.util.TreeMap;
///import java.util.Iterator;
///#else
import jode.util.Comparator;
import jode.util.Map;
import jode.util.TreeMap;
import jode.util.Iterator;
///#endif
import java.io.InputStream;
import java.io.InputStreamReader;
import java.io.BufferedReader;
import java.io.OutputStream;
import java.io.PrintWriter;
import java.io.IOException;
public class TranslationTable extends TreeMap {
///#ifndef JDK12
public TranslationTable() {
super(createStringComparator());
}
private static Comparator createStringComparator() {
return new Comparator() {
public int compare(Object o1, Object o2) {
return ((String) o1).compareTo((String) o2);
}
};
}
///#endif
public void load(InputStream in) throws IOException {
BufferedReader reader =
new BufferedReader(new InputStreamReader(in));
String line;
while ((line = reader.readLine()) != null) {
if (line.charAt(0) == '#')
continue;
int delim = line.indexOf('=');
String key = line.substring(0, delim);
String value = line.substring(delim+1);
put(key, value);
}
}
public void store(OutputStream out) throws IOException {
PrintWriter writer = new PrintWriter(out);
for (Iterator i = entrySet().iterator(); i.hasNext(); ) {
Map.Entry e = (Map.Entry) i.next();
writer.println(e.getKey()+"="+e.getValue());
}
writer.flush();
}
}

@ -0,0 +1,198 @@
// This class is taken from the Classpath project.
// Please note the different copyright holder!
// The changes I did is this comment, the package line, some
// imports from java.util and some minor jdk12 -> jdk11 fixes.
// -- Jochen Hoenicke <jochen@gnu.org>
/////////////////////////////////////////////////////////////////////////////
// Bucket.java -- a class providing a hash-bucket data structure (a lightweight
// linked list)
//
// Copyright (c) 1998 by Jon A. Zeppieri (jon@eease.com)
//
// This program is free software; you can redistribute it and/or modify
// it under the terms of the GNU Library General Public License as published
// by the Free Software Foundation, version 2. (see COPYING.LIB)
//
// 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 Library General Public License for more details.
//
// You should have received a copy of the GNU Library General Public License
// along with this program; if not, write to the Free Software Foundation
// Inc., 59 Temple Place - Suite 330, Boston, MA 02111-1307 USA
/////////////////////////////////////////////////////////////////////////////
package jode.util;
/**
* a class representing a simple, lightweight linked-list, using Node
* objects as its linked nodes; this is used by Hashtable and HashMap
*
* @author Jon Zeppieri
* @version $Revision$
* @modified $Id$
*/
class Bucket
{
/** the first node of the lined list, originally null */
Node first;
/** trivial constructor for a Bucket */
Bucket()
{
}
/** add this key / value pair to the list
*
* @param entry a Map.Entry object to be added to this list
*/
Map.Entry add(Node newNode)
{
Object oKey;
Object oTestKey = newNode.getKey();
Node it = first;
Node prev = null;
if (it == null) // if the list is empty (the ideal case), we make a new single-node list
{
first = newNode;
return null;
}
else // otherwise try to find where this key already exists in the list,
{// and if it does, replace the value with the new one (and return the old one)
while (it != null)
{
oKey = it.getKey();
if ((oKey == null) ? (oTestKey == null) :
oKey.equals(oTestKey))
{
if (prev != null)
prev.next = newNode;
else
first = newNode;
newNode.next = it.next;
return it;
}
prev = it;
it = it.next;
}
prev.next = newNode; // otherwise, just stick this at the
return null; // end of the list
}
}
/**
* remove a Map.Entry in this list with the supplied key and return its value,
* if it exists, else return null
*
* @param key the key we are looking for in this list
*/
Object removeByKey(Object key)
{
Object oEntryKey;
Node prev = null;
Node it = first;
while (it != null)
{
oEntryKey = it.getKey();
if ((oEntryKey == null) ? (key == null) : oEntryKey.equals(key))
{
if (prev == null) // we are removing the first element
first = it.next;
else
prev.next = it.next;
return it.getValue();
}
else
{
prev = it;
it = it.next;
}
}
return null;
}
/**
* return the value which the supplied key maps to, if it maps to anything in this list,
* otherwise, return null
*
* @param key the key mapping to a value that we are looking for
*/
Object getValueByKey(Object key)
{
Node entry = getEntryByKey(key);
return (entry == null) ? null : entry.getValue();
}
/**
* return the Map.Entry which the supplied key is a part of, if such a Map.Entry exists,
* null otherwise
*
* this method is important for HashMap, which can hold null values and the null key
*
* @param key the key for which we are finding the corresponding Map.Entry
*/
Node getEntryByKey(Object key)
{
Object oEntryKey;
Node it = first;
while (it != null)
{
oEntryKey = it.getKey();
if ((oEntryKey == null) ? (key == null) : oEntryKey.equals(key))
return it;
it = it.next;
}
return null;
}
/**
* return true if this list has a Map.Entry whose value equals() the supplied value
*
* @param value the value we are looking to match in this list
*/
boolean containsValue(Object value)
{
Object oEntryValue;
Node it = first;
while (it != null)
{
oEntryValue = it.getValue();
if ((oEntryValue == null) ? (value == null) : oEntryValue.equals(value))
return true;
it = it.next;
}
return false;
}
// INNSER CLASSES ----------------------------------------------------------
/**
* a class represnting a node in our lightweight linked-list
* that we use for hash buckets; a Node object contains a Map.Entry as its
* <pre>value</pre> property and a reference (possibly, even hopefully, null)
* to another Node as its <pre>next</pre> property.
*
* There <i>is</i> a reason for not using a highly generic "LinkedNode" type
* class: we want to eliminate runtime typechecks.
*
* @author Jon Zeppieri
* @version $Revision$
* @modified $Id$
*/
static class Node extends BasicMapEntry implements Map.Entry
{
/** a reference to the next node in the linked list */
Node next;
/** non-trivial contructor -- sets the <pre>value</pre> of the Bucket upon instantiation */
Node(Object key, Object value)
{
super(key, value);
}
}
// EOF ------------------------------------------------------------------------
}

@ -0,0 +1,876 @@
// This class is taken from the Classpath project.
// Please note the different copyright holder!
// The changes I did is this comment, the package line, some
// imports from java.util and some minor jdk12 -> jdk11 fixes.
// -- Jochen Hoenicke <jochen@gnu.org>
/////////////////////////////////////////////////////////////////////////////
// HashMap.java -- a class providing a basic hashtable data structure,
// mapping Object --> Object; part of the JDK1.2 collections
// API
//
// This is a JDK 1.2 compliant version of HashMap.java
//
// Copyright (c) 1998 by Jon A. Zeppieri (jon@eease.com),
// Free Software Foundation, Inc.
//
// This program is free software; you can redistribute it and/or modify
// it under the terms of the GNU Library General Public License as published
// by the Free Software Foundation, version 2. (see COPYING.LIB)
//
// 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 Library General Public License for more details.
//
// You should have received a copy of the GNU Library General Public License
// along with this program; if not, write to the Free Software Foundation
// Inc., 59 Temple Place - Suite 330, Boston, MA 02111-1307 USA
/////////////////////////////////////////////////////////////////////////////
package jode.util;
import java.util.NoSuchElementException;
import java.io.IOException;
import java.io.Serializable;
import java.io.ObjectInputStream;
import java.io.ObjectOutputStream;
/**
* This class provides a hashtable-backed implementation of the
* Map interface.
*
* It uses a hash-bucket approach; that is, hash
* collisions are handled by linking the new node off of the
* pre-existing node (or list of nodes). In this manner, techniques
* such as linear probing (which can casue primary clustering) and
* rehashing (which does not fit very well with Java's method of
* precomputing hash codes) are avoided.
*
* Under ideal circumstances (no collisions, HashMap offers O(1)
* performance on most operations (<pre>containsValue()</pre> is,
* of course, O(n)). In the worst case (all keys map to the same
* hash code -- very unlikely), most operations are O(n).
*
* HashMap is part of the JDK1.2 Collections API. It differs from
* Hashtable in that it accepts the null key and null values, and it
* does not support "Enumeration views."
*
* @author Jon Zeppieri
* @version $Revision$
* @modified $Id$
*/
public class HashMap extends AbstractMap
implements Map, Cloneable, Serializable
{
// STATIC (CLASS) VARIABLES ------------------------------------------
/**
* the default capacity for an instance of HashMap -- I think this
* is low, and perhaps it shoudl be raised; Sun's documentation mildly
* suggests that this (11) is the correct value, though
*/
private static final int DEFAULT_CAPACITY = 11;
/** the default load factor of a HashMap */
private static final float DEFAULT_LOAD_FACTOR = 0.75F;
/** used internally to represent the null key */
private static final HashMap.Null NULL_KEY = new HashMap.Null();
/** used internally to parameterize the creation of set/collection views */
private static final int KEYS = 0;
/** used internally to parameterize the creation of set/collection views */
private static final int VALUES = 1;
/** used internally to parameterize the creation of set/collection views */
private static final int ENTRIES = 2;
private static final long serialVersionUID = 362498820763181265L;
// INSTANCE VARIABLES -------------------------------------------------
/** the capacity of this HashMap: denotes the size of the bucket array */
transient int capacity;
/** the size of this HashMap: denotes the number of key-value pairs */
private transient int size;
/** the load factor of this HashMap: used in computing the threshold
* @serial
*/
float loadFactor;
/* the rounded product of the capacity and the load factor; when the number of
* elements exceeds the threshold, the HashMap calls <pre>rehash()</pre>
* @serial
*/
private int threshold;
/**
* this data structure contains the actual key-value mappings; a
* <pre>BucketList</pre> is a lightweight linked list of "Buckets",
* which, in turn, are linked nodes containing a key-value mapping
* and a reference to the "next" Bucket in the list
*/
private transient Bucket[] buckets;
/**
* counts the number of modifications this HashMap has undergone; used by Iterators
* to know when to throw ConcurrentModificationExceptions (idea ripped-off from
* Stuart Ballard's AbstractList implementation)
*/
private transient int modCount;
// CONSTRUCTORS ---------------------------------------------------------
/**
* construct a new HashMap with the default capacity and the default
* load factor
*/
public HashMap()
{
init(DEFAULT_CAPACITY, DEFAULT_LOAD_FACTOR);
}
/**
* construct a new HashMap with a specific inital capacity and load factor
*
* @param initialCapacity the initial capacity of this HashMap (>=0)
* @param initialLoadFactor the load factor of this HashMap
* (a misnomer, really, since the load factor of
* a HashMap does not change)
*
* @throws IllegalArgumentException if (initialCapacity < 0) ||
* (initialLoadFactor > 1.0) ||
* (initialLoadFactor <= 0.0)
*/
public HashMap(int initialCapacity, float initialLoadFactor)
throws IllegalArgumentException
{
if (initialCapacity < 0 || initialLoadFactor <= 0 || initialLoadFactor > 1)
throw new IllegalArgumentException();
else
init(initialCapacity, initialLoadFactor);
}
/**
* construct a new HashMap with a specific inital capacity
*
* @param initialCapacity the initial capacity of this HashMap (>=0)
*
* @throws IllegalArgumentException if (initialCapacity < 0)
*/
public HashMap(int initialCapacity)
throws IllegalArgumentException
{
if (initialCapacity < 0)
throw new IllegalArgumentException();
else
init(initialCapacity, DEFAULT_LOAD_FACTOR);
}
/**
* construct a new HashMap from the given Map
*
* every element in Map t will be put into this new HashMap
*
* @param t a Map whose key / value pairs will be put into
* the new HashMap. <b>NOTE: key / value pairs
* are not cloned in this constructor</b>
*/
public HashMap(Map t)
{
int mapSize = t.size() * 2;
init(((mapSize > DEFAULT_CAPACITY) ? mapSize : DEFAULT_CAPACITY), DEFAULT_LOAD_FACTOR);
putAll(t);
}
// PUBLIC METHODS ---------------------------------------------------------
/** returns the number of kay-value mappings currently in this Map */
public int size()
{
return size;
}
/** returns true if there are no key-value mappings currently in this Map */
public boolean isEmpty()
{
return size == 0;
}
/** empties this HashMap of all elements */
public void clear()
{
size = 0;
modCount++;
buckets = new Bucket[capacity];
}
/**
* returns a shallow clone of this HashMap (i.e. the Map itself is cloned, but
* its contents are not)
*/
public Object clone()
{
Map.Entry entry;
Iterator it = entrySet().iterator();
HashMap clone = new HashMap(capacity, loadFactor);
while (it.hasNext())
{
entry = (Map.Entry) it.next();
clone.internalPut(entry.getKey(), entry.getValue());
}
return clone;
}
/** returns a "set view" of this HashMap's keys */
public Set keySet()
{
return new HashMapSet(KEYS);
}
/** returns a "set view" of this HashMap's entries */
public Set entrySet()
{
return new HashMapSet(ENTRIES);
}
/** returns a "collection view" (or "bag view") of this HashMap's values */
public Collection values()
{
return new HashMapCollection();
}
/**
* returns true if the supplied object equals (<pre>equals()</pre>) a key
* in this HashMap
*
* @param key the key to search for in this HashMap
*/
public boolean containsKey(Object key)
{
return (internalGet(key) != null);
}
/**
* returns true if this HashMap contains a value <pre>o</pre>, such that
* <pre>o.equals(value)</pre>.
*
* @param value the value to search for in this Hashtable
*/
public boolean containsValue(Object value)
{
int i;
Bucket list;
for (i = 0; i < capacity; i++)
{
list = buckets[i];
if (list != null && list.containsValue(value))
return true;
}
return false;
}
/*
* return the value in this Hashtable associated with the supplied key, or <pre>null</pre>
* if the key maps to nothing
*
* @param key the key for which to fetch an associated value
*/
public Object get(Object key)
{
Map.Entry oResult = internalGet(key);
return (oResult == null) ? null : oResult.getValue();
}
/**
* puts the supplied value into the Map, mapped by the supplied key
*
* @param key the HashMap key used to locate the value
* @param value the value to be stored in the HashMap
*/
public Object put(Object key, Object value)
{
return internalPut(key, value);
}
/**
* part of the Map interface; for each Map.Entry in t, the key / value pair is
* added to this Map, <b>using the <pre>put()</pre> method -- this may not be
* you want, so be warned (if you override the put() method, this could lead to
* off behavior)</b>
*
* @param t a Map whose key / value pairs will be added to this Hashtable
*/
public void putAll(Map t)
{
Map.Entry entry;
Iterator it = t.entrySet().iterator();
while (it.hasNext())
{
entry = (Map.Entry) it.next();
put(entry.getKey(), entry.getValue());
}
}
/**
* removes from the HashMap and returns the value which is mapped by the
* supplied key; if the key maps to nothing, then the HashMap remains unchanged,
* and <pre>null</pre> is returned
*
* @param key the key used to locate the value to remove from the HashMap
*/
public Object remove(Object key)
{
Bucket list;
int index;
Object result = null;
if (size > 0)
{
index = hash(((key == null) ? NULL_KEY : key));
list = buckets[index];
if (list != null)
{
result = list.removeByKey(key);
if (result != null)
{
size--;
modCount++;
if (list.first == null)
buckets[index] = null;
}
}
}
return result;
}
// PRIVATE METHODS -----------------------------------------------------------
/**
* puts the given key-value pair into this HashMap; a private method is used
* because it is called by the rehash() method as well as the put() method,
* and if a subclass overrides put(), then rehash would do funky things
* if it called put()
*
* @param key the HashMap key used to locate the value
* @param value the value to be stored in the HashMap
*/
private Object internalPut(Object key, Object value)
{
HashMapEntry entry;
Bucket list;
int hashIndex;
Object oResult;
Object oRealKey = ((key == null) ? NULL_KEY : key);
modCount++;
if (size == threshold)
rehash();
entry = new HashMapEntry(oRealKey, value);
hashIndex = hash(oRealKey);
list = buckets[hashIndex];
if (list == null)
{
list = new Bucket();
buckets[hashIndex] = list;
}
oResult = list.add(entry);
if (oResult == null)
{
size++;
return null;
}
else
{
return ((Map.Entry) oResult).getValue();
}
}
/**
* a private method, called by all of the constructors to initialize a new HashMap
*
* @param initialCapacity the initial capacity of this HashMap (>=0)
* @param initialLoadFactor the load factor of this HashMap
* (a misnomer, really, since the load factor of
* a HashMap does not change)
*/
private void init(int initialCapacity, float initialLoadFactor)
{
size = 0;
modCount = 0;
capacity = initialCapacity;
loadFactor = initialLoadFactor;
threshold = (int) ((float) capacity * loadFactor);
buckets = new Bucket[capacity];
}
/** private -- simply hashes a non-null Object to its array index */
private int hash(Object key)
{
return Math.abs(key.hashCode() % capacity);
}
/**
* increases the size of the HashMap and rehashes all keys to new array indices;
* this is called when the addition of a new value would cause size() > threshold
*/
private void rehash()
{
int i;
Bucket[] data = buckets;
Bucket.Node node;
modCount++;
capacity = (capacity * 2) + 1;
size = 0;
threshold = (int) ((float) capacity * loadFactor);
buckets = new Bucket[capacity];
for (i = 0; i < data.length; i++)
{
if (data[i] != null)
{
node = data[i].first;
while (node != null)
{
internalPut(node.getKey(), node.getValue());
node = node.next;
}
}
}
}
/**
* a private method which does the "dirty work" (or some of it anyway) of fetching a value
* with a key
*
* @param key the key for which to fetch an associated value
*/
private Map.Entry internalGet(Object key)
{
Bucket list;
if (size == 0)
{
return null;
}
else
{
list = buckets[hash(((key == null) ? NULL_KEY : key))];
return (list == null) ? null : list.getEntryByKey(key);
}
}
/**
* a private method used by inner class HashMapSet to implement its own
* <pre>contains(Map.Entry)</pre> method; returns true if the supplied
* key / value pair is found in this HashMap (again, using <pre>equals()</pre>,
* rather than <pre>==</pre>)
*
* @param entry a Map.Entry to match against key / value pairs in
* this HashMap
*/
private boolean containsEntry(Map.Entry entry)
{
Map.Entry oInternalEntry;
if (entry == null)
{
return false;
}
else
{
oInternalEntry = internalGet(entry.getKey());
return (oInternalEntry != null && oInternalEntry.equals(entry));
}
}
/**
* Serializes this object to the given stream.
* @serialdata the <i>capacity</i>(int) that is the length of the
* bucket array, the <i>size</i>(int) of the hash map are emitted
* first. They are followed by size entries, each consisting of
* a key (Object) and a value (Object).
*/
private void writeObject(ObjectOutputStream s)
throws IOException
{
// the fields
s.defaultWriteObject();
s.writeInt(capacity);
s.writeInt(size);
Iterator it = entrySet().iterator();
while (it.hasNext())
{
Map.Entry oEntry = (Map.Entry) it.next();
s.writeObject(oEntry.getKey());
s.writeObject(oEntry.getValue());
}
}
/**
* Deserializes this object from the given stream.
* @serialdata the <i>capacity</i>(int) that is the length of the
* bucket array, the <i>size</i>(int) of the hash map are emitted
* first. They are followed by size entries, each consisting of
* a key (Object) and a value (Object).
*/
private void readObject(ObjectInputStream s)
throws IOException, ClassNotFoundException
{
// the fields
s.defaultReadObject();
capacity = s.readInt();
int iLen = s.readInt();
size = 0;
modCount = 0;
buckets = new Bucket[capacity];
for (int i = 0; i < iLen; i++)
{
Object oKey = s.readObject();
Object oValue = s.readObject();
internalPut(oKey, oValue);
}
}
// INNER CLASSES -------------------------------------------------------------
// ---------------------------------------------------------------------------
/**
* an inner class providing a Set view of a HashMap; this implementation is
* parameterized to view either a Set of keys or a Set of Map.Entry objects
*
* Note: a lot of these methods are implemented by AbstractSet, and would work
* just fine without any meddling, but far greater efficiency can be gained by
* overriding a number of them. And so I did.
*
* @author Jon Zeppieri
* @version $Revision$
* @modified $Id$
*/
private class HashMapSet extends AbstractSet
implements Set
{
/** the type of this Set view: KEYS or ENTRIES */
private int setType;
/** construct a new HashtableSet with the supplied view type */
HashMapSet(int type)
{
setType = type;
}
/**
* adding an element is unsupported; this method simply throws an exception
*
* @throws UnsupportedOperationException
*/
public boolean add(Object o) throws UnsupportedOperationException
{
throw new UnsupportedOperationException();
}
/**
* adding an element is unsupported; this method simply throws an exception
*
* @throws UnsupportedOperationException
*/
public boolean addAll(Collection c) throws UnsupportedOperationException
{
throw new UnsupportedOperationException();
}
/**
* clears the backing HashMap; this is a prime example of an overridden implementation
* which is far more efficient than its superclass implementation (which uses an iterator
* and is O(n) -- this is an O(1) call)
*/
public void clear()
{
HashMap.this.clear();
}
/**
* returns true if the supplied object is contained by this Set
*
* @param o an Object being testing to see if it is in this Set
*/
public boolean contains(Object o)
{
if (setType == KEYS)
return HashMap.this.containsKey(o);
else
return (o instanceof Map.Entry) ? HashMap.this.containsEntry((Map.Entry) o) : false;
}
/**
* returns true if the backing HashMap is empty (which is the only case either a KEYS
* Set or an ENTRIES Set would be empty)
*/
public boolean isEmpty()
{
return HashMap.this.isEmpty();
}
/**
* removes the supplied Object from the Set
*
* @param o the Object to be removed
*/
public boolean remove(Object o)
{
if (setType == KEYS)
return (HashMap.this.remove(o) != null);
else
return (o instanceof Map.Entry) ?
(HashMap.this.remove(((Map.Entry) o).getKey()) != null) : false;
}
/** returns the size of this Set (always equal to the size of the backing Hashtable) */
public int size()
{
return HashMap.this.size();
}
/** returns an Iterator over the elements of this Set */
public Iterator iterator()
{
return new HashMapIterator(setType);
}
}
/**
* Like the above Set view, except this one if for values, which are not
* guaranteed to be unique in a Map; this prvides a Bag of values
* in the HashMap
*
* @author Jon Zeppieri
* @version $Revision$
* @modified $Id$
*/
private class HashMapCollection extends AbstractCollection
implements Collection
{
/** a trivial contructor for HashMapCollection */
HashMapCollection()
{
}
/**
* adding elements is not supported by this Collection;
* this method merely throws an exception
*
* @throws UnsupportedOperationException
*/
public boolean add(Object o) throws UnsupportedOperationException
{
throw new UnsupportedOperationException();
}
/**
* adding elements is not supported by this Collection;
* this method merely throws an exception
*
* @throws UnsupportedOperationException
*/
public boolean addAll(Collection c) throws UnsupportedOperationException
{
throw new UnsupportedOperationException();
}
/** removes all elements from this Collection (and from the backing HashMap) */
public void clear()
{
HashMap.this.clear();
}
/**
* returns true if this Collection contains at least one Object which equals() the
* supplied Object
*
* @param o the Object to compare against those in the Set
*/
public boolean contains(Object o)
{
return HashMap.this.containsValue(o);
}
/** returns true IFF the Collection has no elements */
public boolean isEmpty()
{
return HashMap.this.isEmpty();
}
/** returns the size of this Collection */
public int size()
{
return HashMap.this.size();
}
/** returns an Iterator over the elements in this Collection */
public Iterator iterator()
{
return new HashMapIterator(VALUES);
}
}
/**
* a class which implements the Iterator interface and is used for
* iterating over HashMaps;
* this implementation is parameterized to give a sequential view of
* keys, values, or entries; it also allows the removal of elements,
* as per the Javasoft spec.
*
* @author Jon Zeppieri
* @version $Revision$
* @modified $Id$
*/
class HashMapIterator implements Iterator
{
/** the type of this Iterator: KEYS, VALUES, or ENTRIES */
private int myType;
/**
* the number of modifications to the backing Hashtable for which
* this Iterator can account (idea ripped off from Stuart Ballard)
*/
private int knownMods;
/** the location of our sequential "cursor" */
private int position;
/** the current index of the BucketList array */
private int bucketIndex;
/** a reference, originally null, to the specific Bucket our "cursor" is pointing to */
private Bucket.Node currentNode;
/** a reference to the current key -- used fro removing elements via the Iterator */
private Object currentKey;
/** construct a new HashtableIterator with the supllied type: KEYS, VALUES, or ENTRIES */
HashMapIterator(int type)
{
myType = type;
knownMods = HashMap.this.modCount;
position = 0;
bucketIndex = -1;
currentNode = null;
currentKey = null;
}
/**
* Stuart Ballard's code: if the backing HashMap has been altered through anything
* but <i>this</i> Iterator's <pre>remove()</pre> method, we will give up right here,
* rather than risking undefined behavior
*
* @throws ConcurrentModificationException
*/
private void checkMod()
{
if (knownMods != HashMap.this.modCount)
throw new ConcurrentModificationException();
}
/** returns true if the Iterator has more elements */
public boolean hasNext()
{
checkMod();
return position < HashMap.this.size();
}
/** returns the next element in the Iterator's sequential view */
public Object next()
{
Bucket list = null;
Object result;
checkMod();
try
{
while (currentNode == null)
{
while (list == null)
list = HashMap.this.buckets[++bucketIndex];
currentNode = list.first;
}
currentKey = currentNode.getKey();
result = (myType == KEYS) ? currentKey :
((myType == VALUES) ? currentNode.getValue() : currentNode);
currentNode = currentNode.next;
}
catch(Exception e)
{
throw new NoSuchElementException();
}
position++;
return result;
}
/**
* removes from the backing HashMap the last element which was fetched with the
* <pre>next()</pre> method
*/
public void remove()
{
checkMod();
if (currentKey == null)
{
throw new IllegalStateException();
}
else
{
HashMap.this.remove(currentKey);
knownMods++;
currentKey = null;
}
}
}
/**
* a singleton instance of this class (HashMap.NULL_KEY)
* is used to represent the null key in HashMap objects
*
* @author Jon Zeppieri
* @version $Revision$
* @modified $Id$
*/
private static class Null
{
/** trivial constructor */
Null()
{
}
}
/**
* a HashMap version of Map.Entry -- one thing in this implementation is
* HashMap-specific: if the key is HashMap.NULL_KEY, getKey() will return
* null
*
* Simply, a key / value pair
*
* @author Jon Zeppieri
* @version $Revision$
* @modified $Id$
*/
private static class HashMapEntry extends Bucket.Node implements Map.Entry
{
/** construct a new HashMapEntry with the given key and value */
public HashMapEntry(Object key, Object value)
{
super(key, value);
}
/**
* if the key == HashMap.NULL_KEY, null is returned, otherwise the actual
* key is returned
*/
public Object getKey()
{
Object oResult = super.getKey();
return (oResult == HashMap.NULL_KEY) ? null : oResult;
}
}
// EOF -----------------------------------------------------------------------
}

@ -0,0 +1,238 @@
// This class is taken from the Classpath project.
// Please note the different copyright holder!
// The changes I did is this comment, the package line, some
// imports from java.util and some minor jdk12 -> jdk11 fixes.
// -- Jochen Hoenicke <jochen@gnu.org>
/////////////////////////////////////////////////////////////////////////////
// HashSet.java -- a class providing a HashMap-backet Set
//
// Copyright (c) 1998 by Jon A. Zeppieri (jon@eease.com)
//
// This program is free software; you can redistribute it and/or modify
// it under the terms of the GNU Library General Public License as published
// by the Free Software Foundation, version 2. (see COPYING.LIB)
//
// 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 Library General Public License for more details.
//
// You should have received a copy of the GNU Library General Public License
// along with this program; if not, write to the Free Software Foundation
// Inc., 59 Temple Place - Suite 330, Boston, MA 02111-1307 USA
/////////////////////////////////////////////////////////////////////////////
package jode.util;
import java.io.IOException;
import java.io.Serializable;
import java.io.ObjectInputStream;
import java.io.ObjectOutputStream;
/**
* This class provides a HashMap-backed implementation of the
* Set interface.
*
* Each element in the Set is a key in the backing HashMap; each key
* maps to a static token, denoting that the key does, in fact, exist.
*
* Most operations are O(1), assuming no hash collisions. In the worst
* case (where all hases collide), operations are O(n).
*
* HashSet is a part of the JDK1.2 Collections API.
*
* @author Jon Zeppieri
* @version $Revision$
* @modified $Id$
*/
public class HashSet extends AbstractSet
implements Set, Cloneable, Serializable
{
// INSTANCE VARIABLES -------------------------------------------------
/** the HashMap which backs this Set */
private transient HashMap map;
static final long serialVersionUID = -5024744406713321676L;
// CONSTRUCTORS ---------------------------------------------------------
/**
* construct a new, empty HashSet whose backing HashMap has the default
* capacity and loadFacor
*/
public HashSet()
{
map = new HashMap();
}
/**
* construct a new, empty HashSet whose backing HashMap has the supplied
* capacity and the default load factor
*
* @param initialCapacity the initial capacity of the backing
* HashMap
*/
public HashSet(int initialCapacity)
{
map = new HashMap(initialCapacity);
}
/**
* construct a new, empty HashSet whose backing HashMap has the supplied
* capacity and load factor
*
* @param initialCapacity the initial capacity of the backing
* HashMap
* @param loadFactor the load factor of the backing HashMap
*/
public HashSet(int initialCapacity, float loadFactor)
{
map = new HashMap(initialCapacity, loadFactor);
}
/**
* construct a new HashSet with the same elements as are in the supplied
* collection (eliminating any duplicates, of course; the backing HashMap
* will have the default capacity and load factor
*
* @param c a collection containing the elements with
* which this set will be initialized
*/
public HashSet(Collection c)
{
map = new HashMap();
addAll(c);
}
// PUBLIC METHODS ---------------------------------------------------------
/**
* adds the given Object to the set if it is not already in the Set,
* returns true if teh element was added, false otherwise
*
* @param o the Object to add to this Set
*/
public boolean add(Object o)
{
if (map.containsKey(o))
{
return false;
}
else
{
internalAdd(o);
return true;
}
}
/**
* empties this Set of all elements; this is a fast operation [O(1)]
*/
public void clear()
{
map.clear();
}
/**
* returns a shallow copy of this Set (the Set itself is cloned; its
* elements are not)
*/
public Object clone()
{
Iterator it = iterator();
HashSet clone = new HashSet(map.capacity, map.loadFactor);
while (it.hasNext())
clone.internalAdd(it.next());
return clone;
}
/**
* returns true if the supplied element is in this Set, false otherwise
*
* @param o the Object whose presence in this Set we are testing for
*/
public boolean contains(Object o)
{
return map.containsKey(o);
}
/**
* returns true if this set has no elements in it (size() == 0)
*/
public boolean isEmpty()
{
return map.isEmpty();
}
/**
* returns an Iterator over the elements of this Set; the Iterator allows
* removal of elements
*/
public Iterator iterator()
{
return map.keySet().iterator();
}
/**
* removes the supplied Object from this Set if it is in the Set; returns
* true if an element was removed, false otherwise
*/
public boolean remove(Object o)
{
return (map.remove(o) != null);
}
/**
* returns the number of elements in this Set
*/
public int size()
{
return map.size();
}
// PRIVATE METHODS -----------------------------------------------------------
/**
* adds the supplied element to this Set; this private method is used
* internally [clone()] instead of add(), because add() can be overridden
* to do unexpected things
*
* @param o the Object to add to this Set
*/
private void internalAdd(Object o)
{
map.put(o, Boolean.TRUE);
}
/** Serialize this Object in a manner which is binary-compatible with the JDK */
private void writeObject(ObjectOutputStream s) throws IOException
{
Iterator it = iterator();
s.writeInt(map.capacity);
s.writeFloat(map.loadFactor);
s.writeInt(size());
while (it.hasNext())
s.writeObject(it.next());
}
/** Deserialize this Object in a manner which is binary-compatible with the JDK */
private void readObject(ObjectInputStream s) throws IOException, ClassNotFoundException
{
int i, iSize, iCapacity;
float fLoadFactor;
Object oElement;
iCapacity = s.readInt();
fLoadFactor = s.readFloat();
iSize = s.readInt();
map = new HashMap(iCapacity, fLoadFactor);
for (i = 0; i < iSize; i++)
{
oElement = s.readObject();
internalAdd(oElement);
}
}
}

@ -0,0 +1,12 @@
.class public jode/test/Base
.super java/lang/Object
.field public static test I
.field public test J
.method <init>()V
aload_0
invokespecial java/lang/Object/<init>()V
return
.end method

@ -0,0 +1,29 @@
.class public jode/test/Child
.super jode/test/Base
.field private test I
.method <init>()V
.limit locals 1
.limit stack 2
aload_0
invokespecial jode/test/Base/<init>()V
getstatic jode/test/Base/test I
pop
aload_0
getfield jode/test/Base/test J
pop2
aload_0
getfield jode/test/Child/test I
pop
aload_0
getfield jode/test/Child/test J
pop2
return
.end method
.method public static main([Ljava/lang/String;)V
.limit locals 1
.limit stack 0
return
.end method
Loading…
Cancel
Save