1 33 34 package edu.rice.cs.drjava.ui.predictive; 35 36 import java.util.List ; 37 import java.util.ArrayList ; 38 import java.util.Collections ; 39 import java.util.regex.Pattern ; 40 import java.util.regex.Matcher ; 41 import java.util.regex.PatternSyntaxException ; 42 43 44 public class PredictiveInputModel<T extends Comparable <? super T>> { 45 46 47 public static interface MatchingStrategy<X extends Comparable <? super X>> { 48 49 54 public boolean isMatch(X item, PredictiveInputModel<X> pim); 55 56 62 public boolean equivalent(X item1, X item2, PredictiveInputModel<X> pim); 63 64 70 public int compare(X item1, X item2, PredictiveInputModel<X> pim); 71 72 78 public X getLongestMatch(X item, List <X> items, PredictiveInputModel<X> pim); 79 80 85 public String getSharedMaskExtension(List <X> items, PredictiveInputModel<X> pim); 86 87 92 public String getExtendedSharedMask(List <X> items, PredictiveInputModel<X> pim); 93 94 99 public String force(X item, String mask); 100 } 101 102 103 public static class PrefixStrategy<X extends Comparable <? super X>> implements MatchingStrategy<X> { 104 public String toString() { return "Prefix"; } 105 public boolean isMatch(X item, PredictiveInputModel<X> pim) { 106 String a = (pim._ignoreCase)?(item.toString().toLowerCase()):(item.toString()); 107 String b = (pim._ignoreCase)?(pim._mask.toLowerCase()):(pim._mask); 108 return a.startsWith(b); 109 } 110 public boolean equivalent(X item1, X item2, PredictiveInputModel<X> pim) { 111 String a = (pim._ignoreCase)?(item1.toString().toLowerCase()):(item1.toString()); 112 String b = (pim._ignoreCase)?(item2.toString().toLowerCase()):(item2.toString()); 113 return a.equals(b); 114 } 115 public int compare(X item1, X item2, PredictiveInputModel<X> pim) { 116 String a = (pim._ignoreCase)?(item1.toString().toLowerCase()):(item1.toString()); 117 String b = (pim._ignoreCase)?(item2.toString().toLowerCase()):(item2.toString()); 118 return a.compareTo(b); 119 } 120 public X getLongestMatch(X item, List <X> items, PredictiveInputModel<X> pim) { 121 X longestMatch = null; 122 int matchLength = -1; 123 for(X i: items) { 124 String s = (pim._ignoreCase)?(i.toString().toLowerCase()):(i.toString()); 125 String t = (pim._ignoreCase)?(item.toString().toLowerCase()):(item.toString()); 126 int ml = 0; 127 while((s.length() > ml) && (t.length() > ml) && (s.charAt(ml) == t.charAt(ml))) { 128 ++ml; 129 } 130 if (ml>matchLength) { 131 matchLength = ml; 132 longestMatch = i; 133 } 134 } 135 return longestMatch; 136 } 137 public String getSharedMaskExtension(List <X> items, PredictiveInputModel<X> pim) { 138 String res = ""; 139 String ext = ""; 140 if (items.size() == 0) { 141 return ext; 142 } 143 boolean allMatching = true; 144 int len = pim._mask.length(); 145 while((allMatching) && (pim._mask.length() + ext.length() < items.get(0).toString().length())) { 146 char origCh = items.get(0).toString().charAt(pim._mask.length()+ext.length()); 147 char ch = (pim._ignoreCase)?(Character.toLowerCase(origCh)):(origCh); 148 allMatching = true; 149 for (X i: items) { 150 String a = (pim._ignoreCase)?(i.toString().toLowerCase()):(i.toString()); 151 if (a.charAt(len)!=ch) { 152 allMatching = false; 153 break; 154 } 155 } 156 if (allMatching) { 157 ext = ext + ch; 158 res = res + origCh; 159 ++len; 160 } 161 } 162 return res; 163 } 164 public String getExtendedSharedMask(List <X> items, PredictiveInputModel<X> pim) { 165 return pim._mask + getSharedMaskExtension(items, pim); 166 } 167 public String force(X item, String mask) { return item.toString(); } 168 }; 169 170 171 public static class FragmentStrategy<X extends Comparable <? super X>> implements MatchingStrategy<X> { 172 public String toString() { return "Fragments"; } 173 public boolean isMatch(X item, PredictiveInputModel<X> pim) { 174 String a = (pim._ignoreCase)?(item.toString().toLowerCase()):(item.toString()); 175 String b = (pim._ignoreCase)?(pim._mask.toLowerCase()):(pim._mask); 176 177 java.util.StringTokenizer tok = new java.util.StringTokenizer (b); 178 while(tok.hasMoreTokens()) { 179 if (a.indexOf(tok.nextToken()) < 0) return false; 180 } 181 return true; 182 } 183 public boolean equivalent(X item1, X item2, PredictiveInputModel<X> pim) { 184 String a = (pim._ignoreCase)?(item1.toString().toLowerCase()):(item1.toString()); 185 String b = (pim._ignoreCase)?(item2.toString().toLowerCase()):(item2.toString()); 186 return a.equals(b); 187 } 188 public int compare(X item1, X item2, PredictiveInputModel<X> pim) { 189 String a = (pim._ignoreCase)?(item1.toString().toLowerCase()):(item1.toString()); 190 String b = (pim._ignoreCase)?(item2.toString().toLowerCase()):(item2.toString()); 191 return a.compareTo(b); 192 } 193 public X getLongestMatch(X item, List <X> items, PredictiveInputModel<X> pim) { 194 if (items.size() > 0) return items.get(0); 195 else return null; 196 } 197 public String getSharedMaskExtension(List <X> items, PredictiveInputModel<X> pim) { 198 return ""; } 200 public String getExtendedSharedMask(List <X> items, PredictiveInputModel<X> pim) { 201 return pim._mask; 202 } 203 public String force(X item, String mask) { return item.toString(); } 204 }; 205 206 207 public static class RegExStrategy<X extends Comparable <? super X>> implements MatchingStrategy<X> { 208 public String toString() { return "RegEx"; } 209 public boolean isMatch(X item, PredictiveInputModel<X> pim) { 210 String a = item.toString(); 211 212 try { 213 Pattern p = Pattern.compile(pim._mask, 214 (pim._ignoreCase)?(Pattern.CASE_INSENSITIVE):(0)); 215 Matcher m = p.matcher(a); 216 return m.matches(); 217 } 218 catch (PatternSyntaxException e) { 219 return false; 220 } 221 } 222 public boolean equivalent(X item1, X item2, PredictiveInputModel<X> pim) { 223 String a = (pim._ignoreCase)?(item1.toString().toLowerCase()):(item1.toString()); 224 String b = (pim._ignoreCase)?(item2.toString().toLowerCase()):(item2.toString()); 225 return a.equals(b); 226 } 227 public int compare(X item1, X item2, PredictiveInputModel<X> pim) { 228 String a = (pim._ignoreCase)?(item1.toString().toLowerCase()):(item1.toString()); 229 String b = (pim._ignoreCase)?(item2.toString().toLowerCase()):(item2.toString()); 230 return a.compareTo(b); 231 } 232 public X getLongestMatch(X item, List <X> items, PredictiveInputModel<X> pim) { 233 if (items.size() > 0) return items.get(0); else return null; 235 } 236 public String getSharedMaskExtension(List <X> items, PredictiveInputModel<X> pim) { 237 return ""; } 239 public String getExtendedSharedMask(List <X> items, PredictiveInputModel<X> pim) { 240 return pim._mask; 241 } 242 public String force(X item, String mask) { return item.toString(); } 243 }; 244 245 246 public static class PrefixLineNumStrategy<X extends Comparable <? super X>> implements MatchingStrategy<X> { 247 public String toString() { return "Prefix"; } 248 public boolean isMatch(X item, PredictiveInputModel<X> pim) { 249 int posB = pim._mask.lastIndexOf(':'); 250 if (posB<0) { posB = pim._mask.length(); } 251 String mask = pim._mask.substring(0,posB); 252 253 String a = (pim._ignoreCase)?(item.toString().toLowerCase()):(item.toString()); 254 String b = (pim._ignoreCase)?(mask.toLowerCase()):(mask); 255 return a.startsWith(b); 256 } 257 public boolean equivalent(X item1, X item2, PredictiveInputModel<X> pim) { 258 int posA = item1.toString().lastIndexOf(':'); 259 if (posA<0) { posA = item1.toString().length(); } 260 String i1 = item1.toString().substring(0,posA); 261 262 int posB = item2.toString().lastIndexOf(':'); 263 if (posB<0) { posB = item2.toString().length(); } 264 String i2 = item2.toString().substring(0,posB); 265 266 String a = (pim._ignoreCase)?(i1.toLowerCase()):(i1); 267 String b = (pim._ignoreCase)?(i2.toLowerCase()):(i2); 268 return a.equals(b); 269 } 270 public int compare(X item1, X item2, PredictiveInputModel<X> pim) { 271 int posA = item1.toString().lastIndexOf(':'); 272 if (posA<0) { posA = item1.toString().length(); } 273 String i1 = item1.toString().substring(0,posA); 274 275 int posB = item2.toString().lastIndexOf(':'); 276 if (posB<0) { posB = item2.toString().length(); } 277 String i2 = item2.toString().substring(0,posB); 278 279 String a = (pim._ignoreCase)?(i1.toLowerCase()):(i1); 280 String b = (pim._ignoreCase)?(i2.toLowerCase()):(i2); 281 return a.compareTo(b); 282 } 283 public X getLongestMatch(X item, List <X> items, PredictiveInputModel<X> pim) { 284 X longestMatch = null; 285 int matchLength = -1; 286 for(X i: items) { 287 int posA = i.toString().lastIndexOf(':'); 288 if (posA<0) { posA = i.toString().length(); } 289 String i1 = i.toString().substring(0,posA); 290 291 int posB = item.toString().lastIndexOf(':'); 292 if (posB<0) { posB = item.toString().length(); } 293 String i2 = item.toString().substring(0,posB); 294 295 String s = (pim._ignoreCase)?(i1.toLowerCase()):(i1); 296 String t = (pim._ignoreCase)?(i2.toLowerCase()):(i2); 297 int ml = 0; 298 while((s.length() > ml) && (t.length() > ml) && (s.charAt(ml) == t.charAt(ml))) { 299 ++ml; 300 } 301 if (ml>matchLength) { 302 matchLength = ml; 303 longestMatch = i; 304 } 305 } 306 return longestMatch; 307 } 308 public String getSharedMaskExtension(List <X> items, PredictiveInputModel<X> pim) { 309 String res = ""; 310 String ext = ""; 311 if (items.size() == 0) { 312 return ext; 313 } 314 315 int posB = pim._mask.lastIndexOf(':'); 316 if (posB<0) { posB = pim._mask.length(); } 317 String mask = pim._mask.substring(0,posB); 318 319 boolean allMatching = true; 320 int len = mask.length(); 321 while((allMatching) && (mask.length() + ext.length() < items.get(0).toString().length())) { 322 char origCh = items.get(0).toString().charAt(mask.length()+ext.length()); 323 char ch = (pim._ignoreCase)?(Character.toLowerCase(origCh)):(origCh); 324 allMatching = true; 325 for (X i: items) { 326 String a = (pim._ignoreCase)?(i.toString().toLowerCase()):(i.toString()); 327 if (a.charAt(len)!=ch) { 328 allMatching = false; 329 break; 330 } 331 } 332 if (allMatching) { 333 ext = ext + ch; 334 res = res + origCh; 335 ++len; 336 } 337 } 338 return res; 339 } 340 public String getExtendedSharedMask(List <X> items, PredictiveInputModel<X> pim) { 341 int pos = pim._mask.lastIndexOf(':'); 342 if (pos<0) { 343 return pim._mask + getSharedMaskExtension(items, pim); 344 } 345 else { 346 return pim._mask.substring(0,pos) + getSharedMaskExtension(items, pim) + pim._mask.substring(pos); 347 } 348 } 349 public String force(X item, String mask) { 350 int pos = mask.lastIndexOf(':'); 351 if (pos<0) { 352 return item.toString(); 353 } 354 else { 355 return item.toString()+mask.substring(pos); 356 } 357 } 358 }; 359 360 361 public static class FragmentLineNumStrategy<X extends Comparable <? super X>> implements MatchingStrategy<X> { 362 public String toString() { return "Fragments"; } 363 public boolean isMatch(X item, PredictiveInputModel<X> pim) { 364 int posB = pim._mask.lastIndexOf(':'); 365 if (posB<0) { posB = pim._mask.length(); } 366 String mask = pim._mask.substring(0,posB); 367 368 String a = (pim._ignoreCase)?(item.toString().toLowerCase()):(item.toString()); 369 String b = (pim._ignoreCase)?(mask.toLowerCase()):(mask); 370 371 java.util.StringTokenizer tok = new java.util.StringTokenizer (b); 372 while(tok.hasMoreTokens()) { 373 if (a.indexOf(tok.nextToken()) < 0) return false; 374 } 375 return true; 376 } 377 public boolean equivalent(X item1, X item2, PredictiveInputModel<X> pim) { 378 int posA = item1.toString().lastIndexOf(':'); 379 if (posA<0) { posA = item1.toString().length(); } 380 String i1 = item1.toString().substring(0,posA); 381 382 int posB = item2.toString().lastIndexOf(':'); 383 if (posB<0) { posB = item2.toString().length(); } 384 String i2 = item2.toString().substring(0,posB); 385 386 String a = (pim._ignoreCase)?(i1.toLowerCase()):(i1); 387 String b = (pim._ignoreCase)?(i2.toLowerCase()):(i2); 388 return a.equals(b); 389 } 390 public int compare(X item1, X item2, PredictiveInputModel<X> pim) { 391 int posA = item1.toString().lastIndexOf(':'); 392 if (posA<0) { posA = item1.toString().length(); } 393 String i1 = item1.toString().substring(0,posA); 394 395 int posB = item2.toString().lastIndexOf(':'); 396 if (posB<0) { posB = item2.toString().length(); } 397 String i2 = item2.toString().substring(0,posB); 398 399 String a = (pim._ignoreCase)?(i1.toLowerCase()):(i1); 400 String b = (pim._ignoreCase)?(i2.toLowerCase()):(i2); 401 return a.compareTo(b); 402 } 403 public X getLongestMatch(X item, List <X> items, PredictiveInputModel<X> pim) { 404 if (items.size() > 0) return items.get(0); 405 else return null; 406 } 407 public String getSharedMaskExtension(List <X> items, PredictiveInputModel<X> pim) { 408 return ""; } 410 public String getExtendedSharedMask(List <X> items, PredictiveInputModel<X> pim) { 411 return pim._mask; 412 } 413 public String force(X item, String mask) { 414 int pos = mask.lastIndexOf(':'); 415 if (pos<0) { 416 return item.toString(); 417 } 418 else { 419 return item.toString()+mask.substring(pos); 420 } 421 } 422 }; 423 424 425 public static class RegExLineNumStrategy<X extends Comparable <? super X>> implements MatchingStrategy<X> { 426 public String toString() { return "RegEx"; } 427 public boolean isMatch(X item, PredictiveInputModel<X> pim) { 428 int posB = pim._mask.lastIndexOf(':'); 429 if (posB<0) { posB = pim._mask.length(); } 430 String mask = pim._mask.substring(0,posB); 431 432 String a = item.toString(); 433 434 try { 435 Pattern p = Pattern.compile(mask, 436 (pim._ignoreCase)?(Pattern.CASE_INSENSITIVE):(0)); 437 Matcher m = p.matcher(a); 438 return m.matches(); 439 } 440 catch (PatternSyntaxException e) { 441 return false; 442 } 443 } 444 public boolean equivalent(X item1, X item2, PredictiveInputModel<X> pim) { 445 int posA = item1.toString().lastIndexOf(':'); 446 if (posA<0) { posA = item1.toString().length(); } 447 String i1 = item1.toString().substring(0,posA); 448 449 int posB = item2.toString().lastIndexOf(':'); 450 if (posB<0) { posB = item2.toString().length(); } 451 String i2 = item2.toString().substring(0,posB); 452 453 String a = (pim._ignoreCase)?(i1.toLowerCase()):(i1); 454 String b = (pim._ignoreCase)?(i2.toLowerCase()):(i2); 455 return a.equals(b); 456 } 457 public int compare(X item1, X item2, PredictiveInputModel<X> pim) { 458 int posA = item1.toString().lastIndexOf(':'); 459 if (posA<0) { posA = item1.toString().length(); } 460 String i1 = item1.toString().substring(0,posA); 461 462 int posB = item2.toString().lastIndexOf(':'); 463 if (posB<0) { posB = item2.toString().length(); } 464 String i2 = item2.toString().substring(0,posB); 465 466 String a = (pim._ignoreCase)?(i1.toLowerCase()):(i1); 467 String b = (pim._ignoreCase)?(i2.toLowerCase()):(i2); 468 return a.compareTo(b); 469 } 470 public X getLongestMatch(X item, List <X> items, PredictiveInputModel<X> pim) { 471 if (items.size() > 0) return items.get(0); else return null; 473 } 474 public String getSharedMaskExtension(List <X> items, PredictiveInputModel<X> pim) { 475 return ""; } 477 public String getExtendedSharedMask(List <X> items, PredictiveInputModel<X> pim) { 478 return pim._mask; 479 } 480 public String force(X item, String mask) { 481 int pos = mask.lastIndexOf(':'); 482 if (pos<0) { 483 return item.toString(); 484 } 485 else { 486 return item.toString()+mask.substring(pos); 487 } 488 } 489 }; 490 491 492 private volatile ArrayList <T> _items = new ArrayList <T>(); 493 494 495 private volatile int _index = 0; 496 497 498 private final ArrayList <T> _matchingItems = new ArrayList <T>(); 499 500 501 private volatile String _mask = ""; 502 503 504 private volatile boolean _ignoreCase = false; 505 506 507 private volatile MatchingStrategy<T> _strategy; 508 509 513 public PredictiveInputModel(boolean ignoreCase, PredictiveInputModel<T> pim) { 514 this(ignoreCase, pim._strategy, pim._items); 515 setMask(pim.getMask()); 516 } 517 518 523 public PredictiveInputModel(boolean ignoreCase, MatchingStrategy<T> strategy, List <T> items) { 524 _ignoreCase = ignoreCase; 525 _strategy = strategy; 526 setList(items); 527 } 528 529 535 public PredictiveInputModel(boolean ignoreCase, MatchingStrategy<T> strategy, T... items) { 536 _ignoreCase = ignoreCase; 537 _strategy = strategy; 538 setList(items); 539 } 540 541 544 public void setStrategy(MatchingStrategy<T> strategy) { 545 _strategy = strategy; 546 updateMatchingStrings(_items); 547 } 548 549 553 public void setList(List <T> items) { 554 _items = new ArrayList <T>(items); 555 Collections.sort(_items); 556 updateMatchingStrings(_items); 557 } 558 559 562 public void setList(T... items) { 563 _items = new ArrayList <T>(items.length); 564 for(T s: items) _items.add(s); 565 Collections.sort(_items); 566 updateMatchingStrings(_items); 567 } 568 569 572 public void setList(PredictiveInputModel<T> pim) { setList(pim._items); } 573 574 577 public String getMask() { return _mask; } 578 579 582 public void setMask(String mask) { 583 _mask = mask; 584 updateMatchingStrings(_items); 585 } 586 587 592 private int indexOf(ArrayList <T> l, T item) { 593 int index = 0; 594 for (T i: l) { 595 if (_strategy.equivalent(item, i, this)) return index; 596 ++index; 597 } 598 return -1; 599 } 600 601 604 private void updateMatchingStrings(ArrayList <T> items) { 605 items = new ArrayList <T>(items); _matchingItems.clear(); 607 for(T s: items) { 608 if (_strategy.isMatch(s, this)) _matchingItems.add(s); 609 } 610 if (_items.size() > 0) setCurrentItem(_items.get(_index)); 611 else _index = 0; 612 } 613 614 617 public T getCurrentItem() { 618 if (_items.size() > 0) return _items.get(_index); 619 else return null; 620 } 621 622 626 public void setCurrentItem(T item) { 627 if (_items.size() == 0) { 628 _index = 0; 629 return; 630 } 631 boolean found = false; 632 int index = indexOf(_items, item); 633 if (index<0) { 634 pickClosestMatch(item); 636 } 637 else { 638 for (int i=index; i<_items.size(); ++i) { 639 if (0 <= indexOf(_matchingItems, _items.get(i))) { 640 _index = i; 641 found = true; 642 break; 643 } 644 } 645 if (!found) { 646 pickClosestMatch(item); 647 } 648 } 649 } 650 651 654 private void pickClosestMatch(T item) { 655 if (_matchingItems.size() > 0) { 656 T follows = _matchingItems.get(0); 658 for (T i: _matchingItems) { 659 if (_strategy.compare(item, i, this) < 0) { 660 break; 661 } 662 follows = i; 663 } 664 _index = indexOf(_items, follows); 665 } 666 else { 667 _index = indexOf(_items, _strategy.getLongestMatch(item, _items, this)); 668 } 669 } 670 671 675 public List <T> getMatchingItems() { 676 return new ArrayList <T>(_matchingItems); 677 } 678 679 685 public String getSharedMaskExtension() { 686 return _strategy.getSharedMaskExtension(_matchingItems, this); 687 } 688 689 694 public void extendMask(String extension) { 695 _mask = _mask + extension; 696 updateMatchingStrings(_matchingItems); 697 } 698 699 700 703 public void extendSharedMask() { 704 _mask = _strategy.getExtendedSharedMask(_matchingItems, this); 705 updateMatchingStrings(_matchingItems); 706 } 707 } 708 | Popular Tags |