001: /*
002: * DO NOT ALTER OR REMOVE COPYRIGHT NOTICES OR THIS HEADER.
003: *
004: * Copyright 1997-2007 Sun Microsystems, Inc. All rights reserved.
005: *
006: * The contents of this file are subject to the terms of either the GNU
007: * General Public License Version 2 only ("GPL") or the Common
008: * Development and Distribution License("CDDL") (collectively, the
009: * "License"). You may not use this file except in compliance with the
010: * License. You can obtain a copy of the License at
011: * http://www.netbeans.org/cddl-gplv2.html
012: * or nbbuild/licenses/CDDL-GPL-2-CP. See the License for the
013: * specific language governing permissions and limitations under the
014: * License. When distributing the software, include this License Header
015: * Notice in each file and include the License file at
016: * nbbuild/licenses/CDDL-GPL-2-CP. Sun designates this
017: * particular file as subject to the "Classpath" exception as provided
018: * by Sun in the GPL Version 2 section of the License file that
019: * accompanied this code. If applicable, add the following below the
020: * License Header, with the fields enclosed by brackets [] replaced by
021: * your own identifying information:
022: * "Portions Copyrighted [year] [name of copyright owner]"
023: *
024: * Contributor(s):
025: *
026: * The Original Software is NetBeans. The Initial Developer of the Original
027: * Software is Sun Microsystems, Inc. Portions Copyright 1997-2006 Sun
028: * Microsystems, Inc. All Rights Reserved.
029: *
030: * If you wish your version of this file to be governed by only the CDDL
031: * or only the GPL Version 2, indicate your decision by adding
032: * "[Contributor] elects to include this software in this distribution
033: * under the [CDDL or GPL Version 2] license." If you do not indicate a
034: * single choice of license, a recipient has the option to distribute
035: * your version of this file under either the CDDL, the GPL Version 2 or
036: * to extend the choice of license to its licensees as provided above.
037: * However, if you add GPL Version 2 code and therefore, elected the GPL
038: * Version 2 license, then the option applies only if the new code is
039: * made subject to such option by the copyright holder.
040: */
041:
042: package org.netbeans.api.debugger;
043:
044: import java.beans.*;
045: import java.io.*;
046: import java.util.*;
047:
048: import org.netbeans.spi.debugger.ActionsProvider;
049: import org.netbeans.spi.debugger.ActionsProviderListener;
050: import org.openide.util.Cancellable;
051: import org.openide.util.Task;
052:
053: /**
054: * Manages some set of actions. Loads some set of ActionProviders registerred
055: * for some context, and allows to call isEnabled and doAction methods on them.
056: *
057: * @author Jan Jancura
058: */
059: public final class ActionsManager {
060:
061: /** Action constant for Step Over Action. */
062: public static final Object ACTION_STEP_OVER = "stepOver";
063:
064: /** Action constant for breakpoint hit action. */
065: public static final Object ACTION_RUN_INTO_METHOD = "runIntoMethod";
066:
067: /** Action constant for Step Into Action. */
068: public static final Object ACTION_STEP_INTO = "stepInto";
069:
070: /** Action constant for Step Out Action. */
071: public static final Object ACTION_STEP_OUT = "stepOut";
072:
073: /** Action constant for Step Operation Action. */
074: public static final Object ACTION_STEP_OPERATION = "stepOperation";
075:
076: /** Action constant for Continue Action. */
077: public static final Object ACTION_CONTINUE = "continue";
078:
079: /** Action constant for Start Action. */
080: public static final Object ACTION_START = "start";
081:
082: /** Action constant for Kill Action. */
083: public static final Object ACTION_KILL = "kill";
084:
085: /** Action constant for Make Caller Current Action. */
086: public static final Object ACTION_MAKE_CALLER_CURRENT = "makeCallerCurrent";
087:
088: /** Action constant for Make Callee Current Action. */
089: public static final Object ACTION_MAKE_CALLEE_CURRENT = "makeCalleeCurrent";
090:
091: /** Action constant for Pause Action. */
092: public static final Object ACTION_PAUSE = "pause";
093:
094: /** Action constant for Run to Cursor Action. */
095: public static final Object ACTION_RUN_TO_CURSOR = "runToCursor";
096:
097: /** Action constant for Pop Topmost Call Action. */
098: public static final Object ACTION_POP_TOPMOST_CALL = "popTopmostCall";
099:
100: /** Action constant for Fix Action. */
101: public static final Object ACTION_FIX = "fix";
102:
103: /** Action constant for Restart Action. */
104: public static final Object ACTION_RESTART = "restart";
105:
106: /** Action constant for Restart Action. */
107: public static final Object ACTION_TOGGLE_BREAKPOINT = "toggleBreakpoint";
108:
109: // variables ...............................................................
110:
111: private Vector listener = new Vector();
112: private HashMap listeners = new HashMap();
113: private HashMap actionProviders;
114: private Object actionProvidersLock = new Object();
115: private MyActionListener actionListener = new MyActionListener();
116: private Lookup lookup;
117: private boolean doiingDo = false;
118: private boolean destroy = false;
119:
120: /**
121: * Create a new instance of ActionManager.
122: * This is called from synchronized blocks of other classes that need to have
123: * just one instance of this. Therefore do not put any foreign calls here.
124: */
125: ActionsManager(Lookup lookup) {
126: this .lookup = lookup;
127: }
128:
129: // main public methods .....................................................
130:
131: /**
132: * Performs action on this DebbuggerEngine.
133: *
134: * @param action action constant (default set of constanct are defined
135: * in this class with ACTION_ prefix)
136: * @return true if action has been performed
137: */
138: public final void doAction(final Object action) {
139: doiingDo = true;
140: ArrayList l;
141: synchronized (actionProvidersLock) {
142: if (actionProviders == null)
143: initActionImpls();
144: l = (ArrayList) actionProviders.get(action);
145: if (l != null) {
146: l = (ArrayList) l.clone();
147: }
148: }
149: boolean done = false;
150: if (l != null) {
151: int i, k = l.size();
152: for (i = 0; i < k; i++) {
153: if (((ActionsProvider) l.get(i)).isEnabled(action)) {
154: done = true;
155: ((ActionsProvider) l.get(i)).doAction(action);
156: }
157: }
158: }
159: if (done) {
160: fireActionDone(action);
161: }
162: doiingDo = false;
163: if (destroy)
164: destroyIn();
165: }
166:
167: /**
168: * Post action on this DebbuggerEngine.
169: * This method does not block till the action is done,
170: * if {@link #canPostAsynchronously} returns true.
171: * Otherwise it behaves like {@link #doAction}.
172: * The returned taks, or
173: * {@link ActionsManagerListener} can be used to
174: * be notified when the action is done.
175: *
176: * @param action action constant (default set of constanct are defined
177: * in this class with ACTION_ prefix)
178: *
179: * @return a task, that can be checked for whether the action finished
180: * or not.
181: *
182: * @since 1.5
183: */
184: public final Task postAction(final Object action) {
185: doiingDo = true;
186: ArrayList l;
187: synchronized (actionProvidersLock) {
188: if (actionProviders == null)
189: initActionImpls();
190: l = (ArrayList) actionProviders.get(action);
191: if (l != null) {
192: l = (ArrayList) l.clone();
193: }
194: }
195: boolean posted = false;
196: int k;
197: if (l != null) {
198: k = l.size();
199: } else {
200: k = 0;
201: }
202: List postedActions = new ArrayList(k);
203: final AsynchActionTask task = new AsynchActionTask(
204: postedActions);
205: if (l != null) {
206: int i;
207: for (i = 0; i < k; i++) {
208: ActionsProvider ap = (ActionsProvider) l.get(i);
209: if (ap.isEnabled(action)) {
210: postedActions.add(ap);
211: posted = true;
212: }
213: }
214: if (posted) {
215: final int[] count = new int[] { 0 };
216: Runnable notifier = new Runnable() {
217: public void run() {
218: synchronized (count) {
219: if (--count[0] == 0) {
220: task.actionDone();
221: fireActionDone(action);
222: doiingDo = false;
223: if (destroy)
224: destroyIn();
225: }
226: }
227: }
228: };
229: count[0] = k = postedActions.size();
230: for (i = 0; i < k; i++) {
231: ((ActionsProvider) postedActions.get(i))
232: .postAction(action, notifier);
233: }
234: }
235: }
236: if (!posted) {
237: doiingDo = false;
238: if (destroy)
239: destroyIn();
240: task.actionDone();
241: }
242: return task;
243: }
244:
245: /**
246: * Returns true if given action can be performed on this DebuggerEngine.
247: *
248: * @param action action constant (default set of constanct are defined
249: * in this class with ACTION_ prefix)
250: * @return true if given action can be performed on this DebuggerEngine
251: */
252: public final boolean isEnabled(final Object action) {
253: ArrayList l;
254: synchronized (actionProvidersLock) {
255: if (actionProviders == null)
256: initActionImpls();
257: l = (ArrayList) actionProviders.get(action);
258: if (l != null) {
259: l = (ArrayList) l.clone();
260: }
261: }
262: if (l != null) {
263: int i, k = l.size();
264: for (i = 0; i < k; i++)
265: if (((ActionsProvider) l.get(i)).isEnabled(action))
266: return true;
267: }
268: return false;
269: }
270:
271: /**
272: * Stops listening on all actions, stops firing events.
273: */
274: public void destroy() {
275: if (!doiingDo)
276: destroyIn();
277: destroy = true;
278: }
279:
280: // ActionsManagerListener support ..........................................
281:
282: /**
283: * Add ActionsManagerListener.
284: *
285: * @param l listener instance
286: */
287: public void addActionsManagerListener(ActionsManagerListener l) {
288: listener.addElement(l);
289: }
290:
291: /**
292: * Removes ActionsManagerListener.
293: *
294: * @param l listener instance
295: */
296: public void removeActionsManagerListener(ActionsManagerListener l) {
297: listener.removeElement(l);
298: }
299:
300: /**
301: * Add ActionsManagerListener.
302: *
303: * @param propertyName a name of property to listen on
304: * @param l the ActionsManagerListener to add
305: */
306: public void addActionsManagerListener(String propertyName,
307: ActionsManagerListener l) {
308: Vector listener = (Vector) listeners.get(propertyName);
309: if (listener == null) {
310: listener = new Vector();
311: listeners.put(propertyName, listener);
312: }
313: listener.addElement(l);
314: }
315:
316: /**
317: * Remove ActionsManagerListener.
318: *
319: * @param propertyName a name of property to listen on
320: * @param l the ActionsManagerListener to remove
321: */
322: public void removeActionsManagerListener(String propertyName,
323: ActionsManagerListener l) {
324: Vector listener = (Vector) listeners.get(propertyName);
325: if (listener == null)
326: return;
327: listener.removeElement(l);
328: if (listener.size() == 0)
329: listeners.remove(propertyName);
330: }
331:
332: // firing support ..........................................................
333:
334: /**
335: * Notifies registered listeners about a change.
336: * Notifies {@link #listener registered listeners} that a breakpoint
337: * {@link DebuggerManagerListener#breakpointRemoved was removed}
338: * and {@link #pcs property change listeners} that its properties
339: * {@link PropertyChangeSupport#firePropertyChange(String, Object, Object)}
340: * were changed.
341: *
342: * @param breakpoint a breakpoint that was removed
343: */
344: private void fireActionDone(final Object action) {
345: initListeners();
346: Vector l = (Vector) listener.clone();
347: Vector l1 = (Vector) listeners
348: .get(ActionsManagerListener.PROP_ACTION_PERFORMED);
349: if (l1 != null)
350: l1 = (Vector) l1.clone();
351: int i, k = l.size();
352: for (i = 0; i < k; i++)
353: ((ActionsManagerListener) l.elementAt(i))
354: .actionPerformed(action);
355: if (l1 != null) {
356: k = l1.size();
357: for (i = 0; i < k; i++)
358: ((ActionsManagerListener) l1.elementAt(i))
359: .actionPerformed(action);
360: }
361: }
362:
363: /**
364: * Notifies registered listeners about a change.
365: * Notifies {@link #listener registered listeners} that a breakpoint
366: * {@link DebuggerManagerListener#breakpointRemoved was removed}
367: * and {@link #pcs property change listeners} that its properties
368: * {@link PropertyChangeSupport#firePropertyChange(String, Object, Object)}
369: * were changed.
370: *
371: * @param breakpoint a breakpoint that was removed
372: */
373: private void fireActionStateChanged(final Object action) {
374: boolean enabled = isEnabled(action);
375: initListeners();
376: Vector l = (Vector) listener.clone();
377: Vector l1 = (Vector) listeners
378: .get(ActionsManagerListener.PROP_ACTION_STATE_CHANGED);
379: if (l1 != null)
380: l1 = (Vector) l1.clone();
381: int i, k = l.size();
382: for (i = 0; i < k; i++)
383: ((ActionsManagerListener) l.elementAt(i))
384: .actionStateChanged(action, enabled);
385: if (l1 != null) {
386: k = l1.size();
387: for (i = 0; i < k; i++)
388: ((ActionsManagerListener) l1.elementAt(i))
389: .actionStateChanged(action, enabled);
390: }
391: }
392:
393: // private support .........................................................
394:
395: private void registerActionsProvider(Object action,
396: ActionsProvider p) {
397: synchronized (actionProvidersLock) {
398: ArrayList l = (ArrayList) actionProviders.get(action);
399: if (l == null) {
400: l = new ArrayList();
401: actionProviders.put(action, l);
402: }
403: l.add(p);
404: }
405: fireActionStateChanged(action);
406: p.addActionsProviderListener(actionListener);
407: }
408:
409: private void initActionImpls() {
410: actionProviders = new HashMap();
411: for (ActionsProvider ap : lookup.lookup(null,
412: ActionsProvider.class)) {
413: Iterator ii = ap.getActions().iterator();
414: while (ii.hasNext())
415: registerActionsProvider(ii.next(), ap);
416: }
417: }
418:
419: private boolean listerersLoaded = false;
420: private List lazyListeners;
421:
422: private void initListeners() {
423: if (listerersLoaded)
424: return;
425: listerersLoaded = true;
426: lazyListeners = lookup.lookup(null,
427: LazyActionsManagerListener.class);
428: int i, k = lazyListeners.size();
429: for (i = 0; i < k; i++) {
430: LazyActionsManagerListener l = (LazyActionsManagerListener) lazyListeners
431: .get(i);
432: String[] props = l.getProperties();
433: if (props == null) {
434: addActionsManagerListener(l);
435: continue;
436: }
437: int j, jj = props.length;
438: for (j = 0; j < jj; j++) {
439: addActionsManagerListener(props[j], l);
440: }
441: }
442: }
443:
444: private synchronized void destroyIn() {
445: if (lazyListeners == null)
446: return;
447: int i, k = lazyListeners.size();
448: for (i = 0; i < k; i++) {
449: LazyActionsManagerListener l = (LazyActionsManagerListener) lazyListeners
450: .get(i);
451: String[] props = l.getProperties();
452: if (props == null) {
453: removeActionsManagerListener(l);
454: continue;
455: }
456: int j, jj = props.length;
457: for (j = 0; j < jj; j++)
458: removeActionsManagerListener(props[j], l);
459: l.destroy();
460: }
461: lazyListeners = new ArrayList();
462: }
463:
464: // innerclasses ............................................................
465:
466: private static class AsynchActionTask extends Task implements
467: Cancellable {
468:
469: private Collection postedActions;
470:
471: public AsynchActionTask(Collection postedActions) {
472: this .postedActions = postedActions;
473: }
474:
475: void actionDone() {
476: notifyFinished();
477: }
478:
479: public boolean cancel() {
480: for (Iterator it = postedActions.iterator(); it.hasNext();) {
481: Object action = it.next();
482: if (action instanceof Cancellable) {
483: if (!((Cancellable) action).cancel()) {
484: return false;
485: }
486: } else {
487: return false;
488: }
489: }
490: return true;
491: }
492: }
493:
494: class MyActionListener implements ActionsProviderListener {
495: public void actionStateChange(Object action, boolean enabled) {
496: fireActionStateChanged(action);
497: }
498: }
499: }
|