
org.commonvox.collections.KeyComponentProfile Maven / Gradle / Ivy
Go to download
Show more of this group Show more artifacts with this name
Show all versions of ordered-set Show documentation
Show all versions of ordered-set Show documentation
An OrderedSet extends the standard Java collections framework
to provide composite-key based ordering of a set of values (analogous
to composite key ordering in a relational database).
The newest version!
/*
* Copyright (C) 2016 Daniel Vimont
*
* Licensed under the Apache License, Version 2.0 (the "License");
* you may not use this file except in compliance with the License.
* You may obtain a copy of the License at
*
* http://www.apache.org/licenses/LICENSE-2.0
*
* Unless required by applicable law or agreed to in writing, software
* distributed under the License is distributed on an "AS IS" BASIS,
* WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
* See the License for the specific language governing permissions and
* limitations under the License.
*
*/
package org.commonvox.collections;
import java.io.Serializable;
import java.lang.reflect.InvocationTargetException;
import java.lang.reflect.Method;
import java.lang.reflect.Modifier;
import java.lang.reflect.ParameterizedType;
import java.lang.reflect.Type;
import java.util.ArrayList;
import java.util.Arrays;
import java.util.Collection;
import java.util.Comparator;
import java.util.HashSet;
import java.util.List;
import java.util.Set;
/**
* An array of KeyComponentProfile objects specifies the composite-key
* structure which orders the elements in an {@link OrderedSet}.
* The construction of a KeyComponentProfile establishes how its
* corresponding keyComponentClass-objects are to be automatically retrieved
* and ordered when valueClass-elements are added to an {@link OrderedSet}
* to which the KeyComponentProfile belongs.
* A single KeyComponentProfile object may be used in multiple
* {@link OrderedSet}s.
*
* Usage examples can be found
* here.
*
* @author Daniel Vimont
* @param The valueClass, i.e. the class of elements contained in
* the {@link OrderedSet} to which the KeyComponentProfile
* belongs.
* The valueClass must match the {@literal } type-parameter
* of any {@link OrderedSet} to which the KeyComponentProfile
* belongs (enforced at compile-time).
*/
public class KeyComponentProfile implements Serializable {
private final Class valueClass;
private final Class> keyComponentClass;
private final List valueClassMethodsThatReturnKeyComponents;
private final KeyComponentBasis indexComponentBasis;
private final String indexComponentName;
private final Comparator> keyComponentClassComparator;
private final int immutableHashCode;
static final Method IDENTITY_METHOD;
static final String INVALID_METHOD_MESSAGE_OPENER =
"Invalid method(s) submitted in the KeyComponentProfile's "
+ "methodsThatReturnKeyComponents array: ";
static enum KeyComponentBasis {
CLASS, METHOD, IDENTITY // , FIELD
};
static {
Method dummyMethod;
try {
dummyMethod = GetThisObject.class.getMethod("getThis");
}
catch (NoSuchMethodException e) {
throw new InternalError("Internal coding error has resulted in failure "
+ "to set IDENTITY_METHOD constant in <"
+ KeyComponentProfile.class.getSimpleName() + "> class.");
}
IDENTITY_METHOD = dummyMethod;
}
/**
* THIS PACKAGE-PRIVATE CONSTRUCTOR USED ONLY INTERNALLY BY MapNode class!
* Constructs an IDENTITY KeyComponentProfile for the
* {@link OrderedSet} to which it belongs;
* the ordering maintained by such a KeyComponentProfile is based
* upon the valueClass itself (as stipulated by the
* {@literal } type-parameter).
*/
KeyComponentProfile() {
this.indexComponentBasis = KeyComponentBasis.IDENTITY;
this.valueClass = null;
this.indexComponentName = KeyComponentBasis.IDENTITY.toString();
this.keyComponentClass = Object.class;
this.valueClassMethodsThatReturnKeyComponents = new ArrayList();
this.valueClassMethodsThatReturnKeyComponents.add(IDENTITY_METHOD);
this.keyComponentClassComparator = null;
this.immutableHashCode = computeImmutableHashCode();
}
/**
* Constructs a KeyComponentProfile belonging to an
* {@link OrderedSet} containing elements of
* class valueClass;
* the ordering maintained by a KeyComponentProfile is stipulated
* by the keyComponentClass parameter, which must either be the same
* as the valueClass or must correspond to a class of
* objects returned by one or more of the valueClass's parameterless
* "get" methods; submission of methodsReturningKeyComponents vararg
* parameters is optional.
*
* Usage examples can be found
* here.
*
* CONSTRUCTION
*
* During construction of the KeyComponentProfile, automatic
* assembly of an internal valueClassMethodsThatReturnKeyComponents
* array is done via Java reflection. Any non-private, non-static,
* parameterless method of the valueClass which returns either a
* single instance of a keyComponentClass-object or a Collection of
* keyComponentClass-objects is added to the
* valueClassMethodsThatReturnKeyComponents array.
* A subset of these methods may optionally be stipulated by the programmer
* via the optional methodsReturningKeyComponents vararg parameters;
* if one or more of these vararg parameters are submitted, the
* valueClassMethodsThatReturnKeyComponents array is limited to this
* subset of the valueClass "get" methods.
*
* USAGE IN AN {@link OrderedSet}
*
* Subsequent to KeyComponentProfile construction, as
* valueClass-elements are
* {@link OrderedSet#add(java.lang.Object) add}ed
* to an {@link OrderedSet}
* to which the KeyComponentProfile belongs,
* keyComponentClass-objects are automatically retrieved and ordered.
* If the keyComponentClass equals the valueClass, then the
* valueClass-element itself serves the role of
* keyComponentClass-object. Otherwise,
* keyComponentClass-objects are retrieved via reflection-based
* invocation of the "get" methods in the
* valueClassMethodsThatReturnKeyComponents array.
* All retrieved keyComponentClass-objects are internally ordered via backing
*
* TreeMaps, either in
*
* natural order or
*
* hashCode order.
* If the keyComponentClass implements the
* Comparable interface, its objects are ordered
* in natural order; otherwise they are ordered in hashCode order.
*
* @param keyComponentClass
* @param valueClass The class of elements contained in the
* {@link OrderedSet} to which the KeyComponentProfile
* belongs.
* This value must match that of the {@literal } type-parameter
* (enforced at compile-time).
* @param keyComponentClass The class of the objects to be retrieved and
* ordered by the KeyComponentProfile.
* The keyComponentClass must either be the same
* as the valueClass or must correspond to a class of
* objects returned by one or more of the valueClass's parameterless
* "get" methods.
* @param methodsReturningKeyComponents an optional subset of the
* valueClass's parameterless "get" methods; if submitted, keyComponent
* reflection-based retrieval (as described above) will be limited to
* invocation of this subset of methods.
*/
public KeyComponentProfile(Class valueClass,
Class keyComponentClass,
Method... methodsReturningKeyComponents) {
this(valueClass, keyComponentClass, null, methodsReturningKeyComponents);
}
/**
* Constructs a KeyComponentProfile belonging to an
* {@link OrderedSet} containing elements of
* class valueClass;
* the ordering maintained by a KeyComponentProfile is stipulated
* by the keyComponentClass parameter, which must either be the same
* as the valueClass or must correspond to a class of
* objects returned by one or more of the valueClass's parameterless
* "get" methods; submission of methodsReturningKeyComponents vararg
* parameters is optional.
*
* Usage examples can be found
* here.
*
* CONSTRUCTION
*
* During construction of the KeyComponentProfile, automatic
* assembly of an internal valueClassMethodsThatReturnKeyComponents
* array is done via Java reflection. Any non-private, non-static,
* parameterless method of the valueClass which returns either a
* single instance of a keyComponentClass-object or a Collection of
* keyComponentClass-objects is added to the
* valueClassMethodsThatReturnKeyComponents array.
* A subset of these methods may optionally be stipulated by the programmer
* via the optional methodsReturningKeyComponents vararg parameters;
* if one or more of these vararg parameters are submitted, the
* valueClassMethodsThatReturnKeyComponents array is limited to this
* subset of the valueClass "get" methods.
*
* USAGE IN AN {@link OrderedSet}
*
* Subsequent to KeyComponentProfile construction, as
* valueClass-elements are
* {@link OrderedSet#add(java.lang.Object) add}ed
* to an {@link OrderedSet}
* to which the KeyComponentProfile belongs,
* keyComponentClass-objects are automatically retrieved and ordered.
* If the keyComponentClass equals the valueClass, then the
* valueClass-element itself serves the role of
* keyComponentClass-object. Otherwise,
* keyComponentClass-objects are retrieved via reflection-based
* invocation of the "get" methods in the
* valueClassMethodsThatReturnKeyComponents array.
* The retrieved keyComponentClass-objects are then ordered using a
* TreeMap structure, placing the
* keyComponentClass-objects in the order maintained by the
* Comparator stipulated in the keyComponentClassComparator parameter.
*
* @param keyComponentClass
* @param valueClass The class of elements contained in the
* {@link OrderedSet} to which the KeyComponentProfile
* belongs.
* This value must match that of the {@literal } type-parameter
* (enforced at compile-time).
* @param keyComponentClass The class of the objects to be retrieved and
* ordered by the KeyComponentProfile.
* The keyComponentClass must either be the same
* as the valueClass or must correspond to a class of
* objects returned by one or more of the valueClass's parameterless
* "get" methods.
* @param keyComponentClassComparator Comparator to be used for ordering
* retrieved keyComponentClass-objects.
* @param methodsReturningKeyComponents an optional subset of the
* valueClass's parameterless "get" methods; if submitted, keyComponent
* reflection-based retrieval (as described above) will be limited to
* invocation of this subset of methods.
*/
public KeyComponentProfile(Class valueClass, Class keyComponentClass,
Comparator super K> keyComponentClassComparator,
Method... methodsReturningKeyComponents) {
this.valueClass = valueClass;
this.keyComponentClass = keyComponentClass;
this.keyComponentClassComparator = keyComponentClassComparator;
StringBuilder nameBuilder = new StringBuilder();
nameBuilder.append(this.keyComponentClass.getSimpleName());
if (this.valueClass.equals(this.keyComponentClass)) {
this.indexComponentBasis = KeyComponentBasis.IDENTITY;
this.valueClassMethodsThatReturnKeyComponents = new ArrayList();
this.valueClassMethodsThatReturnKeyComponents.add(IDENTITY_METHOD);
} else {
if (methodsReturningKeyComponents == null
|| methodsReturningKeyComponents.length == 0) {
this.indexComponentBasis = KeyComponentBasis.CLASS;
this.valueClassMethodsThatReturnKeyComponents
= getMethodsThatReturnObjectsOfKeyComponentClass();
if (this.valueClassMethodsThatReturnKeyComponents.isEmpty()) {
throw new IllegalArgumentException("Invalid keyComponentClass, <"
+ this.keyComponentClass.getSimpleName() + ">, submitted for "
+ "construction of KeyComponentProfile. No parameterless "
+ "method returning object of this keyComponentClass "
+ "(or Collection of objects of this keyComponentClass) "
+ "is declared by class (or superclass of) valueClass, <"
+ this.valueClass.getSimpleName() + ">");
}
} else {
this.indexComponentBasis = KeyComponentBasis.METHOD;
this.valueClassMethodsThatReturnKeyComponents =
Arrays.asList(methodsReturningKeyComponents);
if (this.keyComponentClass != getClassOfObjectsReturnedByMethods()) {
throw new IllegalArgumentException(INVALID_METHOD_MESSAGE_OPENER
+ "method(s) return object(s) of class <"
+ getClassOfObjectsReturnedByMethods().getSimpleName()
+ "> which differs from submitted keyComponentClass <"
+ this.keyComponentClass.getSimpleName() + ">");
}
}
for (Method method : this.valueClassMethodsThatReturnKeyComponents) {
nameBuilder.append(";").append(method.getName());
}
}
this.indexComponentName = nameBuilder.toString();
this.immutableHashCode = computeImmutableHashCode();
}
private List getMethodsThatReturnObjectsOfKeyComponentClass() {
List methodSet = new ArrayList();
// examine all declared and inherited non-static PUBLIC methods
for (Method method : this.valueClass.getMethods()) {
if (Modifier.isStatic(method.getModifiers())) {
continue;
}
if (methodIsParameterlessAndReturnsObjectsOfKeyComponentClass(method)) {
methodSet.add(method);
}
}
// examine all declared non-static PROTECTED and PACKAGE-PRIVATE methods
for (Method method : this.valueClass.getDeclaredMethods()) {
int modifiers = method.getModifiers();
if (Modifier.isStatic(modifiers) || Modifier.isPublic(modifiers)
|| Modifier.isPrivate(modifiers)) {
continue;
}
if (methodIsParameterlessAndReturnsObjectsOfKeyComponentClass(method)) {
method.setAccessible(true);
methodSet.add(method);
}
}
return methodSet;
}
private boolean methodIsParameterlessAndReturnsObjectsOfKeyComponentClass(Method method) {
if (method.getParameterCount() > 0) {
return false;
}
if (method.getReturnType().equals(this.keyComponentClass)) {
return true;
} else if (Collection.class.isAssignableFrom(method.getReturnType())) {
ParameterizedType collectionType
= (ParameterizedType) method.getGenericReturnType();
Type typeOfObjectsInCollection
= collectionType.getActualTypeArguments()[0];
if (typeOfObjectsInCollection.getTypeName().equals(
this.keyComponentClass.getTypeName())) {
return true;
}
}
return false;
}
private Class> getClassOfObjectsReturnedByMethods() {
Class returnedClass = null;
for (Method method : this.valueClassMethodsThatReturnKeyComponents) {
if (method == null) {
throw new IllegalArgumentException(INVALID_METHOD_MESSAGE_OPENER
+ ".");
}
if (!method.getDeclaringClass().isAssignableFrom(this.valueClass)) {
throw new IllegalArgumentException(INVALID_METHOD_MESSAGE_OPENER
+ "<" + method.getName() + "> method's declaring class is <"
+ method.getDeclaringClass().getSimpleName()
+ ">, which is not assignable from valueClass, <"
+ this.valueClass.getSimpleName() + ">.");
}
if (method.getParameterCount() > 0) {
throw new IllegalArgumentException(INVALID_METHOD_MESSAGE_OPENER
+ "<" + method.getName()
+ "> method is not parameterless as required.");
}
if (Collection.class.isAssignableFrom(method.getReturnType())) {
ParameterizedType collectionType
= (ParameterizedType) method.getGenericReturnType();
Type typeOfObjectsInCollection
= collectionType.getActualTypeArguments()[0];
try {
Class classInCollection
= Class.forName(typeOfObjectsInCollection.getTypeName());
if (returnedClass == null) {
returnedClass = classInCollection;
} else if (!returnedClass.equals(classInCollection)) {
throw new IllegalArgumentException(INVALID_METHOD_MESSAGE_OPENER
+ "<" + method.getName()
+ "> method returns Collection of objects of class "
+ "which differs from class of object returned by "
+ "other method(s) in the KeyComponentProfile's "
+ " array.");
}
}
catch (ClassNotFoundException e) {
throw new IllegalArgumentException(INVALID_METHOD_MESSAGE_OPENER
+ "<" + method.getName()
+ "> method returns Collection of objects "
+ "belonging to a Class that cannot be found.", e);
}
} else {
if (returnedClass == null) {
returnedClass = method.getReturnType();
} else if (!returnedClass.equals(method.getReturnType())) {
throw new IllegalArgumentException(INVALID_METHOD_MESSAGE_OPENER
+ "<" + method.getName()
+ "> method returns object of class which differs from "
+ "class of object returned by other method(s) in the "
+ "KeyComponentProfile's "
+ " array.");
}
}
}
return returnedClass;
}
KeyComponentBasis getKeyComponentBasis() {
return this.indexComponentBasis;
}
Class> getKeyComponentClass() {
return this.keyComponentClass;
}
Comparator getKeyComponentClassComparator() {
return this.keyComponentClassComparator;
}
List getKeyComponentGetMethods() {
return this.valueClassMethodsThatReturnKeyComponents;
}
private int computeImmutableHashCode() {
int hash = 5;
hash = 13 * hash
+ (this.valueClass != null ? this.valueClass.hashCode() : 0);
hash = 13 * hash + (this.keyComponentClass != null ? this.keyComponentClass.hashCode() : 0);
hash = 13 * hash + (this.valueClassMethodsThatReturnKeyComponents != null ?
this.valueClassMethodsThatReturnKeyComponents.hashCode() : 0);
hash = 13 * hash + (this.indexComponentBasis != null ?
this.indexComponentBasis.hashCode() : 0);
hash = 13 * hash + (this.keyComponentClassComparator != null ?
this.keyComponentClassComparator.hashCode() : 0);
return hash;
}
@Override
public int hashCode() {
return immutableHashCode;
}
@Override
public boolean equals(Object other) {
if (other == null) {
return false;
}
if (this.getClass() != other.getClass()) {
return false;
}
return this.immutableHashCode ==
((KeyComponentProfile>)other).immutableHashCode;
}
// public int compareTo(KeyComponentProfile o) {
// return this.immutableHashCode - o.immutableHashCode;
// }
/**
* Returns a String representation of this object. The String representation
* consists of the KeyComponentProfile's basis (either CLASS, METHOD, or
* IDENTITY) and the name of its basis entity.
* @return a String representation of this object
*/
@Override
public String toString() {
return "";
}
Set
© 2015 - 2025 Weber Informatics LLC | Privacy Policy