blob: 7b675c261bbdcf02b909d4c1a8c22a09eaf75001 [file] [log] [blame]
Stuart McCullochbb014372012-06-07 21:57:32 +00001package aQute.bnd.differ;
2
3import static aQute.bnd.service.diff.Delta.*;
4
5import java.util.*;
6
7import aQute.bnd.service.diff.*;
8
9/**
10 * A DiffImpl class compares a newer Element to an older Element. The Element
11 * classes hide all the low level details. A Element class is either either
12 * Structured (has children) or it is a Leaf, it only has a value. The
13 * constructor will first build its children (if any) and then calculate the
14 * delta. Each comparable element is translated to an Element. If necessary the
15 * Element can be sub classed to provide special behavior.
16 */
17
18public class DiffImpl implements Diff, Comparable<DiffImpl> {
19
20 final Element older;
21 final Element newer;
22 final Collection<DiffImpl> children;
23 final Delta delta;
24
25 /**
26 * The transitions table defines how the state is escalated depending on the
27 * children. horizontally is the current delta and this is indexed with the
28 * child delta for each child. This escalates deltas from below up.
29 */
30 final static Delta[][] TRANSITIONS = {
31 { IGNORED, UNCHANGED, CHANGED, MICRO, MINOR, MAJOR }, // IGNORED
32 { IGNORED, UNCHANGED, CHANGED, MICRO, MINOR, MAJOR }, // UNCHANGED
33 { IGNORED, CHANGED, CHANGED, MICRO, MINOR, MAJOR }, // CHANGED
34 { IGNORED, MICRO, MICRO, MICRO, MINOR, MAJOR }, // MICRO
35 { IGNORED, MINOR, MINOR, MINOR, MINOR, MAJOR }, // MINOR
36 { IGNORED, MAJOR, MAJOR, MAJOR, MAJOR, MAJOR }, // MAJOR
37 { IGNORED, MAJOR, MAJOR, MAJOR, MAJOR, MAJOR }, // REMOVED
38 { IGNORED, MINOR, MINOR, MINOR, MINOR, MAJOR }, // ADDED
39 };
40
41 /**
42 * Compares the newer against the older, traversing the children if
43 * necessary.
44 *
45 * @param newer
46 * The newer Element
47 * @param older
48 * The older Element
49 * @param types
50 */
51 DiffImpl(Element newer, Element older) {
52 assert newer != null || older != null;
53 this.older = older;
54 this.newer = newer;
55
56 // Either newer or older can be null, indicating remove or add
57 // so we have to be very careful.
58 Element[] newerChildren = newer == null ? Element.EMPTY : newer.children;
59 Element[] olderChildren = older == null ? Element.EMPTY : older.children;
60
61 int o = 0;
62 int n = 0;
63 List<DiffImpl> children = new ArrayList<DiffImpl>();
64 while (true) {
65 Element nw = n < newerChildren.length ? newerChildren[n] : null;
66 Element ol = o < olderChildren.length ? olderChildren[o] : null;
67 DiffImpl diff;
68
69 if (nw == null && ol == null)
70 break;
71
72 if (nw != null && ol != null) {
73 // we have both sides
74 int result = nw.compareTo(ol);
75 if (result == 0) {
76 // we have two equal named elements
77 // use normal diff
78 diff = new DiffImpl(nw, ol);
79 n++;
80 o++;
81 } else if (result > 0) {
82 // we newer > older, so there is no newer == removed
83 diff = new DiffImpl(null, ol);
84 o++;
85 } else {
86 // we newer < older, so there is no older == added
87 diff = new DiffImpl(nw, null);
88 n++;
89 }
90 } else {
91 // we reached the end of one of the list
92 diff = new DiffImpl(nw, ol);
93 n++;
94 o++;
95 }
96 children.add(diff);
97 }
98
99 // make sure they're read only
100 this.children = Collections.unmodifiableCollection(children);
101 delta = getDelta(null);
102 }
103
104 /**
105 * Return the absolute delta. Also see
106 * {@link #getDelta(aQute.bnd.service.diff.Diff.Ignore)} that allows you to
107 * ignore Diff objects on the fly (and calculate their parents accordingly).
108 */
109 public Delta getDelta() {
110 return delta;
111 }
112
113 /**
114 * This getDelta calculates the delta but allows the caller to ignore
115 * certain Diff objects by calling back the ignore call back parameter. This
116 * can be useful to ignore warnings/errors.
117 */
118
119 public Delta getDelta(Ignore ignore) {
120
121 // If ignored, we just return ignore.
122 if (ignore != null && ignore.contains(this))
123 return IGNORED;
124
125 if (newer == null) {
126 return REMOVED;
127 } else if (older == null) {
128 return ADDED;
129 } else {
130 // now we're sure newer and older are both not null
131 assert newer != null && older != null;
132 assert newer.getClass() == older.getClass();
133
134 Delta local = Delta.UNCHANGED;
135
136 for (DiffImpl child : children) {
137 Delta sub = child.getDelta(ignore);
138 if (sub == REMOVED)
139 sub = child.older.remove;
140 else if (sub == ADDED)
141 sub = child.newer.add;
142
143 // The escalate method is used to calculate the default
144 // transition in the
145 // delta based on the children. In general the delta can
146 // only escalate, i.e.
147 // move up in the chain.
148
149 local = TRANSITIONS[sub.ordinal()][local.ordinal()];
150 }
151 return local;
152 }
153 }
154
155 public Type getType() {
156 return (newer == null ? older : newer).getType();
157 }
158
159 public String getName() {
160 return (newer == null ? older : newer).getName();
161 }
162
163 public Collection<? extends Diff> getChildren() {
164 return children;
165 }
166
167 public String toString() {
168 return String.format("%-10s %-10s %s", getDelta(), getType(), getName());
169 }
170
171 public boolean equals(Object other) {
172 if (other instanceof DiffImpl) {
173 DiffImpl o = (DiffImpl) other;
174 return getDelta() == o.getDelta() && getType() == o.getType()
175 && getName().equals(o.getName());
176 }
177 return false;
178 }
179
180 public int hashCode() {
181 return getDelta().hashCode() ^ getType().hashCode() ^ getName().hashCode();
182 }
183
184 public int compareTo(DiffImpl other) {
185 if (getDelta() == other.getDelta()) {
186 if (getType() == other.getType()) {
187 return getName().compareTo(other.getName());
188 } else
189 return getType().compareTo(other.getType());
190 } else
191 return getDelta().compareTo(other.getDelta());
192 }
193
194 public Diff get(String name) {
195 for (DiffImpl child : children) {
196 if (child.getName().equals(name))
197 return child;
198 }
199 return null;
200 }
201
202 public Tree getOlder() {
203 return older;
204 }
205
206 public Tree getNewer() {
207 return newer;
208 }
209
210}