All Downloads are FREE. Search and download functionalities are using the official Maven repository.

aQute.bnd.differ.DiffImpl Maven / Gradle / Ivy

package aQute.bnd.differ;

import static aQute.bnd.service.diff.Delta.*;

import java.util.*;

import aQute.bnd.service.diff.*;

/**
 * A DiffImpl class compares a newer Element to an older Element. The Element
 * classes hide all the low level details. A Element class is either either
 * Structured (has children) or it is a Leaf, it only has a value. The
 * constructor will first build its children (if any) and then calculate the
 * delta. Each comparable element is translated to an Element. If necessary the
 * Element can be sub classed to provide special behavior.
 */

public class DiffImpl implements Diff, Comparable {

	final Tree				older;
	final Tree				newer;
	final Collection	children;
	final Delta					delta;

	/**
	 * The transitions table defines how the state is escalated depending on the
	 * children. horizontally is the current delta and this is indexed with the
	 * child delta for each child. This escalates deltas from below up.
	 */
	final static Delta[][]		TRANSITIONS	= {
			{
			IGNORED, UNCHANGED, CHANGED, MICRO, MINOR, MAJOR
			}, // IGNORED
			{
			IGNORED, UNCHANGED, CHANGED, MICRO, MINOR, MAJOR
			}, // UNCHANGED
			{
			IGNORED, CHANGED, CHANGED, MICRO, MINOR, MAJOR
			}, // CHANGED
			{
			IGNORED, MICRO, MICRO, MICRO, MINOR, MAJOR
			}, // MICRO
			{
			IGNORED, MINOR, MINOR, MINOR, MINOR, MAJOR
			}, // MINOR
			{
			IGNORED, MAJOR, MAJOR, MAJOR, MAJOR, MAJOR
			}, // MAJOR
			{
			IGNORED, MAJOR, MAJOR, MAJOR, MAJOR, MAJOR
			}, // REMOVED
			{
			IGNORED, MINOR, MINOR, MINOR, MINOR, MAJOR
			}, // ADDED
											};

	/**
	 * Compares the newer against the older, traversing the children if
	 * necessary.
	 * 
	 * @param newer
	 *            The newer Element
	 * @param older
	 *            The older Element
	 * @param types
	 */
	public DiffImpl(Tree newer, Tree older) {
		assert newer != null || older != null;
		this.older = older;
		this.newer = newer;

		// Either newer or older can be null, indicating remove or add
		// so we have to be very careful.
		Tree[] newerChildren = newer == null ? Element.EMPTY : newer.getChildren();
		Tree[] olderChildren = older == null ? Element.EMPTY : older.getChildren();

		int o = 0;
		int n = 0;
		List children = new ArrayList();
		while (true) {
			Tree nw = n < newerChildren.length ? newerChildren[n] : null;
			Tree ol = o < olderChildren.length ? olderChildren[o] : null;
			DiffImpl diff;

			if (nw == null && ol == null)
				break;

			if (nw != null && ol != null) {
				// we have both sides
				int result = nw.compareTo(ol);
				if (result == 0) {
					// we have two equal named elements
					// use normal diff
					diff = new DiffImpl(nw, ol);
					n++;
					o++;
				} else if (result > 0) {
					// we newer > older, so there is no newer == removed
					diff = new DiffImpl(null, ol);
					o++;
				} else {
					// we newer < older, so there is no older == added
					diff = new DiffImpl(nw, null);
					n++;
				}
			} else {
				// we reached the end of one of the list
				diff = new DiffImpl(nw, ol);
				n++;
				o++;
			}
			children.add(diff);
		}

		// make sure they're read only
		this.children = Collections.unmodifiableCollection(children);
		delta = getDelta(null);
	}

	/**
	 * Return the absolute delta. Also see
	 * {@link #getDelta(aQute.bnd.service.diff.Diff.Ignore)} that allows you to
	 * ignore Diff objects on the fly (and calculate their parents accordingly).
	 */
	public Delta getDelta() {
		return delta;
	}

	/**
	 * This getDelta calculates the delta but allows the caller to ignore
	 * certain Diff objects by calling back the ignore call back parameter. This
	 * can be useful to ignore warnings/errors.
	 */

	public Delta getDelta(Ignore ignore) {

		// If ignored, we just return ignore.
		if (ignore != null && ignore.contains(this))
			return IGNORED;

		if (newer == null) {
			return REMOVED;
		} else if (older == null) {
			return ADDED;
		} else {
			// now we're sure newer and older are both not null
			assert newer != null && older != null;
			assert newer.getClass() == older.getClass();

			Delta local = Delta.UNCHANGED;

			for (DiffImpl child : children) {
				Delta sub = child.getDelta(ignore);
				if (sub == REMOVED)
					sub = child.older.ifRemoved();
				else if (sub == ADDED)
					sub = child.newer.ifAdded();

				// The escalate method is used to calculate the default
				// transition in the
				// delta based on the children. In general the delta can
				// only escalate, i.e.
				// move up in the chain.

				local = TRANSITIONS[sub.ordinal()][local.ordinal()];
			}
			return local;
		}
	}

	public Type getType() {
		return (newer == null ? older : newer).getType();
	}

	public String getName() {
		return (newer == null ? older : newer).getName();
	}

	public Collection< ? extends Diff> getChildren() {
		return children;
	}

	@Override
	public String toString() {
		return String.format("%-10s %-10s %s", getDelta(), getType(), getName());
	}

	@Override
	public boolean equals(Object other) {
		if (other instanceof DiffImpl) {
			DiffImpl o = (DiffImpl) other;
			return getDelta() == o.getDelta() && getType() == o.getType() && getName().equals(o.getName());
		}
		return false;
	}

	@Override
	public int hashCode() {
		return getDelta().hashCode() ^ getType().hashCode() ^ getName().hashCode();
	}

	public int compareTo(DiffImpl other) {
		if (getDelta() == other.getDelta()) {
			if (getType() == other.getType()) {
				return getName().compareTo(other.getName());
			}
			return getType().compareTo(other.getType());
		}
		return getDelta().compareTo(other.getDelta());
	}

	public Diff get(String name) {
		for (DiffImpl child : children) {
			if (child.getName().equals(name))
				return child;
		}
		return null;
	}

	public Tree getOlder() {
		return older;
	}

	public Tree getNewer() {
		return newer;
	}

	public Data serialize() {
		Data data = new Data();
		data.type = getType();
		data.delta = delta;
		data.name = getName();
		data.children = new Data[children.size()];
		
		int i=0;		
		for ( Diff d : children)
			data.children[i++] = d.serialize();
				
		return data;
	}


}




© 2015 - 2024 Weber Informatics LLC | Privacy Policy