/** * <copyright> * * Copyright (c) 2013-2016 Thales Global Services S.A.S. * 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: * Thales Global Services S.A.S. - initial API and implementation * * </copyright> */ package org.eclipse.emf.diffmerge.util.structures.comparable; import java.util.Arrays; import java.util.Comparator; import java.util.Iterator; import java.util.List; import java.util.Map; import java.util.Set; import org.eclipse.emf.diffmerge.util.structures.StructuresUtil; /** * A structure which is Comparable based on its contents. * @param <E> the type of the comparable contents * @author Olivier Constant */ public interface IComparableStructure<E extends Comparable<?>> extends Comparable<IComparableStructure<?>> { /** * Return an iterator over the members of this structure; the order of the * members defined by this iterator is used for comparing the structure * with others. Null values can be encountered during iteration. * @return a non-null iterator */ Iterator<E> getCompareIterator(); /** * A List which is an IComparableStructure. */ interface IComparableList<E extends Comparable<?>> extends List<E>, IComparableStructure<E> { // Nothing to add } /** * A Set which is an IComparableStructure. */ interface IComparableSet<E extends Comparable<?>> extends Set<E>, IComparableStructure<E> { // Nothing to add } /** * A Map.Entry which is an IComparableStructure. * @param <K> the type of the keys * @param <V> the type of the values */ interface IComparableMapEntry<K extends Comparable<?>, V extends Comparable<?>> extends Map.Entry<K, V>, IComparableStructure<Comparable<?>> { // Nothing to add } /** * A Map which is an IComparableStructure. */ interface IComparableMap<K extends Comparable<?>, V extends Comparable<?>> extends Map<K, V>, IComparableStructure<IComparableMapEntry<K, V>> { // Nothing to add } /** * A default Comparator for IComparableStructures. * The natural order of IComparableStructures is derived from the natural order * of their elements, if available, similarly as the order of Strings is derived * from the alphanumeric order of Characters. * Structures of different types are ordered based on their class name. * Structures of the same type but whose contents are not mutually comparable are * considered equal. In this situation, this comparator is thus not consistent * with equals. */ Comparator<IComparableStructure<?>> STRUCTURE_COMPARATOR = new Comparator<IComparableStructure<?>>() { public int compare(IComparableStructure<?> o1_p, IComparableStructure<?> o2_p) { // Objects of different types: arbitrarily order by type name if (o1_p.getClass() != o2_p.getClass()) { return o1_p.getClass().getName().compareTo( o2_p.getClass().getName()); } // Objects of the same type try { Iterator<? extends Comparable<?>> it1 = o1_p.getCompareIterator(); Iterator<? extends Comparable<?>> it2 = o2_p.getCompareIterator(); // Compare contents while (it1.hasNext() && it2.hasNext()) { @SuppressWarnings("unchecked") Comparable<Object> member1 = (Comparable<Object>)it1.next(); @SuppressWarnings("unchecked") Comparable<Object> member2 = (Comparable<Object>)it2.next(); if (member1 != null && member2 != null) { int result = member1.compareTo(member2); if (result != 0) return result; } else if (member1 == null && member2 != null) { return -1; // null is considered "smaller than" anything else } else if (member1 != null) { return 1; // Only member 2 is null } // Else both are null: keep iterating } return (!it1.hasNext() && !it2.hasNext())? 0: it1.hasNext()? 1: -1; } catch (ClassCastException e) { // Members cannot be compared return 0; } } }; /** * A simple implementation of IComparableMapEntry that wraps another Entry. * @param <K> the type of the keys * @param <V> the type of the values */ public static class ComparableMapEntry<K extends Comparable<?>, V extends Comparable<?>> implements IComparableMapEntry<K, V> { /** The non-null wrapped entry */ protected final Map.Entry<K, V> _wrappedEntry; /** * Constructor * @param entry_p a non-null map entry */ public ComparableMapEntry(Map.Entry<K, V> entry_p) { _wrappedEntry = entry_p; } /** * @see java.lang.Comparable#compareTo(java.lang.Object) */ public int compareTo(IComparableStructure<?> o_p) { return STRUCTURE_COMPARATOR.compare(this, o_p); } /** * @see java.lang.Object#equals(java.lang.Object) */ @Override public boolean equals(Object o_p) { return _wrappedEntry.equals(o_p); } /** * @see org.eclipse.emf.diffmerge.util.structures.comparable.IComparableStructure#getCompareIterator() */ public Iterator<Comparable<?>> getCompareIterator() { return Arrays.asList(getKey(), getValue()).iterator(); } /** * @see java.util.Map.Entry#getKey() */ public K getKey() { return _wrappedEntry.getKey(); } /** * @see java.util.Map.Entry#getValue() */ public V getValue() { return _wrappedEntry.getValue(); } /** * @see java.lang.Object#hashCode() */ @Override public int hashCode() { return _wrappedEntry.hashCode(); } /** * @see java.util.Map.Entry#setValue(java.lang.Object) */ public V setValue(V value_p) { return _wrappedEntry.setValue(value_p); } /** * @see java.lang.Object#toString() */ @Override public String toString() { return StructuresUtil.toMapEntryString(this); } } }