/*******************************************************************************
* Copyright (c) 1998, 2015 Oracle and/or its affiliates. All rights reserved.
* This program and the accompanying materials are made available under the
* terms of the Eclipse Public License v1.0 and Eclipse Distribution License v. 1.0
* which accompanies this distribution.
* The Eclipse Public License is available at http://www.eclipse.org/legal/epl-v10.html
* and the Eclipse Distribution License is available at
* http://www.eclipse.org/org/documents/edl-v10.php.
*
* Contributors:
* Oracle - initial API and implementation from Oracle TopLink
******************************************************************************/
package org.eclipse.persistence.tools.workbench.utility.diff;
import org.eclipse.persistence.tools.workbench.utility.ClassTools;
import org.eclipse.persistence.tools.workbench.utility.string.StringTools;
/**
* Given a pair of ordered containers to compare, use the element differentiator
* to compare the elements' values. The comparisons are based on the indices
* of the elements; i.e. an element in the first container is only compared to
* the element in the second container at the same index - there is no searching
* of the second container for a match. If the containers are different sizes,
* the "remainder" elements at the end of the longer container are recorded as
* "missing".
*/
public class OrderedContainerDifferentiator
implements Differentiator
{
/** the adapter used to adapter the containers */
private Adapter adapter;
/** the differentiator used to compare the containers' elements */
private Differentiator elementDifferentiator;
/** used as a placeholder when the containers are different sizes */
static final Object UNDEFINED_ELEMENT =
new Object() {
public String toString() {
return "<undefined>";
}
};
// ********** convenience static methods **********
public static OrderedContainerDifferentiator forLists() {
return new OrderedContainerDifferentiator(ListAdapter.instance());
}
public static OrderedContainerDifferentiator forLists(Differentiator elementDifferentiator) {
return new OrderedContainerDifferentiator(ListAdapter.instance(), elementDifferentiator);
}
public static OrderedContainerDifferentiator forArrays() {
return new OrderedContainerDifferentiator(ArrayAdapter.instance());
}
public static OrderedContainerDifferentiator forArrays(Differentiator elementDifferentiator) {
return new OrderedContainerDifferentiator(ArrayAdapter.instance(), elementDifferentiator);
}
// ********** constructors **********
/**
* by default, use an "equality" element differentiator
* use this constructor if you want to override the various
* "adapter" methods instead of building an Adapter
*/
public OrderedContainerDifferentiator() {
this(Adapter.INVALID_INSTANCE);
}
/**
* use this constructor if you want to override the various
* "adapter" methods instead of building an Adapter
*/
public OrderedContainerDifferentiator(Differentiator elementDifferentiator) {
this(Adapter.INVALID_INSTANCE, elementDifferentiator);
}
/**
* by default, use an "equality" element differentiator
*/
public OrderedContainerDifferentiator(Adapter adapter) {
this(adapter, EqualityDifferentiator.instance());
}
public OrderedContainerDifferentiator(Adapter adapter, Differentiator elementDifferentiator) {
super();
this.adapter = adapter;
this.elementDifferentiator = elementDifferentiator;
}
// ********** Differentiator implementation **********
/**
* @see Differentiator#diff(Object, Object)
*/
public Diff diff(Object object1, Object object2) {
return this.diff(object1, object2, DifferentiatorAdapter.NORMAL);
}
/**
* @see Differentiator#keyDiff(Object, Object)
*/
public Diff keyDiff(Object object1, Object object2) {
return this.diff(object1, object2, DifferentiatorAdapter.KEY);
}
/**
* compare the containers, element by element at the same index
*/
private Diff diff(Object object1, Object object2, DifferentiatorAdapter drAdapter) {
if (object1 == object2) {
return new NullDiff(object1, object2, this);
}
if (this.diffIsFatal(object1, object2)) {
return new SimpleDiff(object1, object2, this.fatalDescriptionTitle(), this);
}
int size1 = this.size(object1);
int size2 = this.size(object2);
int min = Math.min(size1, size2);
int max = Math.max(size1, size2);
Diff[] diffs = new Diff[max];
for (int i = 0; i < min; i++) {
Diff elementDiff = drAdapter.diff(this.elementDifferentiator, this.get(object1, i), this.get(object2, i));
diffs[i] = new OrderedContainerElementDiff(i, elementDiff, this);
}
if (min != max) {
// if the lists are different sizes, add some more element diffs
if (size1 < size2) {
for (int i = min; i < max; i++) {
Diff elementDiff = new SimpleDiff(UNDEFINED_ELEMENT, this.get(object2, i), this.missingElementDescriptionTitle(i), this);
diffs[i] = new OrderedContainerElementDiff(i, elementDiff, this);
}
} else {
for (int i = min; i < max; i++) {
Diff elementDiff = new SimpleDiff(this.get(object1, i), UNDEFINED_ELEMENT, this.missingElementDescriptionTitle(i), this);
diffs[i] = new OrderedContainerElementDiff(i, elementDiff, this);
}
}
}
return new OrderedContainerDiff(this.containerClass(), object1, object2, diffs, this);
}
private String missingElementDescriptionTitle(int index) {
return "Only one of the " + ClassTools.shortNameFor(this.containerClass()) + "s has a value at index " + index;
}
private String fatalDescriptionTitle() {
return "The two " + ClassTools.shortNameFor(this.containerClass()) + "s cannot be compared";
}
/**
* this will probably never be called, but we'll try 'false' for now
* @see Differentiator#comparesValueObjects()
*/
public boolean comparesValueObjects() {
return false;
}
// ********** accessors **********
public Differentiator getElementDifferentiator() {
return this.elementDifferentiator;
}
public void setElementDifferentiator(Differentiator elementDifferentiator) {
this.elementDifferentiator = elementDifferentiator;
}
// ********** "adapter" methods **********
/**
* return whether the the compared objects can even be
* compared (e.g. they both must be non-null and instances
* of java.util.List)
*/
protected boolean diffIsFatal(Object object1, Object object2) {
return this.adapter.diffIsFatal(object1, object2);
}
/**
* return the class to be passed to any diffs created
* during the comparison
*/
protected Class containerClass() {
return this.adapter.containerClass();
}
/**
* return the size of the specified container
*/
protected int size(Object container) {
return this.adapter.size(container);
}
/**
* return the element at the specified index
*/
protected Object get(Object container, int index) {
return this.adapter.get(container, index);
}
// ********** standard override **********
/**
* @see Object#toString()
*/
public String toString() {
return StringTools.buildToStringFor(this, this.elementDifferentiator);
}
// ********** member interface **********
/**
* This adapter is used by the differentiator to adapt
* access to the various containers to be compared
* (e.g. List, Array).
*/
public interface Adapter {
/**
* Return whether the the specified objects can even be
* compared (e.g. they both must be non-null and instances
* of java.util.List).
*/
boolean diffIsFatal(Object object1, Object object2);
/**
* Return the class to be passed to any diffs created
* during the comparison. Most of the time this is used
* only in user messages.
*/
Class containerClass();
/**
* Return the size of the specified container.
*/
int size(Object container);
/**
* Return the element at the specified index.
*/
Object get(Object container, int index);
Adapter INVALID_INSTANCE =
new Adapter() {
public boolean diffIsFatal(Object object1, Object object2) {
throw new UnsupportedOperationException();
}
public Class containerClass() {
throw new UnsupportedOperationException();
}
public int size(Object container) {
throw new UnsupportedOperationException();
}
public Object get(Object container, int index) {
throw new UnsupportedOperationException();
}
public String toString() {
return "InvalidAdapter";
}
};
}
}