Skip to main content
summaryrefslogtreecommitdiffstats
diff options
context:
space:
mode:
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.java392
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();
+}

Back to the top