1 11 package org.eclipse.jface.bindings; 12 13 import java.io.BufferedWriter ; 14 import java.io.IOException ; 15 import java.io.StringWriter ; 16 import java.util.ArrayList ; 17 import java.util.Arrays ; 18 import java.util.Collection ; 19 import java.util.Collections ; 20 import java.util.HashMap ; 21 import java.util.HashSet ; 22 import java.util.Iterator ; 23 import java.util.List ; 24 import java.util.Locale ; 25 import java.util.Map ; 26 import java.util.Set ; 27 import java.util.StringTokenizer ; 28 29 import org.eclipse.core.commands.CommandManager; 30 import org.eclipse.core.commands.ParameterizedCommand; 31 import org.eclipse.core.commands.common.HandleObjectManager; 32 import org.eclipse.core.commands.common.NotDefinedException; 33 import org.eclipse.core.commands.contexts.Context; 34 import org.eclipse.core.commands.contexts.ContextManager; 35 import org.eclipse.core.commands.contexts.ContextManagerEvent; 36 import org.eclipse.core.commands.contexts.IContextManagerListener; 37 import org.eclipse.core.commands.util.Tracing; 38 import org.eclipse.core.runtime.IStatus; 39 import org.eclipse.core.runtime.MultiStatus; 40 import org.eclipse.core.runtime.Status; 41 import org.eclipse.jface.bindings.keys.IKeyLookup; 42 import org.eclipse.jface.bindings.keys.KeyLookupFactory; 43 import org.eclipse.jface.bindings.keys.KeyStroke; 44 import org.eclipse.jface.contexts.IContextIds; 45 import org.eclipse.jface.internal.InternalPolicy; 46 import org.eclipse.jface.util.Policy; 47 import org.eclipse.jface.util.Util; 48 import org.eclipse.swt.SWT; 49 50 68 public final class BindingManager extends HandleObjectManager implements 69 IContextManagerListener, ISchemeListener { 70 71 76 public static boolean DEBUG = false; 77 78 81 private static final TriggerSequence[] EMPTY_TRIGGER_SEQUENCE = new TriggerSequence[0]; 82 83 86 private static final String LOCALE_SEPARATOR = "_"; 88 105 private static final void addReverseLookup(final Map map, final Object key, 106 final Object value) { 107 if (map == null) { 108 return; 109 } 110 111 final Object currentValue = map.get(key); 112 if (currentValue != null) { 113 final Collection values = (Collection ) currentValue; 114 values.add(value); 115 } else { final Collection values = new ArrayList (1); 117 values.add(value); 118 map.put(key, values); 119 } 120 } 121 122 142 private static final String [] expand(String string, final String separator) { 143 if (string == null || separator == null) { 145 return new String [0]; 146 } 147 148 final List strings = new ArrayList (); 149 final StringBuffer stringBuffer = new StringBuffer (); 150 string = string.trim(); if (string.length() > 0) { 152 final StringTokenizer stringTokenizer = new StringTokenizer (string, 153 separator); 154 while (stringTokenizer.hasMoreElements()) { 155 if (stringBuffer.length() > 0) { 156 stringBuffer.append(separator); 157 } 158 stringBuffer.append(((String ) stringTokenizer.nextElement()) 159 .trim()); 160 strings.add(stringBuffer.toString()); 161 } 162 } 163 Collections.reverse(strings); 164 strings.add(Util.ZERO_LENGTH_STRING); 165 strings.add(null); 166 return (String []) strings.toArray(new String [strings.size()]); 167 } 168 169 175 private Map activeBindings = null; 176 177 184 private Map activeBindingsByParameterizedCommand = null; 185 186 private Set triggerConflicts = new HashSet (); 187 188 195 private Scheme activeScheme = null; 196 197 202 private String [] activeSchemeIds = null; 203 204 207 private int bindingCount = 0; 208 209 212 private Set bindingErrors = new HashSet (); 213 214 220 private Binding[] bindings = null; 221 222 227 private Map cachedBindings = new HashMap (); 228 229 234 private final CommandManager commandManager; 235 236 241 private final ContextManager contextManager; 242 243 247 private String locale = Locale.getDefault().toString(); 248 249 254 private String [] locales = expand(locale, LOCALE_SEPARATOR); 255 256 260 private String platform = SWT.getPlatform(); 261 262 267 private String [] platforms = expand(platform, Util.ZERO_LENGTH_STRING); 268 269 276 private Map prefixTable = null; 277 278 293 public BindingManager(final ContextManager contextManager, 294 final CommandManager commandManager) { 295 if (contextManager == null) { 296 throw new NullPointerException ( 297 "A binding manager requires a context manager"); } 299 300 if (commandManager == null) { 301 throw new NullPointerException ( 302 "A binding manager requires a command manager"); } 304 305 this.contextManager = contextManager; 306 contextManager.addContextManagerListener(this); 307 this.commandManager = commandManager; 308 } 309 310 323 public final void addBinding(final Binding binding) { 324 if (binding == null) { 325 throw new NullPointerException ("Cannot add a null binding"); } 327 328 if (bindings == null) { 329 bindings = new Binding[1]; 330 } else if (bindingCount >= bindings.length) { 331 final Binding[] oldBindings = bindings; 332 bindings = new Binding[oldBindings.length * 2]; 333 System.arraycopy(oldBindings, 0, bindings, 0, oldBindings.length); 334 } 335 bindings[bindingCount++] = binding; 336 clearCache(); 337 } 338 339 352 public final void addBindingManagerListener( 353 final IBindingManagerListener listener) { 354 addListenerObject(listener); 355 } 356 357 379 private final Map buildPrefixTable(final Map activeBindings) { 380 final Map prefixTable = new HashMap (); 381 382 final Iterator bindingItr = activeBindings.entrySet().iterator(); 383 while (bindingItr.hasNext()) { 384 final Map.Entry entry = (Map.Entry ) bindingItr.next(); 385 final TriggerSequence triggerSequence = (TriggerSequence) entry 386 .getKey(); 387 388 if (!prefixTable.containsKey(triggerSequence)) { 390 prefixTable.put(triggerSequence, null); 391 } 392 393 final TriggerSequence[] prefixes = triggerSequence.getPrefixes(); 394 final int prefixesLength = prefixes.length; 395 if (prefixesLength == 0) { 396 continue; 397 } 398 399 final Binding binding = (Binding) entry.getValue(); 401 for (int i = 0; i < prefixesLength; i++) { 402 final TriggerSequence prefix = prefixes[i]; 403 final Object value = prefixTable.get(prefix); 404 if ((prefixTable.containsKey(prefix)) && (value instanceof Map )) { 405 ((Map ) value).put(triggerSequence, binding); 406 } else { 407 final Map map = new HashMap (); 408 prefixTable.put(prefix, map); 409 map.put(triggerSequence, binding); 410 } 411 } 412 } 413 414 return prefixTable; 415 } 416 417 426 private final void clearCache() { 427 if (DEBUG) { 428 Tracing.printTrace("BINDINGS", "Clearing cache"); } 430 cachedBindings.clear(); 431 clearSolution(); 432 } 433 434 441 private final void clearSolution() { 442 setActiveBindings(null, null, null, null); 443 } 444 445 462 private final int compareSchemes(final String schemeId1, 463 final String schemeId2) { 464 if (!schemeId2.equals(schemeId1)) { 465 for (int i = 0; i < activeSchemeIds.length; i++) { 466 final String schemePointer = activeSchemeIds[i]; 467 if (schemeId2.equals(schemePointer)) { 468 return 1; 469 470 } else if (schemeId1.equals(schemePointer)) { 471 return -1; 472 473 } 474 475 } 476 } 477 478 return 0; 479 } 480 481 514 private final void computeBindings(final Map activeContextTree, 515 final Map bindingsByTrigger, final Map triggersByCommandId, 516 final Map conflictsByTrigger) { 517 520 final Binding[] trimmedBindings = removeDeletions(bindings); 521 522 526 final Map possibleBindings = new HashMap (); 527 final int length = trimmedBindings.length; 528 for (int i = 0; i < length; i++) { 529 final Binding binding = trimmedBindings[i]; 530 boolean found; 531 532 final String contextId = binding.getContextId(); 534 if ((activeContextTree != null) 535 && (!activeContextTree.containsKey(contextId))) { 536 continue; 537 } 538 539 if (!localeMatches(binding)) { 541 continue; 542 } 543 544 if (!platformMatches(binding)) { 546 continue; 547 } 548 549 final String schemeId = binding.getSchemeId(); 551 found = false; 552 if (activeSchemeIds != null) { 553 for (int j = 0; j < activeSchemeIds.length; j++) { 554 if (Util.equals(schemeId, activeSchemeIds[j])) { 555 found = true; 556 break; 557 } 558 } 559 } 560 if (!found) { 561 continue; 562 } 563 564 final TriggerSequence trigger = binding.getTriggerSequence(); 566 final Object existingMatch = possibleBindings.get(trigger); 567 if (existingMatch instanceof Binding) { 568 possibleBindings.remove(trigger); 569 final Collection matches = new ArrayList (); 570 matches.add(existingMatch); 571 matches.add(binding); 572 possibleBindings.put(trigger, matches); 573 574 } else if (existingMatch instanceof Collection ) { 575 final Collection matches = (Collection ) existingMatch; 576 matches.add(binding); 577 578 } else { 579 possibleBindings.put(trigger, binding); 580 } 581 } 582 583 MultiStatus conflicts = new MultiStatus("org.eclipse.jface", 0, "Keybinding conflicts occurred. They may interfere with normal accelerator operation.", null); 586 592 final Iterator possibleBindingItr = possibleBindings.entrySet() 593 .iterator(); 594 while (possibleBindingItr.hasNext()) { 595 final Map.Entry entry = (Map.Entry ) possibleBindingItr.next(); 596 final TriggerSequence trigger = (TriggerSequence) entry.getKey(); 597 final Object match = entry.getValue(); 598 603 if (activeContextTree == null) { 604 final Collection bindings = new ArrayList (); 606 if (match instanceof Binding) { 607 bindings.add(match); 608 bindingsByTrigger.put(trigger, bindings); 609 addReverseLookup(triggersByCommandId, ((Binding) match) 610 .getParameterizedCommand(), trigger); 611 612 } else if (match instanceof Collection ) { 613 bindings.addAll((Collection ) match); 614 bindingsByTrigger.put(trigger, bindings); 615 616 final Iterator matchItr = bindings.iterator(); 617 while (matchItr.hasNext()) { 618 addReverseLookup(triggersByCommandId, 619 ((Binding) matchItr.next()) 620 .getParameterizedCommand(), trigger); 621 } 622 } 623 624 } else { 625 if (match instanceof Binding) { 627 final Binding binding = (Binding) match; 628 bindingsByTrigger.put(trigger, binding); 629 addReverseLookup(triggersByCommandId, binding 630 .getParameterizedCommand(), trigger); 631 632 } else if (match instanceof Collection ) { 633 final Binding winner = resolveConflicts((Collection ) match, 634 activeContextTree); 635 if (winner == null) { 636 conflictsByTrigger.put(trigger, match); 638 if (triggerConflicts.add(trigger)) { 639 final StringWriter sw = new StringWriter (); 640 final BufferedWriter buffer = new BufferedWriter (sw); 641 try { 642 buffer.write("A conflict occurred for "); buffer.write(trigger.toString()); 644 buffer.write(':'); 645 Iterator i = ((Collection ) match).iterator(); 646 while (i.hasNext()) { 647 buffer.newLine(); 648 buffer.write(i.next().toString()); 649 } 650 buffer.flush(); 651 } catch (IOException e) { 652 } 654 conflicts.add(new Status(IStatus.WARNING, 655 "org.eclipse.jface", sw.toString())); 657 } 658 if (DEBUG) { 659 Tracing.printTrace("BINDINGS", "A conflict occurred for " + trigger); Tracing.printTrace("BINDINGS", " " + match); } 663 } else { 664 bindingsByTrigger.put(trigger, winner); 665 addReverseLookup(triggersByCommandId, winner 666 .getParameterizedCommand(), trigger); 667 } 668 } 669 } 670 } 671 if (conflicts.getSeverity() != IStatus.OK) { 672 Policy.getLog().log(conflicts); 673 } 674 } 675 676 685 public final void contextManagerChanged( 686 final ContextManagerEvent contextManagerEvent) { 687 if (contextManagerEvent.isActiveContextsChanged()) { 688 recomputeBindings(); 690 } 691 } 692 693 705 private final int countStrokes(final Trigger[] triggers) { 706 int strokeCount = triggers.length; 707 for (int i = 0; i < triggers.length; i++) { 708 final Trigger trigger = triggers[i]; 709 if (trigger instanceof KeyStroke) { 710 final KeyStroke keyStroke = (KeyStroke) trigger; 711 final int modifierKeys = keyStroke.getModifierKeys(); 712 final IKeyLookup lookup = KeyLookupFactory.getDefault(); 713 if ((modifierKeys & lookup.getAlt()) != 0) { 714 strokeCount += 8; 715 } 716 if ((modifierKeys & lookup.getCtrl()) != 0) { 717 strokeCount += 2; 718 } 719 if ((modifierKeys & lookup.getShift()) != 0) { 720 strokeCount += 4; 721 } 722 if ((modifierKeys & lookup.getCommand()) != 0) { 723 strokeCount += 2; 724 } 725 } else { 726 strokeCount += 99; 727 } 728 } 729 730 return strokeCount; 731 } 732 733 750 private final Map createContextTreeFor(final Set contextIds) { 751 final Map contextTree = new HashMap (); 752 753 final Iterator contextIdItr = contextIds.iterator(); 754 while (contextIdItr.hasNext()) { 755 String childContextId = (String ) contextIdItr.next(); 756 while (childContextId != null) { 757 if (contextTree.containsKey(childContextId)) { 759 break; 760 } 761 762 final Context childContext = contextManager 764 .getContext(childContextId); 765 766 try { 768 final String parentContextId = childContext.getParentId(); 769 contextTree.put(childContextId, parentContextId); 770 childContextId = parentContextId; 771 } catch (final NotDefinedException e) { 772 break; } 774 } 775 } 776 777 return contextTree; 778 } 779 780 798 private final Map createFilteredContextTreeFor(final Set contextIds) { 799 boolean dialog = false; 801 boolean window = false; 802 Iterator contextIdItr = contextIds.iterator(); 803 while (contextIdItr.hasNext()) { 804 final String contextId = (String ) contextIdItr.next(); 805 if (IContextIds.CONTEXT_ID_DIALOG.equals(contextId)) { 806 dialog = true; 807 continue; 808 } 809 if (IContextIds.CONTEXT_ID_WINDOW.equals(contextId)) { 810 window = true; 811 continue; 812 } 813 } 814 815 820 contextIdItr = contextIds.iterator(); 821 while (contextIdItr.hasNext()) { 822 String contextId = (String ) contextIdItr.next(); 823 Context context = contextManager.getContext(contextId); 824 try { 825 String parentId = context.getParentId(); 826 while (parentId != null) { 827 if (IContextIds.CONTEXT_ID_DIALOG.equals(parentId)) { 828 if (!dialog) { 829 contextIdItr.remove(); 830 } 831 break; 832 } 833 if (IContextIds.CONTEXT_ID_WINDOW.equals(parentId)) { 834 if (!window) { 835 contextIdItr.remove(); 836 } 837 break; 838 } 839 if (IContextIds.CONTEXT_ID_DIALOG_AND_WINDOW 840 .equals(parentId)) { 841 if ((!window) && (!dialog)) { 842 contextIdItr.remove(); 843 } 844 break; 845 } 846 847 context = contextManager.getContext(parentId); 848 parentId = context.getParentId(); 849 } 850 } catch (NotDefinedException e) { 851 contextIdItr.remove(); 854 855 if (context == null || !bindingErrors.contains(context.getId())) { 857 if (context != null) { 858 bindingErrors.add(context.getId()); 859 } 860 861 Policy 863 .getLog() 864 .log( 865 new Status( 866 IStatus.ERROR, 867 Policy.JFACE, 868 IStatus.OK, 869 "Undefined context while filtering dialog/window contexts", e)); 871 } 872 } 873 } 874 875 return createContextTreeFor(contextIds); 876 } 877 878 892 private final void fireBindingManagerChanged(final BindingManagerEvent event) { 893 if (event == null) { 894 throw new NullPointerException (); 895 } 896 897 final Object [] listeners = getListeners(); 898 for (int i = 0; i < listeners.length; i++) { 899 final IBindingManagerListener listener = (IBindingManagerListener) listeners[i]; 900 listener.bindingManagerChanged(event); 901 } 902 } 903 904 919 private final Map getActiveBindings() { 920 if (activeBindings == null) { 921 recomputeBindings(); 922 } 923 924 return activeBindings; 925 } 926 927 943 private final Map getActiveBindingsByParameterizedCommand() { 944 if (activeBindingsByParameterizedCommand == null) { 945 recomputeBindings(); 946 } 947 948 return activeBindingsByParameterizedCommand; 949 } 950 951 966 public final Map getActiveBindingsDisregardingContext() { 967 if (bindings == null) { 968 return Collections.EMPTY_MAP; 970 } 971 972 final CachedBindingSet bindingCache = new CachedBindingSet(null, 974 locales, platforms, activeSchemeIds); 975 976 980 CachedBindingSet existingCache = (CachedBindingSet) cachedBindings 981 .get(bindingCache); 982 if (existingCache == null) { 983 existingCache = bindingCache; 984 cachedBindings.put(existingCache, existingCache); 985 } 986 Map commandIdsByTrigger = existingCache.getBindingsByTrigger(); 987 if (commandIdsByTrigger != null) { 988 if (DEBUG) { 989 Tracing.printTrace("BINDINGS", "Cache hit"); } 991 992 return Collections.unmodifiableMap(commandIdsByTrigger); 993 } 994 995 if (DEBUG) { 997 Tracing.printTrace("BINDINGS", "Cache miss"); } 999 1000 commandIdsByTrigger = new HashMap (); 1002 final Map triggersByParameterizedCommand = new HashMap (); 1003 final Map conflictsByTrigger = new HashMap (); 1004 computeBindings(null, commandIdsByTrigger, 1005 triggersByParameterizedCommand, conflictsByTrigger); 1006 existingCache.setBindingsByTrigger(commandIdsByTrigger); 1007 existingCache.setTriggersByCommandId(triggersByParameterizedCommand); 1008 existingCache.setConflictsByTrigger(conflictsByTrigger); 1009 return Collections.unmodifiableMap(commandIdsByTrigger); 1010 } 1011 1012 1028 private final Map getActiveBindingsDisregardingContextByParameterizedCommand() { 1029 if (bindings == null) { 1030 return Collections.EMPTY_MAP; 1032 } 1033 1034 final CachedBindingSet bindingCache = new CachedBindingSet(null, 1036 locales, platforms, activeSchemeIds); 1037 1038 1042 CachedBindingSet existingCache = (CachedBindingSet) cachedBindings 1043 .get(bindingCache); 1044 if (existingCache == null) { 1045 existingCache = bindingCache; 1046 cachedBindings.put(existingCache, existingCache); 1047 } 1048 Map triggersByParameterizedCommand = existingCache 1049 .getTriggersByCommandId(); 1050 if (triggersByParameterizedCommand != null) { 1051 if (DEBUG) { 1052 Tracing.printTrace("BINDINGS", "Cache hit"); } 1054 1055 return Collections.unmodifiableMap(triggersByParameterizedCommand); 1056 } 1057 1058 if (DEBUG) { 1060 Tracing.printTrace("BINDINGS", "Cache miss"); } 1062 1063 final Map commandIdsByTrigger = new HashMap (); 1065 final Map conflictsByTrigger = new HashMap (); 1066 triggersByParameterizedCommand = new HashMap (); 1067 computeBindings(null, commandIdsByTrigger, 1068 triggersByParameterizedCommand, conflictsByTrigger); 1069 existingCache.setBindingsByTrigger(commandIdsByTrigger); 1070 existingCache.setTriggersByCommandId(triggersByParameterizedCommand); 1071 existingCache.setConflictsByTrigger(conflictsByTrigger); 1072 1073 return Collections.unmodifiableMap(triggersByParameterizedCommand); 1074 } 1075 1076 1091 public final Collection getActiveBindingsDisregardingContextFlat() { 1092 final Collection bindingCollections = getActiveBindingsDisregardingContext() 1093 .values(); 1094 final Collection mergedBindings = new ArrayList (); 1095 final Iterator bindingCollectionItr = bindingCollections.iterator(); 1096 while (bindingCollectionItr.hasNext()) { 1097 final Collection bindingCollection = (Collection ) bindingCollectionItr 1098 .next(); 1099 if ((bindingCollection != null) && (!bindingCollection.isEmpty())) { 1100 mergedBindings.addAll(bindingCollection); 1101 } 1102 } 1103 1104 return mergedBindings; 1105 } 1106 1107 1127 public final TriggerSequence[] getActiveBindingsDisregardingContextFor( 1128 final ParameterizedCommand parameterizedCommand) { 1129 final Object object = getActiveBindingsDisregardingContextByParameterizedCommand() 1130 .get(parameterizedCommand); 1131 if (object instanceof Collection ) { 1132 final Collection collection = (Collection ) object; 1133 return (TriggerSequence[]) collection 1134 .toArray(new TriggerSequence[collection.size()]); 1135 } 1136 1137 return EMPTY_TRIGGER_SEQUENCE; 1138 } 1139 1140 1158 public final TriggerSequence[] getActiveBindingsFor( 1159 final ParameterizedCommand parameterizedCommand) { 1160 final Object object = getActiveBindingsByParameterizedCommand().get( 1161 parameterizedCommand); 1162 if (object instanceof Collection ) { 1163 final Collection collection = (Collection ) object; 1164 return (TriggerSequence[]) collection 1165 .toArray(new TriggerSequence[collection.size()]); 1166 } 1167 1168 return EMPTY_TRIGGER_SEQUENCE; 1169 } 1170 1171 1190 public final TriggerSequence[] getActiveBindingsFor(final String commandId) { 1191 final ParameterizedCommand parameterizedCommand = new ParameterizedCommand( 1192 commandManager.getCommand(commandId), null); 1193 final Object object = getActiveBindingsByParameterizedCommand().get( 1194 parameterizedCommand); 1195 if (object instanceof Collection ) { 1196 final Collection collection = (Collection ) object; 1197 return (TriggerSequence[]) collection 1198 .toArray(new TriggerSequence[collection.size()]); 1199 } 1200 1201 return EMPTY_TRIGGER_SEQUENCE; 1202 } 1203 1204 1216 private final Binding[] getActiveBindingsFor1(final String commandId) { 1217 final TriggerSequence[] triggers = getActiveBindingsFor(commandId); 1218 if (triggers.length == 0) { 1219 return null; 1220 } 1221 1222 final Map activeBindings = getActiveBindings(); 1223 if (activeBindings != null) { 1224 final Binding[] bindings = new Binding[triggers.length]; 1225 for (int i = 0; i < triggers.length; i++) { 1226 final TriggerSequence triggerSequence = triggers[i]; 1227 final Object object = activeBindings.get(triggerSequence); 1228 final Binding binding = (Binding) object; 1229 bindings[i] = binding; 1230 } 1231 return bindings; 1232 } 1233 1234 return null; 1235 } 1236 1237 1249 public final Scheme getActiveScheme() { 1250 return activeScheme; 1251 } 1252 1253 1271 public final TriggerSequence getBestActiveBindingFor(final String commandId) { 1272 final Binding[] bindings = getActiveBindingsFor1(commandId); 1273 if ((bindings == null) || (bindings.length == 0)) { 1274 return null; 1275 } 1276 1277 Binding bestBinding = bindings[0]; 1278 int compareTo; 1279 for (int i = 1; i < bindings.length; i++) { 1280 final Binding currentBinding = bindings[i]; 1281 1282 final String bestSchemeId = bestBinding.getSchemeId(); 1284 final String currentSchemeId = currentBinding.getSchemeId(); 1285 compareTo = compareSchemes(bestSchemeId, currentSchemeId); 1286 if (compareTo > 0) { 1287 bestBinding = currentBinding; 1288 } 1289 if (compareTo != 0) { 1290 continue; 1291 } 1292 1293 1297 final String bestLocale = bestBinding.getLocale(); 1298 final String currentLocale = currentBinding.getLocale(); 1299 if ((bestLocale == null) && (currentLocale != null)) { 1300 bestBinding = currentBinding; 1301 } 1302 if (!(Util.equals(bestLocale, currentLocale))) { 1303 continue; 1304 } 1305 1306 1310 final String bestPlatform = bestBinding.getPlatform(); 1311 final String currentPlatform = currentBinding.getPlatform(); 1312 if ((bestPlatform == null) && (currentPlatform != null)) { 1313 bestBinding = currentBinding; 1314 } 1315 if (!(Util.equals(bestPlatform, currentPlatform))) { 1316 continue; 1317 } 1318 1319 1323 final TriggerSequence bestTriggerSequence = bestBinding 1324 .getTriggerSequence(); 1325 final TriggerSequence currentTriggerSequence = currentBinding 1326 .getTriggerSequence(); 1327 final Trigger[] bestTriggers = bestTriggerSequence.getTriggers(); 1328 final Trigger[] currentTriggers = currentTriggerSequence 1329 .getTriggers(); 1330 compareTo = bestTriggers.length - currentTriggers.length; 1331 if (compareTo > 0) { 1332 bestBinding = currentBinding; 1333 } 1334 if (compareTo != 0) { 1335 continue; 1336 } 1337 1338 1343 compareTo = countStrokes(bestTriggers) 1344 - countStrokes(currentTriggers); 1345 if (compareTo > 0) { 1346 bestBinding = currentBinding; 1347 } 1348 if (compareTo != 0) { 1349 continue; 1350 } 1351 1352 compareTo = bestTriggerSequence.format().length() 1354 - currentTriggerSequence.format().length(); 1355 if (compareTo > 0) { 1356 bestBinding = currentBinding; 1357 } 1358 } 1359 1360 return bestBinding.getTriggerSequence(); 1361 } 1362 1363 1380 public final String getBestActiveBindingFormattedFor(final String commandId) { 1381 final TriggerSequence binding = getBestActiveBindingFor(commandId); 1382 if (binding != null) { 1383 return binding.format(); 1384 } 1385 1386 return null; 1387 } 1388 1389 1400 public final Binding[] getBindings() { 1401 if (bindings == null) { 1402 return null; 1403 } 1404 1405 final Binding[] returnValue = new Binding[bindingCount]; 1406 System.arraycopy(bindings, 0, returnValue, 0, bindingCount); 1407 return returnValue; 1408 } 1409 1410 1421 public final Scheme[] getDefinedSchemes() { 1422 return (Scheme[]) definedHandleObjects 1423 .toArray(new Scheme[definedHandleObjects.size()]); 1424 } 1425 1426 1437 public final String getLocale() { 1438 return locale; 1439 } 1440 1441 1457 public final Map getPartialMatches(final TriggerSequence trigger) { 1458 final Map partialMatches = (Map ) getPrefixTable().get(trigger); 1459 if (partialMatches == null) { 1460 return Collections.EMPTY_MAP; 1461 } 1462 1463 return partialMatches; 1464 } 1465 1466 1481 public final Binding getPerfectMatch(final TriggerSequence trigger) { 1482 return (Binding) getActiveBindings().get(trigger); 1483 } 1484 1485 1496 public final String getPlatform() { 1497 return platform; 1498 } 1499 1500 1516 private final Map getPrefixTable() { 1517 if (prefixTable == null) { 1518 recomputeBindings(); 1519 } 1520 1521 return prefixTable; 1522 } 1523 1524 1539 public final Scheme getScheme(final String schemeId) { 1540 checkId(schemeId); 1541 1542 Scheme scheme = (Scheme) handleObjectsById.get(schemeId); 1543 if (scheme == null) { 1544 scheme = new Scheme(schemeId); 1545 handleObjectsById.put(schemeId, scheme); 1546 scheme.addSchemeListener(this); 1547 } 1548 1549 return scheme; 1550 } 1551 1552 1567 private final String [] getSchemeIds(String schemeId) { 1568 final List strings = new ArrayList (); 1569 while (schemeId != null) { 1570 strings.add(schemeId); 1571 try { 1572 schemeId = getScheme(schemeId).getParentId(); 1573 } catch (final NotDefinedException e) { 1574 Policy.getLog().log( 1575 new Status(IStatus.ERROR, Policy.JFACE, IStatus.OK, 1576 "Failed ascending scheme parents", e)); 1578 return new String [0]; 1579 } 1580 } 1581 1582 return (String []) strings.toArray(new String [strings.size()]); 1583 } 1584 1585 1602 public final boolean isPartialMatch(final TriggerSequence trigger) { 1603 return (getPrefixTable().get(trigger) != null); 1604 } 1605 1606 1623 public final boolean isPerfectMatch(final TriggerSequence trigger) { 1624 return getActiveBindings().containsKey(trigger); 1625 } 1626 1627 1642 private final boolean localeMatches(final Binding binding) { 1643 boolean matches = false; 1644 1645 final String locale = binding.getLocale(); 1646 if (locale == null) { 1647 return true; } 1649 1650 for (int i = 0; i < locales.length; i++) { 1651 if (Util.equals(locales[i], locale)) { 1652 matches = true; 1653 break; 1654 } 1655 } 1656 1657 return matches; 1658 } 1659 1660 1675 private final boolean platformMatches(final Binding binding) { 1676 boolean matches = false; 1677 1678 final String platform = binding.getPlatform(); 1679 if (platform == null) { 1680 return true; } 1682 1683 for (int i = 0; i < platforms.length; i++) { 1684 if (Util.equals(platforms[i], platform)) { 1685 matches = true; 1686 break; 1687 } 1688 } 1689 1690 return matches; 1691 } 1692 1693 1709 private final void recomputeBindings() { 1710 if (bindings == null) { 1711 setActiveBindings(Collections.EMPTY_MAP, Collections.EMPTY_MAP, 1713 Collections.EMPTY_MAP, Collections.EMPTY_MAP); 1714 return; 1715 } 1716 1717 final Set activeContextIds = new HashSet (contextManager 1719 .getActiveContextIds()); 1720 final Map activeContextTree = createFilteredContextTreeFor(activeContextIds); 1721 1722 final CachedBindingSet bindingCache = new CachedBindingSet( 1724 activeContextTree, locales, platforms, activeSchemeIds); 1725 1726 1730 CachedBindingSet existingCache = (CachedBindingSet) cachedBindings 1731 .get(bindingCache); 1732 if (existingCache == null) { 1733 existingCache = bindingCache; 1734 cachedBindings.put(existingCache, existingCache); 1735 } 1736 Map commandIdsByTrigger = existingCache.getBindingsByTrigger(); 1737 if (commandIdsByTrigger != null) { 1738 if (DEBUG) { 1739 Tracing.printTrace("BINDINGS", "Cache hit"); } 1741 setActiveBindings(commandIdsByTrigger, existingCache 1742 .getTriggersByCommandId(), existingCache.getPrefixTable(), 1743 existingCache.getConflictsByTrigger()); 1744 return; 1745 } 1746 1747 if (DEBUG) { 1749 Tracing.printTrace("BINDINGS", "Cache miss"); } 1751 1752 commandIdsByTrigger = new HashMap (); 1754 final Map triggersByParameterizedCommand = new HashMap (); 1755 final Map conflictsByTrigger = new HashMap (); 1756 computeBindings(activeContextTree, commandIdsByTrigger, 1757 triggersByParameterizedCommand, conflictsByTrigger); 1758 existingCache.setBindingsByTrigger(commandIdsByTrigger); 1759 existingCache.setTriggersByCommandId(triggersByParameterizedCommand); 1760 existingCache.setConflictsByTrigger(conflictsByTrigger); 1761 setActiveBindings(commandIdsByTrigger, triggersByParameterizedCommand, 1762 buildPrefixTable(commandIdsByTrigger), 1763 conflictsByTrigger); 1764 existingCache.setPrefixTable(prefixTable); 1765 } 1766 1767 1781 public final void removeBinding(final Binding binding) { 1782 if (bindings == null || bindings.length < 1) { 1783 return; 1784 } 1785 1786 final Binding[] newBindings = new Binding[bindings.length]; 1787 boolean bindingsChanged = false; 1788 int index = 0; 1789 for (int i = 0; i < bindingCount; i++) { 1790 final Binding b = bindings[i]; 1791 if (b == binding) { 1792 bindingsChanged = true; 1793 } else { 1794 newBindings[index++] = b; 1795 } 1796 } 1797 1798 if (bindingsChanged) { 1799 this.bindings = newBindings; 1800 bindingCount = index; 1801 clearCache(); 1802 } 1803 } 1804 1805 1816 public final void removeBindingManagerListener( 1817 final IBindingManagerListener listener) { 1818 removeListenerObject(listener); 1819 } 1820 1821 1848 public final void removeBindings(final TriggerSequence sequence, 1849 final String schemeId, final String contextId, final String locale, 1850 final String platform, final String windowManager, final int type) { 1851 if ((bindings == null) || (bindingCount < 1)) { 1852 return; 1853 } 1854 1855 final Binding[] newBindings = new Binding[bindings.length]; 1856 boolean bindingsChanged = false; 1857 int index = 0; 1858 for (int i = 0; i < bindingCount; i++) { 1859 final Binding binding = bindings[i]; 1860 boolean equals = true; 1861 equals &= Util.equals(sequence, binding.getTriggerSequence()); 1862 equals &= Util.equals(schemeId, binding.getSchemeId()); 1863 equals &= Util.equals(contextId, binding.getContextId()); 1864 equals &= Util.equals(locale, binding.getLocale()); 1865 equals &= Util.equals(platform, binding.getPlatform()); 1866 equals &= (type == binding.getType()); 1867 if (equals) { 1868 bindingsChanged = true; 1869 } else { 1870 newBindings[index++] = binding; 1871 } 1872 } 1873 1874 if (bindingsChanged) { 1875 this.bindings = newBindings; 1876 bindingCount = index; 1877 clearCache(); 1878 } 1879 } 1880 1881 1898 private final Binding[] removeDeletions(final Binding[] bindings) { 1899 final Map deletions = new HashMap (); 1900 final Binding[] bindingsCopy = new Binding[bindingCount]; 1901 System.arraycopy(bindings, 0, bindingsCopy, 0, bindingCount); 1902 int deletedCount = 0; 1903 1904 for (int i = 0; i < bindingCount; i++) { 1906 final Binding binding = bindingsCopy[i]; 1907 if ((binding.getParameterizedCommand() == null) 1908 && (localeMatches(binding)) && (platformMatches(binding))) { 1909 final TriggerSequence sequence = binding.getTriggerSequence(); 1910 final Object currentValue = deletions.get(sequence); 1911 if (currentValue instanceof Binding) { 1912 final Collection collection = new ArrayList (2); 1913 collection.add(currentValue); 1914 collection.add(binding); 1915 deletions.put(sequence, collection); 1916 } else if (currentValue instanceof Collection ) { 1917 final Collection collection = (Collection ) currentValue; 1918 collection.add(binding); 1919 } else { 1920 deletions.put(sequence, binding); 1921 } 1922 bindingsCopy[i] = null; 1923 deletedCount++; 1924 } 1925 } 1926 1927 if (DEBUG) { 1928 Tracing.printTrace("BINDINGS", "There are " + deletions.size() + " deletion markers"); } 1931 1932 for (int i = 0; i < bindingCount; i++) { 1934 final Binding binding = bindingsCopy[i]; 1935 if (binding != null) { 1936 final Object deletion = deletions.get(binding 1937 .getTriggerSequence()); 1938 if (deletion instanceof Binding) { 1939 if (((Binding) deletion).deletes(binding)) { 1940 bindingsCopy[i] = null; 1941 deletedCount++; 1942 } 1943 1944 } else if (deletion instanceof Collection ) { 1945 final Collection collection = (Collection ) deletion; 1946 final Iterator iterator = collection.iterator(); 1947 while (iterator.hasNext()) { 1948 final Object deletionBinding = iterator.next(); 1949 if (deletionBinding instanceof Binding) { 1950 if (((Binding) deletionBinding).deletes(binding)) { 1951 bindingsCopy[i] = null; 1952 deletedCount++; 1953 break; 1954 } 1955 } 1956 } 1957 1958 } 1959 } 1960 } 1961 1962 final Binding[] returnValue = new Binding[bindingCount - deletedCount]; 1964 int index = 0; 1965 for (int i = 0; i < bindingCount; i++) { 1966 final Binding binding = bindingsCopy[i]; 1967 if (binding != null) { 1968 returnValue[index++] = binding; 1969 } 1970 } 1971 1972 return returnValue; 1973 } 1974 1975 1999 private final Binding resolveConflicts(final Collection bindings, 2000 final Map activeContextTree) { 2001 2007 boolean conflict = false; 2008 2009 final Iterator bindingItr = bindings.iterator(); 2010 Binding bestMatch = (Binding) bindingItr.next(); 2011 2012 2018 while (bindingItr.hasNext()) { 2019 final Binding current = (Binding) bindingItr.next(); 2020 2021 2026 final String currentSchemeId = current.getSchemeId(); 2027 final String bestSchemeId = bestMatch.getSchemeId(); 2028 final int compareTo = compareSchemes(bestSchemeId, currentSchemeId); 2029 if (compareTo > 0) { 2030 bestMatch = current; 2031 conflict = false; 2032 } 2033 if (compareTo != 0) { 2034 continue; 2035 } 2036 2037 2042 final String currentContext = current.getContextId(); 2043 final String bestContext = bestMatch.getContextId(); 2044 if (!currentContext.equals(bestContext)) { 2045 boolean goToNextBinding = false; 2046 2047 String contextPointer = currentContext; 2049 while (contextPointer != null) { 2050 if (contextPointer.equals(bestContext)) { 2051 bestMatch = current; 2053 conflict = false; 2054 goToNextBinding = true; 2055 break; 2056 } 2057 contextPointer = (String ) activeContextTree 2058 .get(contextPointer); 2059 } 2060 2061 contextPointer = bestContext; 2063 while (contextPointer != null) { 2064 if (contextPointer.equals(currentContext)) { 2065 goToNextBinding = true; 2067 break; 2068 } 2069 contextPointer = (String ) activeContextTree 2070 .get(contextPointer); 2071 } 2072 2073 if (goToNextBinding) { 2074 continue; 2075 } 2076 } 2077 2078 2081 if (current.getType() > bestMatch.getType()) { 2082 bestMatch = current; 2083 conflict = false; 2084 continue; 2085 } else if (bestMatch.getType() > current.getType()) { 2086 continue; 2087 } 2088 2089 conflict = true; 2091 } 2092 2093 if (conflict) { 2095 return null; 2096 } 2097 2098 return bestMatch; 2100 } 2101 2102 2115 public final void schemeChanged(final SchemeEvent schemeEvent) { 2116 if (schemeEvent.isDefinedChanged()) { 2117 final Scheme scheme = schemeEvent.getScheme(); 2118 final boolean schemeIdAdded = scheme.isDefined(); 2119 boolean activeSchemeChanged = false; 2120 if (schemeIdAdded) { 2121 definedHandleObjects.add(scheme); 2122 } else { 2123 definedHandleObjects.remove(scheme); 2124 2125 if (activeScheme == scheme) { 2126 activeScheme = null; 2127 activeSchemeIds = null; 2128 activeSchemeChanged = true; 2129 2130 clearSolution(); 2132 } 2133 } 2134 2135 if (isListenerAttached()) { 2136 fireBindingManagerChanged(new BindingManagerEvent(this, false, 2137 null, activeSchemeChanged, scheme, schemeIdAdded, 2138 false, false)); 2139 } 2140 } 2141 } 2142 2143 2166 private final void setActiveBindings(final Map activeBindings, 2167 final Map activeBindingsByCommandId, final Map prefixTable, 2168 final Map conflicts) { 2169 this.activeBindings = activeBindings; 2170 final Map previousBindingsByParameterizedCommand = this.activeBindingsByParameterizedCommand; 2171 this.activeBindingsByParameterizedCommand = activeBindingsByCommandId; 2172 this.prefixTable = prefixTable; 2173 InternalPolicy.currentConflicts = conflicts; 2174 2175 fireBindingManagerChanged(new BindingManagerEvent(this, true, 2176 previousBindingsByParameterizedCommand, false, null, false, 2177 false, false)); 2178 } 2179 2180 2195 public final void setActiveScheme(final Scheme scheme) 2196 throws NotDefinedException { 2197 if (scheme == null) { 2198 throw new NullPointerException ("Cannot activate a null scheme"); } 2200 2201 if ((scheme == null) || (!scheme.isDefined())) { 2202 throw new NotDefinedException( 2203 "Cannot activate an undefined scheme. " + scheme.getId()); 2205 } 2206 2207 if (Util.equals(activeScheme, scheme)) { 2208 return; 2209 } 2210 2211 activeScheme = scheme; 2212 activeSchemeIds = getSchemeIds(activeScheme.getId()); 2213 clearSolution(); 2214 fireBindingManagerChanged(new BindingManagerEvent(this, false, null, 2215 true, null, false, false, false)); 2216 } 2217 2218 2235 public final void setBindings(final Binding[] bindings) { 2236 if (Arrays.equals(this.bindings, bindings)) { 2237 return; } 2239 2240 if ((bindings == null) || (bindings.length == 0)) { 2241 this.bindings = null; 2242 bindingCount = 0; 2243 } else { 2244 final int bindingsLength = bindings.length; 2245 this.bindings = new Binding[bindingsLength]; 2246 System.arraycopy(bindings, 0, this.bindings, 0, bindingsLength); 2247 bindingCount = bindingsLength; 2248 } 2249 clearCache(); 2250 } 2251 2252 2268 public final void setLocale(final String locale) { 2269 if (locale == null) { 2270 throw new NullPointerException ("The locale cannot be null"); } 2272 2273 if (!Util.equals(this.locale, locale)) { 2274 this.locale = locale; 2275 this.locales = expand(locale, LOCALE_SEPARATOR); 2276 clearSolution(); 2277 fireBindingManagerChanged(new BindingManagerEvent(this, false, 2278 null, false, null, false, true, false)); 2279 } 2280 } 2281 2282 2298 public final void setPlatform(final String platform) { 2299 if (platform == null) { 2300 throw new NullPointerException ("The platform cannot be null"); } 2302 2303 if (!Util.equals(this.platform, platform)) { 2304 this.platform = platform; 2305 this.platforms = expand(platform, Util.ZERO_LENGTH_STRING); 2306 clearSolution(); 2307 fireBindingManagerChanged(new BindingManagerEvent(this, false, 2308 null, false, null, false, false, true)); 2309 } 2310 } 2311} 2312 | Popular Tags |