001: /*
002: * ProxyActionProvider.java
003: *
004: * Created on January 24, 2004, 2:55 PM
005: */
006:
007: package org.netbeans.actions.spi;
008:
009: import java.util.ArrayList;
010: import java.util.Arrays;
011: import java.util.HashMap;
012: import java.util.List;
013: import java.util.Map;
014: import javax.swing.Icon;
015: import org.netbeans.actions.spi.ActionProvider;
016:
017: /** ActionProvider impl which can proxy other action providers, such as
018: * a legacy wrapper and some other implementation.
019: *
020: * @author Tim Boudreau
021: */
022: public abstract class ProxyActionProvider extends ActionProvider {
023: /** Stores mapping of containerContext + action name to action providers,
024: * so implementation methods of ActionProvider can efficiently
025: * look up which provider should be proxied for a given action and
026: * container context */
027: private Map cache = new HashMap();
028:
029: /** Creates a new instance of ProxyActionProvider */
030: protected ProxyActionProvider() {
031: }
032:
033: /** Fetch the action providers being proxied */
034: protected abstract ActionProvider[] getProviders();
035:
036: /** Sorts the actions that will be returned by getActionNames from the
037: * set of providers. The default implementation does nothing except
038: * concatenate the arrays of actions.
039: *
040: * @param providers the action providers
041: * @param An array of arrays of strings provided by the passed providers
042: * @param length The combined number of actions passed in in the actions array
043: * @param A sorted array containing all the strings passed in in the actions
044: * parameter. The result array <strong>must</strong> contain the same number
045: * of actions as the length parameter - this is enforced. Do not use
046: * sort() as a convenient place to filter out actions. */
047: protected String[] sort(ActionProvider[] providers,
048: Object[] actions, int length) {
049: String[] result = new String[length];
050: int pos = 0;
051: for (int i = 0; i < providers.length; i++) {
052: String[] curr = (String[]) actions[i];
053: System.arraycopy(curr, 0, result, pos, curr.length);
054: pos += curr.length;
055: }
056: return result;
057: }
058:
059: /** Call this if the return value of getProviders has changed to dump any
060: * cached mappings between providers, contexts and actions */
061: protected void providersChanged() {
062: cache.clear();
063: }
064:
065: /** Given a container context and an action, reverse-lookup which action
066: * provider we are proxying is responsible for it. */
067: protected final ActionProvider findProviderOf(String containerCtx,
068: String action) {
069: ActionProvider result = findInCache(containerCtx, action);
070: if (result == null) {
071: result = locateAndCache(containerCtx, action);
072: }
073: return result;
074: }
075:
076: public final String[] getActionNames(String containerCtx) {
077: ActionProvider[] providers = getProviders();
078: List list = new ArrayList(providers.length);
079: int count = 0;
080: for (int i = 0; i < providers.length; i++) {
081: String[] curr = providers[i].getActionNames(containerCtx);
082: count += curr.length;
083: list.add(curr);
084: }
085: Object[] actions = new Object[list.size()];
086: actions = list.toArray(actions);
087: String[] result = sort(providers, actions, count);
088: if (result == null) {
089: throw new IllegalStateException("Sort may not return null"); //NOI18N
090: }
091: if (result.length != count) {
092: throw new IllegalStateException("Passed " + count
093: + " actions to " + " sort() but only got "
094: + result.length + " back. Do NOT use "
095: + "sort() to filter actions."); //NOI18N
096: }
097: return result;
098: }
099:
100: public final int getActionType(String actionName,
101: String containerCtx) {
102: ActionProvider provider = findProviderOf(actionName,
103: containerCtx);
104: return provider.getActionType(actionName, containerCtx);
105: }
106:
107: public final String getDescription(String actionName,
108: String containerCtx) {
109: ActionProvider provider = findProviderOf(actionName,
110: containerCtx);
111: return provider.getDescription(actionName, containerCtx);
112: }
113:
114: public final String getDisplayName(String actionName,
115: String containerCtx) {
116: ActionProvider provider = findProviderOf(actionName,
117: containerCtx);
118: return provider.getDisplayName(actionName, containerCtx);
119: }
120:
121: public final Icon getIcon(String actionName, String containerCtx,
122: int type) {
123: ActionProvider provider = findProviderOf(actionName,
124: containerCtx);
125: return provider.getIcon(actionName, containerCtx, type);
126: }
127:
128: public final int getMnemonic(String actionName, String containerCtx) {
129: ActionProvider provider = findProviderOf(actionName,
130: containerCtx);
131: return provider.getMnemonic(actionName, containerCtx);
132: }
133:
134: public final int getMnemonicIndex(String actionName,
135: String containerCtx) {
136: ActionProvider provider = findProviderOf(actionName,
137: containerCtx);
138: return provider.getMnemonicIndex(actionName, containerCtx);
139: }
140:
141: public final int getState(String actionName, String containerCtx,
142: Map context) {
143: ActionProvider provider = findProviderOf(actionName,
144: containerCtx);
145: return provider.getState(actionName, containerCtx, context);
146: }
147:
148: //*************** Caching implementation for reverse lookups ******************
149:
150: private Object munge(String containerCtx, String action) {
151: return new Integer(containerCtx.hashCode() ^ action.hashCode());
152: }
153:
154: private ActionProvider findInCache(String containerCtx,
155: String action) {
156: Object key = munge(containerCtx, action);
157: return (ActionProvider) cache.get(key);
158: }
159:
160: private ActionProvider locateAndCache(String containerCtx,
161: String action) {
162: ActionProvider[] providers = getProviders();
163: ActionProvider result = null;
164: Object key = munge(containerCtx, action);
165: for (int i = 0; i < providers.length; i++) {
166: String[] curr = providers[i].getActionNames(containerCtx);
167: if (Arrays.asList(curr).contains(action)) {
168: result = providers[i];
169: //XXX may want to use result.hashCode as the value in the cache
170: //instead, and then look up the provider by the hashcode in
171: //findProviderOf, to avoid holding any references to the
172: //provider objects themselves. This will only be needed if
173: //we're want to be able to look up/recreate providers on the
174: //fly, which is only needed if we expect providers to change
175: //frequently. Otherwise, caching the provider is fine, and
176: //we can just dump any references to providers in providersChanged().
177: cache.put(key, result);
178: break;
179: }
180: }
181: return result;
182: }
183: }
|