1 19 package org.netbeans.modules.java.source.save; 20 21 import java.util.*; 22 import org.netbeans.api.java.lexer.JavaTokenId; 23 import static org.netbeans.modules.java.source.save.Measure.*; 24 25 33 public final class ListMatcher<E> { 34 35 private final E[] oldL; 37 38 private final E[] newL; 40 41 private final Stack<ResultItem<E>> result; 43 44 private final Measure measure; 46 47 private ListMatcher(List<? extends E> oldL, List<? extends E> newL, Measure measure) { 49 this((E[]) oldL.toArray(), (E[]) newL.toArray(), measure); 50 } 51 52 private ListMatcher(List<? extends E> oldL, List<? extends E> newL) { 54 this((E[]) oldL.toArray(), (E[]) newL.toArray()); 55 } 56 57 private ListMatcher(E[] oldL, E[] newL) { 59 this(oldL, newL, null); 60 } 61 62 private ListMatcher(E[] oldL, E[] newL, Measure measure) { 63 this.oldL = oldL; 64 this.newL = newL; 65 this.measure = measure != null ? measure : Measure.DEFAULT; 66 result = new Stack<ResultItem<E>>(); 67 } 68 69 76 public static <T> ListMatcher<T> instance(List<? extends T> oldL, List<? extends T> newL) { 77 return new ListMatcher<T>(oldL, newL); 78 } 79 80 88 public static <T> ListMatcher<T> instance(List<? extends T> oldL, List<? extends T> newL, Measure measure) { 89 return new ListMatcher<T>(oldL, newL, measure); 90 } 91 98 public static <T> ListMatcher<T> instance(T[] oldL, T[] newL) { 99 return new ListMatcher<T>(oldL, newL); 100 } 101 102 105 public static enum Operation { 106 107 INSERT("insert"), 108 109 110 MODIFY("modify"), 111 112 113 DELETE("delete"), 114 115 116 NOCHANGE("nochange"); 117 118 private Operation(String name) { 119 this.name = name; 120 } 121 122 private final String name; 123 124 public String toString() { 125 return name; 126 } 127 } 129 132 public static final class ResultItem<S> { 133 134 135 public final S element; 136 137 138 public final Operation operation; 139 140 147 public ResultItem(final S element, final Operation operation) { 148 this.element = element; 149 this.operation = operation; 150 } 151 152 public String toString() { 153 StringBuffer sb = new StringBuffer (128); 154 sb.append('{'); 155 sb.append(operation); 156 sb.append("} "); 157 sb.append(element); 158 return sb.toString(); 159 } 160 }; 161 162 168 public boolean match() { 169 final int NEITHER = 0; 170 final int UP = 1; 171 final int LEFT = 2; 172 final int UP_AND_LEFT = 3; 173 final int UP_AND_LEFT_MOD = 4; 174 175 int n = oldL.length; 176 int m = newL.length; 177 int S[][] = new int[n+1][m+1]; 178 int R[][] = new int[n+1][m+1]; 179 int ii, jj; 180 181 for (ii = 0; ii <= n; ++ii) { 183 S[ii][0] = 0; 184 R[ii][0] = UP; 185 } 186 for (jj = 0; jj <= m; ++jj) { 187 S[0][jj] = 0; 188 R[0][jj] = LEFT; 189 } 190 191 for (ii = 1; ii <= n; ++ii) { 193 for (jj = 1; jj <= m; ++jj) { 194 if (oldL[ii-1].equals(newL[jj-1])) { 195 S[ii][jj] = S[ii-1][jj-1] + 1; 196 R[ii][jj] = UP_AND_LEFT; 197 } else { 198 int distance = measure.getDistance(oldL[ii-1], newL[jj-1]); 199 if (distance > OBJECTS_MATCH && distance < INFINITE_DISTANCE) { 202 S[ii][jj] = S[ii-1][jj-1] + 1; 203 R[ii][jj] = UP_AND_LEFT_MOD; 204 } else { 205 S[ii][jj] = S[ii-1][jj-1] + 0; 206 R[ii][jj] = distance == OBJECTS_MATCH ? UP_AND_LEFT : NEITHER; 207 } 208 } 209 210 if (S[ii-1][jj] >= S[ii][jj]) { 211 S[ii][jj] = S[ii-1][jj]; 212 R[ii][jj] = UP; 213 } 214 215 if (S[ii][jj-1] >= S[ii][jj]) { 216 S[ii][jj] = S[ii][jj-1]; 217 R[ii][jj] = LEFT; 218 } 219 } 220 } 221 222 ii = n; 224 jj = m; 225 226 if (result.empty() == false) result.clear(); 229 while (ii > 0 || jj > 0) { 231 if(R[ii][jj] == UP_AND_LEFT) { 232 ii--; 233 jj--; 234 E element = oldL[ii]; 235 result.push(new ResultItem(element, Operation.NOCHANGE)); 236 } else if (R[ii][jj] == UP_AND_LEFT_MOD) { 237 ii--; 238 jj--; 239 E element = newL[ii]; 240 result.push(new ResultItem(element, Operation.MODIFY)); 241 } else if (R[ii][jj] == UP) { 242 ii--; 243 E element = oldL[ii]; 244 result.push(new ResultItem(element, Operation.DELETE)); 245 } else if (R[ii][jj] == LEFT) { 246 jj--; 247 E element = newL[jj]; 248 result.push(new ResultItem(element, Operation.INSERT)); 249 } 250 } 251 return !result.empty(); 252 } 253 254 260 public ResultItem<E>[] getResult() { 261 int size = result.size(); 262 ResultItem<E>[] temp = new ResultItem[size]; 263 for (ResultItem<E> item : result) { 264 temp[--size] = item; 265 } 266 return temp; 267 } 268 269 276 public ResultItem<E>[] getTransformedResult() { 277 Stack<ResultItem<E>> copy = (Stack<ResultItem<E>>) result.clone(); 278 ArrayList<ResultItem<E>> temp = new ArrayList<ResultItem<E>>(copy.size()); 279 while (!copy.empty()) { 280 ResultItem<E> item = copy.pop(); 281 if (item.operation == Operation.DELETE && 285 !copy.empty() && copy.peek().operation == Operation.INSERT) 286 { 287 ResultItem nextItem = copy.pop(); 289 temp.add(new ResultItem(nextItem.element, Operation.MODIFY)); 290 } else { 291 temp.add(item); 292 } 293 } 294 return temp.toArray(new ResultItem[0]); 295 } 296 297 public String printResult(boolean transformed) { 299 StringBuffer sb = new StringBuffer (128); 300 ResultItem<E>[] temp = transformed ? getTransformedResult() : getResult(); 301 for (int i = 0; i < temp.length; i++) { 302 sb.append(temp[i]).append('\n'); 303 } 304 return sb.toString(); 305 } 306 307 312 public Separator separatorInstance() { 313 return new Separator(getTransformedResult(), JavaTokenId.COMMA); 314 } 315 316 public static final class Separator<E> { 317 318 static final SItem EMPTY = new SItem(null, 0); 319 320 private final ResultItem<E>[] match; 321 private E lastInList; 322 private E firstInList; 323 private final JavaTokenId separator; 324 private SItem[] result; 325 326 private boolean allNew; 329 private boolean allOld; 330 331 public Separator(ResultItem<E>[] match, JavaTokenId separator) { 332 this.match = match; 333 this.separator = separator; 334 this.lastInList = null; 335 for (int i = match.length-1; i > 0; i--) { 339 if (match[i].operation != Operation.DELETE) { 340 lastInList = match[i].element; 341 break; 342 } 343 } 344 for (int i = 0; i < match.length; i++) { 345 if (match[i].operation != Operation.DELETE) { 346 firstInList = match[i].element; 347 break; 348 } 349 } 350 allNew = allOld = true; 353 for (int i = 0; i < match.length; i++) { 354 if (match[i].operation == Operation.MODIFY || 355 match[i].operation == Operation.NOCHANGE) 356 allNew = allOld = false; 357 else if (match[i].operation == Operation.INSERT) 358 allOld = false; 359 else if (match[i].operation == Operation.DELETE) 360 allNew = false; 361 } 362 } 363 364 private static SItem create(ResultItem item) { 367 return create(item, SItem.NONE); 368 } 369 370 private static SItem create(ResultItem item, int type) { 372 return new SItem(item, type); 373 } 374 375 381 public void compute() { 382 result = new SItem[match.length]; 383 for (int i = match.length-1; i >= 0; --i) { 384 if (match[i].operation == Operation.DELETE) { 385 if (i == (match.length-1)) { 386 if (i > 0 && match[i-1].operation == Operation.DELETE) { 388 result[i] = create(match[i], allOld ? SItem.TAIL : SItem.NONE); 389 while (i > 0 && match[i-1].operation == Operation.DELETE) { 390 if (i > 1 && match[i-2].operation == Operation.DELETE) { 391 result[--i] = create(match[i], SItem.NEXT); 392 } else { 393 int mask = (i-1) == 0 ? SItem.HEAD : SItem.PREV; 394 result[--i] = create(match[i], mask | SItem.NEXT); 395 } 396 } 397 } else { 398 int mask = i > 0 ? SItem.PREV : SItem.HEAD; 399 mask |= allOld ? SItem.TAIL : SItem.NONE; 400 result[i] = create(match[i], mask); 401 } 402 } else { 403 result[i] = create(match[i], SItem.NEXT); 404 } 405 } else if (match[i].operation == Operation.INSERT) { 406 if (i == (match.length-1)) { 407 if (i > 0 && match[i-1].operation == Operation.INSERT) { 409 result[i] = create(match[i], allNew ? SItem.TAIL : SItem.NONE); 410 while (i > 0 && match[i-1].operation == Operation.INSERT) { 411 if (i > 1 && match[i-2].operation == Operation.INSERT) { 412 result[--i] = create(match[i], SItem.NEXT); 413 } else { 414 int mask = (i-1) == 0 ? SItem.HEAD : SItem.PREV; 415 result[--i] = create(match[i], mask | SItem.NEXT); 416 } 417 } 418 } else { 419 int mask = i > 0 ? SItem.PREV : SItem.HEAD; 420 mask |= allNew ? SItem.TAIL : SItem.NONE; 421 result[i] = create(match[i], mask); 422 } 423 } else { 424 result[i] = create(match[i], SItem.NEXT); 425 } 426 } else { 427 result[i] = EMPTY; 428 } 429 } 430 } 431 432 442 public boolean head(int index) { return result[index].head(); } 443 444 453 public boolean prev(int index) { return result[index].prev(); } 454 455 464 public boolean next(int index) { return result[index].next(); } 465 466 477 public boolean tail(int index) { return result[index].tail(); } 478 479 483 public String print() { 484 if (result != null) { 485 StringBuffer sb = new StringBuffer (128); 486 for (SItem item : result) { 487 if (item != EMPTY) 488 sb.append(item).append('\n'); 489 } 490 return sb.toString(); 491 } else { 492 return "Result was not computed!"; 493 } 494 } 495 496 private static final class SItem { 500 private static final int NONE = 0x00; 501 private static final int PREV = 0x01; 502 private static final int NEXT = PREV << 1; 503 private static final int HEAD = NEXT << 1; 504 private static final int TAIL = HEAD << 1; 505 506 private final int type; 507 private final ResultItem item; 508 509 private SItem(ResultItem item, int type) { 510 this.item = item; 511 this.type = type; 512 } 513 514 private boolean prev() { return (type & PREV) != NONE; } 515 private boolean next() { return (type & NEXT) != NONE; } 516 private boolean head() { return (type & HEAD) != NONE; } 517 private boolean tail() { return (type & TAIL) != NONE; } 518 519 public String toString() { 520 StringBuffer sb = new StringBuffer (); 521 if (head()) sb.append("head "); 522 if (prev()) sb.append("previous "); 523 sb.append(item.toString()).append(' '); 524 if (next()) sb.append("next "); 525 if (tail()) sb.append("tail "); 526 return sb.toString(); 527 } 528 } 529 } 530 } | Popular Tags |