001: /*******************************************************************************
002: * Copyright (c) 2007 IBM Corporation and others.
003: * All rights reserved. This program and the accompanying materials
004: * are made available under the terms of the Eclipse Public License v1.0
005: * which accompanies this distribution, and is available at
006: * http://www.eclipse.org/legal/epl-v10.html
007: *
008: * Contributors:
009: * IBM Corporation - initial API and implementation
010: ******************************************************************************/package org.eclipse.ui.internal.services;
011:
012: import java.util.ArrayList;
013: import java.util.Arrays;
014: import java.util.Collection;
015: import java.util.HashMap;
016: import java.util.HashSet;
017: import java.util.Map;
018: import java.util.Set;
019:
020: import org.eclipse.core.commands.util.Tracing;
021: import org.eclipse.core.expressions.Expression;
022: import org.eclipse.core.expressions.ExpressionInfo;
023: import org.eclipse.core.runtime.ISafeRunnable;
024: import org.eclipse.core.runtime.ListenerList;
025: import org.eclipse.core.runtime.SafeRunner;
026: import org.eclipse.jface.util.IPropertyChangeListener;
027: import org.eclipse.jface.util.PropertyChangeEvent;
028: import org.eclipse.ui.ISources;
029: import org.eclipse.ui.internal.WorkbenchPlugin;
030: import org.eclipse.ui.internal.misc.Policy;
031:
032: /**
033: * @since 3.3
034: *
035: */
036: public class EvaluationAuthority extends ExpressionAuthority {
037:
038: /**
039: *
040: */
041: private static final String COMPONENT = "EVALUATION"; //$NON-NLS-1$
042:
043: /**
044: * A bucket sort of the evaluation references based on source priority. Each
045: * reference will appear only once per set, but may appear in multiple sets.
046: * If no references are defined for a particular priority level, then the
047: * array at that index will only contain <code>null</code>.
048: */
049: private final Map cachesBySourceName = new HashMap();
050: private ListenerList serviceListeners = new ListenerList();
051: private int notifying = 0;
052:
053: // private final Map cachesByExpression = new HashMap();
054:
055: public void addEvaluationListener(IEvaluationReference ref) {
056: // we update the source priority bucket sort of activations.
057: String[] sourceNames = getNames(ref);
058: for (int i = 0; i < sourceNames.length; i++) {
059: Map cachesByExpression = (HashMap) cachesBySourceName
060: .get(sourceNames[i]);
061: if (cachesByExpression == null) {
062: cachesByExpression = new HashMap(1);
063: cachesBySourceName.put(sourceNames[i],
064: cachesByExpression);
065: }
066: final Expression expression = ref.getExpression();
067: Set caches = (Set) cachesByExpression.get(expression);
068: if (caches == null) {
069: caches = new HashSet();
070: cachesByExpression.put(expression, caches);
071: }
072: caches.add(ref);
073: }
074:
075: boolean result = evaluate(ref);
076: firePropertyChange(ref, null, new Boolean(result));
077: }
078:
079: /**
080: * @param ref
081: * @return
082: */
083: private String[] getNames(IEvaluationReference ref) {
084: ExpressionInfo info = new ExpressionInfo();
085: ref.getExpression().collectExpressionInfo(info);
086: if (info.hasDefaultVariableAccess()) {
087: ArrayList l = new ArrayList(Arrays.asList(info
088: .getAccessedVariableNames()));
089: l.add(ISources.ACTIVE_CURRENT_SELECTION_NAME);
090: return (String[]) l.toArray(new String[l.size()]);
091: }
092: return info.getAccessedVariableNames();
093: }
094:
095: /*
096: * (non-Javadoc)
097: *
098: * @see org.eclipse.ui.internal.services.ExpressionAuthority#sourceChanged(int)
099: */
100: protected void sourceChanged(int sourcePriority) {
101: // no-op, we want the other one
102: }
103:
104: /*
105: * (non-Javadoc)
106: *
107: * @see org.eclipse.ui.internal.services.ExpressionAuthority#sourceChanged(java.lang.String[])
108: */
109: protected void sourceChanged(String[] sourceNames) {
110: startSourceChange(sourceNames);
111: try {
112: // evaluations to recompute
113: for (int i = 0; i < sourceNames.length; i++) {
114: Map cachesByExpression = (HashMap) cachesBySourceName
115: .get(sourceNames[i]);
116: if (cachesByExpression != null) {
117: Collection v = cachesByExpression.values();
118: Set[] expressionCaches = (Set[]) v
119: .toArray(new Set[v.size()]);
120: for (int j = 0; j < expressionCaches.length; j++) {
121: if (expressionCaches[j].size() > 0) {
122: IEvaluationReference[] refs = (IEvaluationReference[]) expressionCaches[j]
123: .toArray(new IEvaluationReference[expressionCaches[j]
124: .size()]);
125: refsWithSameExpression(refs);
126: }
127: }
128: }
129: }
130: } finally {
131: endSourceChange(sourceNames);
132: }
133: }
134:
135: /**
136: * This will evaluate all refs with the same expression.
137: * @param refs
138: */
139: private void refsWithSameExpression(IEvaluationReference[] refs) {
140: int k = 0;
141: while (k < refs.length && !refs[k].isPostingChanges()) {
142: k++;
143: }
144: if (k >= refs.length) {
145: return;
146: }
147: IEvaluationReference ref = refs[k];
148: boolean oldValue = evaluate(ref);
149: ref.clearResult();
150: final boolean newValue = evaluate(ref);
151: if (oldValue != newValue) {
152: firePropertyChange(ref, new Boolean(oldValue), new Boolean(
153: newValue));
154: }
155: for (k++; k < refs.length; k++) {
156: ref = refs[k];
157: // this is not as expensive as it looks
158: if (ref.isPostingChanges()) {
159: oldValue = evaluate(ref);
160: if (oldValue != newValue) {
161: ref.setResult(newValue);
162: firePropertyChange(ref, new Boolean(oldValue),
163: new Boolean(newValue));
164: }
165: }
166: }
167: }
168:
169: /**
170: * @param sourceNames
171: */
172: private void startSourceChange(final String[] sourceNames) {
173: if (Policy.DEBUG_SOURCES) {
174: Tracing.printTrace(COMPONENT, "start source changed: " //$NON-NLS-1$
175: + Arrays.asList(sourceNames));
176: }
177: notifying++;
178: if (notifying == 1) {
179: fireServiceChange(IEvaluationService.PROP_NOTIFYING,
180: new Boolean(false), new Boolean(true));
181: }
182: }
183:
184: /**
185: * @param sourceNames
186: */
187: private void endSourceChange(final String[] sourceNames) {
188: if (Policy.DEBUG_SOURCES) {
189: Tracing.printTrace(COMPONENT, "end source changed: " //$NON-NLS-1$
190: + Arrays.asList(sourceNames));
191: }
192: if (notifying == 1) {
193: fireServiceChange(IEvaluationService.PROP_NOTIFYING,
194: new Boolean(true), new Boolean(false));
195: }
196: notifying--;
197: }
198:
199: /**
200: * @param ref
201: */
202: public void removeEvaluationListener(IEvaluationReference ref) {
203: // Next we update the source priority bucket sort of activations.
204: String[] sourceNames = getNames(ref);
205: for (int i = 0; i < sourceNames.length; i++) {
206: Map cachesByExpression = (HashMap) cachesBySourceName
207: .get(sourceNames[i]);
208: if (cachesByExpression != null) {
209: Set caches = (Set) cachesByExpression.get(ref
210: .getExpression());
211: if (caches != null) {
212: caches.remove(ref);
213: if (caches.isEmpty()) {
214: cachesByExpression.remove(ref.getExpression());
215: }
216: }
217: if (cachesByExpression.isEmpty()) {
218: cachesBySourceName.remove(sourceNames[i]);
219: }
220: }
221: }
222: boolean result = evaluate(ref);
223: firePropertyChange(ref, new Boolean(result), null);
224: }
225:
226: /**
227: * @param ref
228: * @param oldValue
229: * @param newValue
230: */
231: private void firePropertyChange(IEvaluationReference ref,
232: Object oldValue, Object newValue) {
233: ref.getListener().propertyChange(
234: new PropertyChangeEvent(ref, ref.getProperty(),
235: oldValue, newValue));
236: }
237:
238: private void fireServiceChange(final String property,
239: final Object oldValue, final Object newValue) {
240: Object[] listeners = serviceListeners.getListeners();
241: for (int i = 0; i < listeners.length; i++) {
242: final IPropertyChangeListener listener = (IPropertyChangeListener) listeners[i];
243: SafeRunner.run(new ISafeRunnable() {
244: public void handleException(Throwable exception) {
245: WorkbenchPlugin.log(exception);
246: }
247:
248: public void run() throws Exception {
249: listener.propertyChange(new PropertyChangeEvent(
250: EvaluationAuthority.this , property,
251: oldValue, newValue));
252: }
253: });
254: }
255: }
256:
257: /**
258: * @param listener
259: */
260: public void addServiceListener(IPropertyChangeListener listener) {
261: serviceListeners.add(listener);
262: }
263:
264: /**
265: * @param listener
266: */
267: public void removeServiceListener(IPropertyChangeListener listener) {
268: serviceListeners.remove(listener);
269: }
270: }
|