// Copyright 2012 Google Inc. All Rights Reserved.
//
// Licensed under the Apache License, Version 2.0 (the "License");
// you may not use this file except in compliance with the License.
// You may obtain a copy of the License at
//
// http://www.apache.org/licenses/LICENSE-2.0
//
// Unless required by applicable law or agreed to in writing, software
// distributed under the License is distributed on an "AS IS" BASIS,
// WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
// See the License for the specific language governing permissions and
// limitations under the License.
package com.google.collide.client.code.debugging;
import com.google.collide.client.util.logging.Log;
import com.google.collide.json.shared.JsonArray;
import com.google.collide.shared.util.JsonCollections;
/**
* The model for the debugging info, such as breakpoints and etc.
*/
public class DebuggingModel {
/**
* Callback interface for getting notified about changes to the debugging
* model that have been applied by a controller.
*/
public interface DebuggingModelChangeListener {
/**
* Notification that a breakpoint was added.
*/
void onBreakpointAdded(Breakpoint newBreakpoint);
/**
* Notification that a breakpoint was removed.
*/
void onBreakpointRemoved(Breakpoint oldBreakpoint);
/**
* Notification that a breakpoint was replaced.
*/
void onBreakpointReplaced(Breakpoint oldBreakpoint, Breakpoint newBreakpoint);
/**
* Notification that the Pause-On-Exceptions mode was changed.
*/
void onPauseOnExceptionsModeUpdated(PauseOnExceptionsMode oldMode,
PauseOnExceptionsMode newMode);
/**
* Notification that the breakpoints-enabled flag was changed.
*/
void onBreakpointsEnabledUpdated(boolean newValue);
}
private interface ChangeDispatcher {
void dispatch(DebuggingModelChangeListener changeListener);
}
/**
* Pause-On-Exceptions modes. Tells the debugger what to do if an exception is fired.
*/
public enum PauseOnExceptionsMode {
NONE, ALL, UNCAUGHT
}
private final JsonArray<DebuggingModelChangeListener> modelChangeListeners =
JsonCollections.createArray();
private final JsonArray<Breakpoint> breakpoints = JsonCollections.createArray();
private PauseOnExceptionsMode pauseOnExceptionsMode = PauseOnExceptionsMode.NONE;
private boolean breakpointsEnabled = true;
public DebuggingModel() {
Log.debug(getClass(), "Creating DebuggingModel.");
}
/**
* Adds a {@link DebuggingModelChangeListener} to be notified of mutations applied
* by a controller to the underlying debugging model.
*
* @param modelChangeListener the listener we are adding
*/
public void addModelChangeListener(DebuggingModelChangeListener modelChangeListener) {
modelChangeListeners.add(modelChangeListener);
}
/**
* Removes a {@link DebuggingModelChangeListener} from the set of listeners
* subscribed to model changes.
*
* @param modelChangeListener the listener we are removing
*/
public void removeModelChangeListener(DebuggingModelChangeListener modelChangeListener) {
modelChangeListeners.remove(modelChangeListener);
}
/**
* Adds a breakpoint to the debugging model.
*/
public void addBreakpoint(final Breakpoint breakpoint) {
Log.debug(getClass(), "Adding " + breakpoint);
if (!breakpoints.contains(breakpoint)) {
breakpoints.add(breakpoint);
dispatchModelChange(new ChangeDispatcher() {
@Override
public void dispatch(DebuggingModelChangeListener changeListener) {
changeListener.onBreakpointAdded(breakpoint);
}
});
}
}
/**
* Removes a breakpoint from the debugging model.
*/
public void removeBreakpoint(final Breakpoint breakpoint) {
Log.debug(getClass(), "Removing " + breakpoint);
if (breakpoints.remove(breakpoint)) {
dispatchModelChange(new ChangeDispatcher() {
@Override
public void dispatch(DebuggingModelChangeListener changeListener) {
changeListener.onBreakpointRemoved(breakpoint);
}
});
}
}
/**
* Updates a breakpoint from the debugging model.
*/
public void updateBreakpoint(final Breakpoint oldBreakpoint, final Breakpoint newBreakpoint) {
Log.debug(getClass(), "Updating " + oldBreakpoint + " - to - " + newBreakpoint);
if (oldBreakpoint.equals(newBreakpoint)) {
return; // Nothing to do.
}
if (breakpoints.contains(oldBreakpoint)) {
if (breakpoints.contains(newBreakpoint)) {
removeBreakpoint(oldBreakpoint);
return;
}
breakpoints.remove(oldBreakpoint);
breakpoints.add(newBreakpoint);
dispatchModelChange(new ChangeDispatcher() {
@Override
public void dispatch(DebuggingModelChangeListener changeListener) {
changeListener.onBreakpointReplaced(oldBreakpoint, newBreakpoint);
}
});
}
}
/**
* Sets the Pause-On-Exceptions mode.
*/
public void setPauseOnExceptionsMode(PauseOnExceptionsMode mode) {
Log.debug(getClass(), "Setting pause-on-exceptions " + mode);
if (!pauseOnExceptionsMode.equals(mode)) {
final PauseOnExceptionsMode oldMode = pauseOnExceptionsMode;
final PauseOnExceptionsMode newMode = mode;
pauseOnExceptionsMode = mode;
dispatchModelChange(new ChangeDispatcher() {
@Override
public void dispatch(DebuggingModelChangeListener changeListener) {
changeListener.onPauseOnExceptionsModeUpdated(oldMode, newMode);
}
});
}
}
/**
* Enables or disables all breakpoints.
*/
public void setBreakpointsEnabled(boolean value) {
Log.debug(getClass(), "Setting enable-breakpoints to " + value);
if (breakpointsEnabled != value) {
breakpointsEnabled = value;
dispatchModelChange(new ChangeDispatcher() {
@Override
public void dispatch(DebuggingModelChangeListener changeListener) {
changeListener.onBreakpointsEnabledUpdated(breakpointsEnabled);
}
});
}
}
/**
* @return copy of all breakpoints in the given workspace
*/
public JsonArray<Breakpoint> getBreakpoints() {
return breakpoints.copy();
}
public int getBreakpointCount() {
return breakpoints.size();
}
public PauseOnExceptionsMode getPauseOnExceptionsMode() {
return pauseOnExceptionsMode;
}
public boolean isBreakpointsEnabled() {
return breakpointsEnabled;
}
private void dispatchModelChange(ChangeDispatcher dispatcher) {
JsonArray<DebuggingModelChangeListener> copy = modelChangeListeners.copy();
for (int i = 0, n = copy.size(); i < n; i++) {
dispatcher.dispatch(copy.get(i));
}
}
}