/*
* Licensed to the Apache Software Foundation (ASF) under one
* or more contributor license agreements. See the NOTICE file
* distributed with this work for additional information
* regarding copyright ownership. The ASF licenses this file
* to you 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 net.jini.core.constraint;
import java.io.InvalidObjectException;
import java.lang.reflect.Array;
import java.lang.reflect.Modifier;
import java.rmi.RemoteException;
import java.security.Principal;
import java.util.Arrays;
import java.util.Collection;
/**
* Constraint utility methods.
*
* @author Sun Microsystems, Inc.
*
*/
class Constraint {
/**
* Non-instantiable.
*/
private Constraint() {}
/**
* Returns an array containing the first len elements of the specified
* array, with the same element type as the specified array.
*/
static Object[] trim(Object[] elements, int len) {
if (len == elements.length) {
return elements;
}
Object[] nelements =
(Object[]) Array.newInstance(
elements.getClass().getComponentType(), len);
System.arraycopy(elements, 0, nelements, 0, len);
return nelements;
}
/**
* Returns the sum of the hash codes of all elements of the given array.
*/
static int hash(Object[] elements) {
int h = 0;
for (int i = elements.length; --i >= 0; ) {
h += elements[i].hashCode();
}
return h;
}
/**
* Returns true if the two arrays are the same length and contain equal
* elements (but the order of the elements need not be the same in both
* arrays). The arrays must not contain duplicates.
*/
static boolean equal(Object[] arr1, Object[] arr2) {
if (arr1 == arr2) {
return true;
} else if (arr1.length != arr2.length) {
return false;
}
for (int i = arr1.length; --i >= 0; ) {
if (!contains(arr2, arr2.length, arr1[i])) {
return false;
}
}
return true;
}
/**
* Returns true if the non-null object is equal to any of the elements
* of the array with index less than i.
*/
static boolean contains(Object[] arr, int i, Object obj) {
while (--i >= 0) {
if (obj.equals(arr[i])) {
return true;
}
}
return false;
}
/**
* Returns a sorted comma-separated list of the toString form of the
* elements of the array. If the first element is a Class instance,
* then all of the elements must be Class instances.
*/
static String toString(Object[] a) {
if (a.length == 0) {
return "{}";
} else if (a.length == 1) {
String s;
if (a[0] instanceof Class) {
s = ((Class) a[0]).getName();
} else {
s = a[0].toString();
}
return "{" + s + "}";
}
String[] as = new String[a.length];
int len = a.length * 2;
if (a[0] instanceof Class) {
for (int i = a.length; --i >= 0; ) {
String val = ((Class) a[i]).getName();
as[i] = val;
len += val.length();
}
} else {
for (int i = a.length; --i >= 0; ) {
String val = a[i].toString();
as[i] = val;
len += val.length();
}
}
Arrays.sort(as);
StringBuffer buf = new StringBuffer(len);
buf.append("{");
for (int i = 0; i < as.length; i++) {
if (i > 0) {
buf.append(", ");
}
buf.append(as[i]);
}
buf.append("}");
return buf.toString();
}
/**
* Verifies that all elements of the collection are instances of principal
* classes, and that there is at least one element, and returns an array
* of the elements, in arbitrary order, with duplicates removed.
*/
static Principal[] reduce(Collection c) {
try {
return reduce0((Principal[]) c.toArray(new Principal[c.size()]));
} catch (ArrayStoreException e) {
throw new IllegalArgumentException(
"element of collection is not a Principal");
}
}
/**
* Verifies that there is at least one element, and returns a new array of
* the elements, in arbitrary order, with duplicates removed.
*/
static Principal[] reduce(Principal[] principals) {
return reduce0((Principal[]) principals.clone());
}
/**
* Verifies that there is at least one element, and returns an array of
* the elements, in arbitrary order, with duplicates removed. The
* argument may be modified. If no duplicates need to be removed, the
* argument may be returned.
*/
private static Principal[] reduce0(Principal[] principals) {
if (principals.length == 0) {
throw new IllegalArgumentException(
"cannot create constraint with no elements");
}
int i = 0;
for (int j = 0; j < principals.length; j++) {
Principal p = principals[j];
if (p == null) {
throw new NullPointerException("elements cannot be null");
}
if (!contains(principals, i, p)) {
principals[i++] = p;
}
}
return (Principal[]) trim(principals, i);
}
/**
* Verifies that there is at least one element, and that there are no
* duplicates;
*/
static void verify(Principal[] principals) throws InvalidObjectException {
if (principals == null || principals.length == 0) {
throw new InvalidObjectException(
"cannot create constraint with no elements");
}
for (int i = principals.length; --i >= 0; ) {
Principal p = principals[i];
if (p == null) {
throw new InvalidObjectException("elements cannot be null");
}
if (contains(principals, i, p)) {
throw new InvalidObjectException(
"cannot create constraint with duplicate elements");
}
}
}
/**
* Verifies that all elements of the collection are classes, and that
* there is at least one element, and returns an array of the elements,
* in arbitrary order, with redundant classes removed as follows. For
* any two classes c1 and c2, if c1.isAssignableFrom(c2) is true, then
* c2 is removed if keepSupers is true, otherwise c1 is removed.
*/
static Class[] reduce(Collection c, boolean keepSupers) {
try {
return reduce0((Class[]) c.toArray(new Class[c.size()]),
keepSupers);
} catch (ArrayStoreException e) {
throw new IllegalArgumentException(
"element of collection is not a Class");
}
}
/**
* Verifies that there is at least one element, and returns a new array
* of the elements, in arbitrary order, with redundant classes removed as
* follows. For any two classes c1 and c2, if c1.isAssignableFrom(c2) is
* true, then c2 is removed if keepSupers is true, otherwise c1 is removed.
*/
static Class[] reduce(Class[] classes, boolean keepSupers) {
return reduce0((Class[]) classes.clone(), keepSupers);
}
/**
* Verifies that there is at least one element, and returns an array
* of the elements, in arbitrary order, with redundant classes removed as
* follows. For any two classes c1 and c2, if c1.isAssignableFrom(c2) is
* true, then c2 is removed if keepSupers is true, otherwise c1 is
* removed. The array argument may be modified. If no classes need to
* be removed, the array argument may be returned.
*
* Note #1: Here we're removing ck, and we close the gap by moving the
* last already-processed element (common[i - 1]) down to replace it. We
* don't need to arraycopy all of the elements down because order doesn't
* matter. In the degenerate case (k == i - 1) we copy an element onto
* itself, but that does no harm.
*/
private static Class[] reduce0(Class[] classes, boolean keepSupers) {
if (classes.length == 0) {
throw new IllegalArgumentException(
"cannot create constraint with no elements");
}
int i = 0;
outer:
for (int j = 0; j < classes.length; j++) {
Class cj = classes[j];
verify(cj);
for (int k = i; --k >= 0; ) {
Class ck = classes[k];
if (keepSupers ? ck.isAssignableFrom(cj) :
cj.isAssignableFrom(ck))
{
continue outer;
}
if (keepSupers ? cj.isAssignableFrom(ck) :
ck.isAssignableFrom(cj))
{
classes[k] = classes[--i]; // see note #1
}
}
classes[i++] = cj;
}
return (Class[]) trim(classes, i);
}
/**
* Verifies that the class is not a primitive or array class, and
* either isn't final or is assignable to Principal.
*/
static void verify(Class c) {
if (c == null) {
throw new NullPointerException("elements cannot be null");
}
if (c.isArray() || c.isPrimitive() ||
(Modifier.isFinal(c.getModifiers()) &&
!Principal.class.isAssignableFrom(c)))
{
throw new IllegalArgumentException("invalid class");
}
}
/**
* Verifies that there is at least one element, that they are all
* valid classes, and that no class is assignable to any other class.
*/
static void verify(Class[] classes) throws InvalidObjectException {
if (classes == null || classes.length == 0) {
throw new InvalidObjectException(
"cannot create constraint with no elements");
}
for (int i = classes.length; --i >= 0; ) {
Class ci = classes[i];
if (ci == null) {
throw new InvalidObjectException("elements cannot be null");
}
if (ci.isArray() || ci.isPrimitive() ||
(Modifier.isFinal(ci.getModifiers()) &&
!Principal.class.isAssignableFrom(ci)))
{
throw new InvalidObjectException("invalid class");
}
for (int j = i; --j >= 0; ) {
Class cj = classes[j];
if (ci.isAssignableFrom(cj) || cj.isAssignableFrom(ci)) {
throw new InvalidObjectException(
"cannot create constraint with redundant elements");
}
}
}
}
}