diff options
Diffstat (limited to 'bundles/org.eclipse.equinox.p2.metadata/src/org/eclipse/equinox/internal/p2/metadata/expression/CoercingComparator.java')
-rw-r--r-- | bundles/org.eclipse.equinox.p2.metadata/src/org/eclipse/equinox/internal/p2/metadata/expression/CoercingComparator.java | 392 |
1 files changed, 392 insertions, 0 deletions
diff --git a/bundles/org.eclipse.equinox.p2.metadata/src/org/eclipse/equinox/internal/p2/metadata/expression/CoercingComparator.java b/bundles/org.eclipse.equinox.p2.metadata/src/org/eclipse/equinox/internal/p2/metadata/expression/CoercingComparator.java new file mode 100644 index 000000000..fde3c4eee --- /dev/null +++ b/bundles/org.eclipse.equinox.p2.metadata/src/org/eclipse/equinox/internal/p2/metadata/expression/CoercingComparator.java @@ -0,0 +1,392 @@ +/******************************************************************************* + * Copyright (c) 2010 Cloudsmith Inc. and others. + * All rights reserved. This program and the accompanying materials + * are made available under the terms of the Eclipse Public License v1.0 + * which accompanies this distribution, and is available at + * http://www.eclipse.org/legal/epl-v10.html + * + * Contributors: + * Cloudsmith Inc. - initial API and implementation + *******************************************************************************/ +package org.eclipse.equinox.internal.p2.metadata.expression; + +import java.lang.reflect.AccessibleObject; +import java.lang.reflect.Constructor; +import java.security.AccessController; +import java.security.PrivilegedAction; +import java.util.Comparator; +import org.eclipse.equinox.internal.p2.metadata.MetadataActivator; +import org.eclipse.equinox.p2.metadata.Version; + +/** + * A comparator that performs coercion if needed before comparison. + * @param <T> The type for the comparator. + */ +public abstract class CoercingComparator<T> { + static class BooleanCoercer extends CoercingComparator<Boolean> { + public int compare(Boolean o1, Boolean o2) { + return o1.compareTo(o2); + } + + @Override + Boolean coerce(Object v) { + if (v instanceof Boolean) + return (Boolean) v; + if (v instanceof String) { + String sv = ((String) v).trim(); + if (sv.equalsIgnoreCase("true")) //$NON-NLS-1$ + return Boolean.TRUE; + if (sv.equalsIgnoreCase("false")) //$NON-NLS-1$ + return Boolean.FALSE; + } + throw uncoercable(v); + } + + @Override + Class<Boolean> getCoerceClass() { + return Boolean.class; + } + + @Override + int getCoercePrio() { + return 7; + } + } + + static class ClassCoercer extends CoercingComparator<Class<?>> { + public int compare(Class<?> o1, Class<?> o2) { + return o1.getName().compareTo(o2.getName()); + } + + @Override + Class<?> coerce(Object v) { + if (v instanceof Class<?>) + return (Class<?>) v; + if (v instanceof String) { + try { + return MetadataActivator.context.getBundle().loadClass(((String) v).trim()); + } catch (Exception e) { + // + } + } + throw uncoercable(v); + } + + @SuppressWarnings("unchecked") + @Override + Class<Class<?>> getCoerceClass() { + Class<?> cls = Class.class; + return (Class<Class<?>>) cls; + } + + @Override + int getCoercePrio() { + return 11; + } + } + + static class FromStringCoercer<T extends Comparable<Object>> extends CoercingComparator<T> { + private final Class<T> coerceClass; + private final Constructor<T> constructor; + + public FromStringCoercer(Class<T> coerceClass, Constructor<T> constructor) { + this.coerceClass = coerceClass; + this.constructor = constructor; + } + + @Override + T coerce(Object v) { + if (v instanceof String) { + try { + return constructor.newInstance(new Object[] {((String) v).trim()}); + } catch (Exception e) { + // + } + } + throw uncoercable(v); + } + + @Override + int compare(T o1, T o2) { + return o1.compareTo(o2); + } + + @Override + Class<T> getCoerceClass() { + return coerceClass; + } + + @Override + int getCoercePrio() { + return 0; + } + } + + static class IntegerCoercer extends CoercingComparator<Integer> { + public int compare(Integer o1, Integer o2) { + return o1.compareTo(o2); + } + + @Override + Integer coerce(Object v) { + if (v instanceof Integer) + return (Integer) v; + if (v instanceof Number) + return new Integer(((Number) v).intValue()); + if (v instanceof String) { + try { + return Integer.valueOf(((String) v).trim()); + } catch (NumberFormatException e) { + // + } + } + throw uncoercable(v); + } + + @Override + Class<Integer> getCoerceClass() { + return Integer.class; + } + + @Override + int getCoercePrio() { + return 6; + } + } + + static class LongCoercer extends CoercingComparator<Long> { + public int compare(Long o1, Long o2) { + return o1.compareTo(o2); + } + + @Override + Long coerce(Object v) { + if (v instanceof Long) + return (Long) v; + if (v instanceof Number) + return new Long(((Number) v).longValue()); + if (v instanceof String) { + try { + return Long.valueOf(((String) v).trim()); + } catch (NumberFormatException e) { + // + } + } + throw uncoercable(v); + } + + @Override + Class<Long> getCoerceClass() { + return Long.class; + } + + @Override + int getCoercePrio() { + return 5; + } + } + + static class StringCoercer extends CoercingComparator<String> { + public int compare(String o1, String o2) { + return o1.compareTo(o2); + } + + @Override + String coerce(Object v) { + if (v instanceof Class<?>) + return ((Class<?>) v).getName(); + return v.toString(); + } + + @Override + Class<String> getCoerceClass() { + return String.class; + } + + @Override + int getCoercePrio() { + return 10; + } + } + + static class VersionCoercer extends CoercingComparator<Version> { + public int compare(Version o1, Version o2) { + return o1.compareTo(o2); + } + + boolean canCoerceTo(Class<?> cls) { + return Version.class.isAssignableFrom(cls); + } + + @Override + Version coerce(Object v) { + if (v instanceof Version) + return (Version) v; + if (v instanceof String) + return Version.create((String) v); + if (v instanceof String) { + try { + return Version.create((String) v); + } catch (NumberFormatException e) { + // + } + } + throw uncoercable(v); + } + + @Override + Class<Version> getCoerceClass() { + return Version.class; + } + + @Override + int getCoercePrio() { + return 1; + } + } + + private static class SetAccessibleAction implements PrivilegedAction<Object> { + private final AccessibleObject accessible; + + SetAccessibleAction(AccessibleObject accessible) { + this.accessible = accessible; + } + + public Object run() { + accessible.setAccessible(true); + return null; + } + } + + private static CoercingComparator<?>[] coercers = {new ClassCoercer(), new BooleanCoercer(), new LongCoercer(), new IntegerCoercer(), new VersionCoercer(), new StringCoercer()}; + + private static final Class<?>[] constructorType = new Class<?>[] {String.class}; + + /** + * Finds the comparator for <code>a</code> and <code>b</code> and delegates the coercion/comparison to the comparator + * according to priority. + * @param o1 the first object to be compared. + * @param o2 the second object to be compared. + * @return The result of the comparison + * @throws IllegalArgumentException if no comparator was found or if coercion was impossible + * @see Comparator#compare(Object, Object) + */ + @SuppressWarnings({"unchecked", "rawtypes"}) + public static <TA extends Object, TB extends Object> int coerceAndCompare(TA o1, TB o2) throws IllegalArgumentException { + if (o1 == null || o2 == null) + throw new IllegalArgumentException("Cannot compare null to anything"); //$NON-NLS-1$ + + if (o1 instanceof Comparable && o1.getClass().isAssignableFrom(o2.getClass())) + return ((Comparable) o1).compareTo(o2); + + if (o2 instanceof Comparable && o2.getClass().isAssignableFrom(o1.getClass())) + return -((Comparable) o2).compareTo(o1); + + CoercingComparator<TA> ca = getComparator(o1, o2); + CoercingComparator<TB> cb = getComparator(o2, o1); + return ca.getCoercePrio() <= cb.getCoercePrio() ? ca.compare(o1, ca.coerce(o2)) : cb.compare(cb.coerce(o1), o2); + } + + /** + * Finds the comparator for <code>a</code> and <code>b</code> and delegates the coercion/equal to the comparator + * according to priority. + * @param o1 the first object to be compared. + * @param o2 the second object to be compared. + * @return The result of the equality test + * @throws IllegalArgumentException if no comparator was found or if coercion was impossible + * @see Object#equals(Object) + */ + public static <TA extends Object, TB extends Object> boolean coerceAndEquals(TA o1, TB o2) throws IllegalArgumentException { + if (o1 == o2) + return true; + + if (o1 == null || o2 == null) + return false; + + if (o1.getClass() != o2.getClass()) { + if (o1.getClass().isAssignableFrom(o2.getClass())) + return o1.equals(o2); + if (o2.getClass().isAssignableFrom(o1.getClass())) + return o2.equals(o1); + try { + CoercingComparator<TA> ca = getComparator(o1, o2); + CoercingComparator<TB> cb = getComparator(o2, o1); + return ca.getCoercePrio() <= cb.getCoercePrio() ? o1.equals(ca.coerce(o2)) : o2.equals(cb.coerce(o1)); + } catch (IllegalArgumentException e) { + // + } + } + return o1.equals(o2); + } + + /** + * Obtains the coercing comparator for the given <code>value</code>. + * @param value The value + * @return The coercing comparator + */ + @SuppressWarnings("unchecked") + public static <V extends Object> CoercingComparator<V> getComparator(V value, Object v2) { + Class<V> vClass = (Class<V>) value.getClass(); + CoercingComparator<?>[] carr = coercers; + int idx = carr.length; + while (--idx >= 0) { + CoercingComparator<?> c = carr[idx]; + if (c.canCoerceTo(vClass)) { + CoercingComparator<V> cv = (CoercingComparator<V>) c; + return cv; + } + } + + if (value instanceof Comparable<?> && v2 instanceof String) { + Class<Comparable<Object>> cClass = (Class<Comparable<Object>>) vClass; + Constructor<Comparable<Object>> constructor; + try { + constructor = cClass.getConstructor(constructorType); + if (!constructor.isAccessible()) + AccessController.doPrivileged(new SetAccessibleAction(constructor)); + synchronized (CoercingComparator.class) { + int top = coercers.length; + CoercingComparator<?>[] nc = new CoercingComparator<?>[top + 1]; + System.arraycopy(coercers, 0, nc, 1, top); + CoercingComparator<V> cv = (CoercingComparator<V>) new FromStringCoercer<Comparable<Object>>(cClass, constructor); + nc[0] = cv; + coercers = nc; + return cv; + } + } catch (Exception e) { + // + } + } + throw new IllegalArgumentException("No comparator for " + vClass.getName()); //$NON-NLS-1$ + } + + protected IllegalArgumentException uncoercable(Object v) { + StringBuffer sb = new StringBuffer("Cannot coerce "); //$NON-NLS-1$ + if (v instanceof String) { + sb.append('\''); + sb.append(v); + sb.append('\''); + } else if (v instanceof Number) { + sb.append("number "); //$NON-NLS-1$ + sb.append(v); + } else { + sb.append("an object of instance "); //$NON-NLS-1$ + sb.append(v.getClass().getName()); + } + sb.append(" into a "); //$NON-NLS-1$ + sb.append(getCoerceClass().getName()); + return new IllegalArgumentException(sb.toString()); + } + + boolean canCoerceTo(Class<?> cls) { + return cls == getCoerceClass(); + } + + abstract T coerce(Object v); + + abstract int compare(T o1, T o2); + + abstract Class<T> getCoerceClass(); + + abstract int getCoercePrio(); +} |