git-svn-id: https://svn.code.sf.net/p/jode/code/trunk@995 379699f6-c40d-0410-875b-85095c16579estable
parent
59c6451fa8
commit
8c31bdd062
@ -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…
Reference in new issue