blob: ea6cd5090ccafb4c62771b8937df8197c732b171 [file] [log] [blame]
Stuart McCullochf3173222012-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
Stuart McCulloch4482c702012-06-15 13:27:53 +000020 final Tree older;
21 final Tree newer;
Stuart McCullochf3173222012-06-07 21:57:32 +000022 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 = {
Stuart McCulloch4482c702012-06-15 13:27:53 +000031 {
32 IGNORED, UNCHANGED, CHANGED, MICRO, MINOR, MAJOR
33 }, // IGNORED
34 {
35 IGNORED, UNCHANGED, CHANGED, MICRO, MINOR, MAJOR
36 }, // UNCHANGED
37 {
38 IGNORED, CHANGED, CHANGED, MICRO, MINOR, MAJOR
39 }, // CHANGED
40 {
41 IGNORED, MICRO, MICRO, MICRO, MINOR, MAJOR
42 }, // MICRO
43 {
44 IGNORED, MINOR, MINOR, MINOR, MINOR, MAJOR
45 }, // MINOR
46 {
47 IGNORED, MAJOR, MAJOR, MAJOR, MAJOR, MAJOR
48 }, // MAJOR
49 {
50 IGNORED, MAJOR, MAJOR, MAJOR, MAJOR, MAJOR
51 }, // REMOVED
52 {
53 IGNORED, MINOR, MINOR, MINOR, MINOR, MAJOR
54 }, // ADDED
Stuart McCullochf3173222012-06-07 21:57:32 +000055 };
56
57 /**
58 * Compares the newer against the older, traversing the children if
59 * necessary.
60 *
61 * @param newer
62 * The newer Element
63 * @param older
64 * The older Element
65 * @param types
66 */
Stuart McCulloch4482c702012-06-15 13:27:53 +000067 public DiffImpl(Tree newer, Tree older) {
Stuart McCullochf3173222012-06-07 21:57:32 +000068 assert newer != null || older != null;
69 this.older = older;
70 this.newer = newer;
71
72 // Either newer or older can be null, indicating remove or add
73 // so we have to be very careful.
Stuart McCulloch4482c702012-06-15 13:27:53 +000074 Tree[] newerChildren = newer == null ? Element.EMPTY : newer.getChildren();
75 Tree[] olderChildren = older == null ? Element.EMPTY : older.getChildren();
Stuart McCullochf3173222012-06-07 21:57:32 +000076
77 int o = 0;
78 int n = 0;
79 List<DiffImpl> children = new ArrayList<DiffImpl>();
80 while (true) {
Stuart McCulloch4482c702012-06-15 13:27:53 +000081 Tree nw = n < newerChildren.length ? newerChildren[n] : null;
82 Tree ol = o < olderChildren.length ? olderChildren[o] : null;
Stuart McCullochf3173222012-06-07 21:57:32 +000083 DiffImpl diff;
84
85 if (nw == null && ol == null)
86 break;
87
88 if (nw != null && ol != null) {
89 // we have both sides
90 int result = nw.compareTo(ol);
91 if (result == 0) {
92 // we have two equal named elements
93 // use normal diff
94 diff = new DiffImpl(nw, ol);
95 n++;
96 o++;
97 } else if (result > 0) {
98 // we newer > older, so there is no newer == removed
99 diff = new DiffImpl(null, ol);
100 o++;
101 } else {
102 // we newer < older, so there is no older == added
103 diff = new DiffImpl(nw, null);
104 n++;
105 }
106 } else {
107 // we reached the end of one of the list
108 diff = new DiffImpl(nw, ol);
109 n++;
110 o++;
111 }
112 children.add(diff);
113 }
114
115 // make sure they're read only
116 this.children = Collections.unmodifiableCollection(children);
117 delta = getDelta(null);
118 }
119
120 /**
121 * Return the absolute delta. Also see
122 * {@link #getDelta(aQute.bnd.service.diff.Diff.Ignore)} that allows you to
123 * ignore Diff objects on the fly (and calculate their parents accordingly).
124 */
125 public Delta getDelta() {
126 return delta;
127 }
128
129 /**
130 * This getDelta calculates the delta but allows the caller to ignore
131 * certain Diff objects by calling back the ignore call back parameter. This
132 * can be useful to ignore warnings/errors.
133 */
134
135 public Delta getDelta(Ignore ignore) {
136
137 // If ignored, we just return ignore.
138 if (ignore != null && ignore.contains(this))
139 return IGNORED;
140
141 if (newer == null) {
142 return REMOVED;
143 } else if (older == null) {
144 return ADDED;
145 } else {
146 // now we're sure newer and older are both not null
147 assert newer != null && older != null;
148 assert newer.getClass() == older.getClass();
149
150 Delta local = Delta.UNCHANGED;
151
152 for (DiffImpl child : children) {
153 Delta sub = child.getDelta(ignore);
154 if (sub == REMOVED)
Stuart McCulloch4482c702012-06-15 13:27:53 +0000155 sub = child.older.ifRemoved();
Stuart McCullochf3173222012-06-07 21:57:32 +0000156 else if (sub == ADDED)
Stuart McCulloch4482c702012-06-15 13:27:53 +0000157 sub = child.newer.ifAdded();
Stuart McCullochf3173222012-06-07 21:57:32 +0000158
159 // The escalate method is used to calculate the default
160 // transition in the
161 // delta based on the children. In general the delta can
162 // only escalate, i.e.
163 // move up in the chain.
164
165 local = TRANSITIONS[sub.ordinal()][local.ordinal()];
166 }
167 return local;
168 }
169 }
170
171 public Type getType() {
172 return (newer == null ? older : newer).getType();
173 }
174
175 public String getName() {
176 return (newer == null ? older : newer).getName();
177 }
178
Stuart McCulloch4482c702012-06-15 13:27:53 +0000179 public Collection< ? extends Diff> getChildren() {
Stuart McCullochf3173222012-06-07 21:57:32 +0000180 return children;
181 }
182
183 public String toString() {
184 return String.format("%-10s %-10s %s", getDelta(), getType(), getName());
185 }
186
187 public boolean equals(Object other) {
188 if (other instanceof DiffImpl) {
189 DiffImpl o = (DiffImpl) other;
Stuart McCulloch4482c702012-06-15 13:27:53 +0000190 return getDelta() == o.getDelta() && getType() == o.getType() && getName().equals(o.getName());
Stuart McCullochf3173222012-06-07 21:57:32 +0000191 }
192 return false;
193 }
194
195 public int hashCode() {
196 return getDelta().hashCode() ^ getType().hashCode() ^ getName().hashCode();
197 }
198
199 public int compareTo(DiffImpl other) {
200 if (getDelta() == other.getDelta()) {
201 if (getType() == other.getType()) {
202 return getName().compareTo(other.getName());
203 } else
204 return getType().compareTo(other.getType());
205 } else
206 return getDelta().compareTo(other.getDelta());
207 }
208
209 public Diff get(String name) {
210 for (DiffImpl child : children) {
211 if (child.getName().equals(name))
212 return child;
213 }
214 return null;
215 }
216
217 public Tree getOlder() {
218 return older;
219 }
220
221 public Tree getNewer() {
222 return newer;
223 }
224
Stuart McCulloch4482c702012-06-15 13:27:53 +0000225 public Data serialize() {
226 Data data = new Data();
227 data.type = getType();
228 data.delta = delta;
229 data.name = getName();
230 data.children = new Data[children.size()];
231
232 int i=0;
233 for ( Diff d : children)
234 data.children[i++] = d.serialize();
235
236 return data;
237 }
238
239
Stuart McCullochf3173222012-06-07 21:57:32 +0000240}