/*
* Copyright (C) NetStruxr, Inc. All rights reserved.
*
* This software is published under the terms of the NetStruxr
* Public Software License version 0.5, a copy of which has been
* included with this distribution in the LICENSE.NPL file. */
package er.extensions.foundation;
import java.util.Vector;
import com.webobjects.eoaccess.EOEntity;
import com.webobjects.foundation.NSArray;
import com.webobjects.foundation._NSCollectionPrimitives;
/**
* Simple class to use multiple objects as
* a single key for a dictionary or HashMap.
* The goal of this class is to be very fast.
*/
public class ERXMultiKey {
/** holds the object array of keys */
private Object[] _keys;
/** caches the number of keys */
private short _keyCount;
private int _hashCode;
/**
* Constructs a multi-key for a given
* number.
* @param keyCount number of keys
*/
private ERXMultiKey(short keyCount) {
_keyCount=keyCount;
_keys=new Object[keyCount];
}
/**
* Constructs a multi-key.
*/
public ERXMultiKey() {
_keyCount=0;
_keys=_NSCollectionPrimitives.EmptyArray;
recomputeHashCode();
}
/**
* Constructs a multi-key for a given
* object array.
* @param keys object array
*/
public ERXMultiKey(Object[] keys) {
this((short)keys.length);
System.arraycopy(keys,0,_keys,0,_keyCount);
recomputeHashCode();
}
/**
* Constructs a multi-key for a given
* array.
* @param keys array of keys
*/
public ERXMultiKey(NSArray<Object> keys) {
this((short)keys.count());
for (int i=0; i<keys.count(); i++) _keys[i]=keys.objectAtIndex(i);
recomputeHashCode();
}
/**
* Constructs a multi-key for a given
* vector.
* @param keys vector of keys
*/
public ERXMultiKey(Vector<Object> keys) {
this ((short)keys.size());
for (int i=0; i<keys.size(); i++) _keys[i]=keys.elementAt(i);
recomputeHashCode();
}
/**
* Constructs a multi-key for a given
* list of keys.
* @param key one key
* @param keys additional keys
*/
public ERXMultiKey(Object key, Object ... keys) {
this((short)(keys.length + 1));
_keys[0] = key;
System.arraycopy(keys,0,_keys,1,_keyCount-1);
recomputeHashCode();
}
/**
* Method used to return a copy of the object array
* of keys for the current multi-key.
* @return object array of keys
*/
public final Object[] keys() {
Object[] keys;
if (_keyCount == 0) {
keys = _keys;
} else {
keys = new Object[_keyCount];
System.arraycopy(_keys, 0, keys, 0, _keyCount);
}
return keys;
}
/**
* Method used to return the object array
* of keys for the current multi-key.<br>
* DO NOT MODIFY!
* @return object array of keys
*/
public final Object[] keysNoCopy() {
return _keys;
}
/**
* Calculates a unique hash code for
* the given array of keys.
* @return unique hash code for the array
* of keys.
*/
@Override
public final int hashCode() {
return _hashCode;
}
/**
* Recomputes the hash code if you ever changes the keys array directly
*/
public final void recomputeHashCode() {
int result = 0;
for (int i=0; i<_keyCount; i++) {
final Object theKey = _keys[i];
if ( theKey != null ) {
result ^= theKey.hashCode();
result = ( result << 1 ) | ( result >>> 31 );
}
}
_hashCode = result;
}
/**
* Method used to compare two ERXMultiKeys.
* A multi key is equal to another multi key
* if the number of keys are equal and all
* of the keys are either both null or <code>
* equals</code>.
* @param o object to be compared
* @return result of comparison
*/
@Override
public final boolean equals(Object o) {
if (o instanceof ERXMultiKey) {
ERXMultiKey o2 = (ERXMultiKey) o;
if (this == o2)
return true;
if (_keyCount!=o2._keyCount)
return false;
if (hashCode()!=o2.hashCode())
return false;
for (int i=0; i<_keyCount; i++) {
Object k=o2._keys[i];
Object m=_keys[i];
if (m!=k && (m==null || k==null || !m.equals(k)))
return false;
}
return true;
}
return false;
}
/**
* String representation of the multi-key.
* @return string representation of key.
*/
@Override
public String toString() {
StringBuilder result = new StringBuilder("(");
for (short i=0; i<_keys.length; i++) {
Object o=_keys[i];
result.append(o instanceof EOEntity ? ((EOEntity)o).name() : o != null ? o.toString() : "<NULL>");
if(i != _keys.length-1)
result.append(", ");
}
result.append(')');
return result.toString();
}
}