/*******************************************************************************
* Copyright (c) 2007, 2008 Edgar Espina.
* All rights reserved. This program and the accompanying materials
* are made available under the terms of the Eclipse Public License v1.0
* which accompanies this distribution, and is available at
* http://www.eclipse.org/legal/epl-v10.html
*
*******************************************************************************/
package org.deved.antlride.debug.model;
import java.io.BufferedReader;
import java.io.BufferedWriter;
import java.io.IOException;
import java.io.InputStreamReader;
import java.io.OutputStream;
import java.io.OutputStreamWriter;
import java.io.PrintWriter;
import java.net.Socket;
import java.net.UnknownHostException;
import java.util.ArrayList;
import java.util.HashMap;
import java.util.List;
import java.util.Map;
import java.util.Vector;
import org.deved.antlride.core.model.IGrammar;
import org.deved.antlride.core.model.IModelElement;
import org.deved.antlride.core.model.ast.ModelElementQuery;
import org.deved.antlride.debug.breakpoints.AntlrBreakpoint;
import org.deved.antlride.debug.breakpoints.AntlrLocationBreakpoint;
import org.deved.antlride.debug.breakpoints.AntlrTokenBreakpoint;
import org.deved.antlride.debug.model.event.AntlrDebugTokenEvent;
import org.deved.antlride.debug.model.event.AntlrDebugLTEvent;
import org.deved.antlride.debug.model.event.AntlrDebugRuleEvent;
import org.deved.antlride.debug.model.event.AntlrDebugEvent;
import org.deved.antlride.debug.model.event.AntlrDebugEventFactory;
import org.deved.antlride.debug.model.event.AntlrDebugEventKind;
import org.deved.antlride.debug.model.event.AntlrDebugLocationEvent;
import org.eclipse.core.resources.IMarkerDelta;
import org.eclipse.core.runtime.CoreException;
import org.eclipse.core.runtime.IProgressMonitor;
import org.eclipse.core.runtime.IStatus;
import org.eclipse.core.runtime.Status;
import org.eclipse.core.runtime.jobs.Job;
import org.eclipse.debug.core.DebugException;
import org.eclipse.debug.core.IBreakpointManager;
import org.eclipse.debug.core.IBreakpointManagerListener;
import org.eclipse.debug.core.ILaunch;
import org.eclipse.debug.core.model.IBreakpoint;
import org.eclipse.debug.core.model.IDebugTarget;
import org.eclipse.debug.core.model.IMemoryBlock;
import org.eclipse.debug.core.model.IProcess;
import org.eclipse.debug.core.model.IStackFrame;
import org.eclipse.debug.core.model.IThread;
public class AntlrDebugTarget extends AntlrDebugElement implements
IDebugTarget, IBreakpointManagerListener {
private ILaunch fLaunch;
private IProcess fProcess;
private IThread[] fThreads;
private AntlrThread fThread;
private boolean fTerminated;
private PrintWriter fRequestWriter;
private Socket fSocket;
private BufferedReader fRequestReader;
private EventDispatchJob fEventDispatch;
private Vector<AntlrDebugEventListener> fEventListeners = new Vector<AntlrDebugEventListener>();
private List<AntlrDebugEvent> fDebugEvents = new Vector<AntlrDebugEvent>();
private IGrammar fGrammar;
private int fDecision;
private int fPort;
public AntlrDebugTarget(IGrammar grammar, ILaunch launch, IProcess process,
int port) throws CoreException {
super(null);
this.fGrammar = grammar;
fLaunch = launch;
fPort = port;
fProcess = process;
// fEventListeners.add(this);
try {
// give interpreter a chance to start
try {
Thread.sleep(1000);
} catch (InterruptedException e) {
}
fSocket = new Socket("localhost", port);
fSocket.setTcpNoDelay(true);
fRequestWriter = createWriter(fSocket);
fRequestReader = createReader(fSocket);
} catch (UnknownHostException e) {
requestFailed("Unable to connect to ANTLR Debugger", e);
} catch (IOException e) {
requestFailed("Unable to connect to ANTLR Debugger", e);
}
fThread = new AntlrThread(this);
fThreads = new IThread[] { fThread };
fEventDispatch = new EventDispatchJob();
fEventDispatch.schedule();
IBreakpointManager breakpointManager = getBreakpointManager();
breakpointManager.addBreakpointListener(this);
breakpointManager.addBreakpointManagerListener(this);
}
private PrintWriter createWriter(Socket socket) throws IOException {
OutputStream os = socket.getOutputStream();
OutputStreamWriter osw = new OutputStreamWriter(os, "UTF8");
return new PrintWriter(new BufferedWriter(osw));
}
private BufferedReader createReader(Socket socket) throws IOException {
return new BufferedReader(new InputStreamReader(
socket.getInputStream(), "UTF8"));
}
public String getName() throws DebugException {
return "ANTLR";// $-NON-NLS-1$;
}
private void started() {
fireCreationEvent();
installDeferredBreakpoints();
// try {
// resume();
// } catch (DebugException e) {
// }
}
private IBreakpoint[] getBreakpoints() {
return getBreakpointManager().getBreakpoints(getModelIdentifier());
}
private void installDeferredBreakpoints() {
IBreakpoint[] breakpoints = getBreakpoints();
for (int i = 0; i < breakpoints.length; i++) {
breakpointAdded(breakpoints[i]);
}
}
public boolean hasBreakpointsEnabled() {
IBreakpoint[] breakpoints = getBreakpoints();
for (IBreakpoint breakpoint : breakpoints) {
try {
if (breakpoint.isEnabled()) {
return true;
}
} catch (Exception ex) {
}
}
return false;
}
public boolean addEventListener(AntlrDebugEventListener listener) {
if (!fEventListeners.contains(listener)) {
fEventListeners.add(listener);
return true;
}
return false;
}
public IProcess getProcess() {
return fProcess;
}
public synchronized IThread getThread() {
return fThread;
}
public AntlrDebugEvent getEvent(int index) {
AntlrDebugEvent event = fDebugEvents.get(index);
if(event == null) {
// System.out.println("NULL at " + index + " " + getEventCount());
event = fDebugEvents.get(index);
}
return event;
}
public int getEventCount() {
return fDebugEvents.size();
}
public AntlrDebugEvent[] getEvents() {
return fDebugEvents.toArray(new AntlrDebugEvent[fDebugEvents.size()]);
}
public List<AntlrDebugEvent> getEventList() {
return new ArrayList<AntlrDebugEvent>(fDebugEvents);
}
public IThread[] getThreads() throws DebugException {
return fThreads;
}
public int getPort() {
return fPort;
}
public boolean hasThreads() throws DebugException {
return true;
}
public boolean supportsBreakpoint(IBreakpoint breakpoint) {
return breakpoint instanceof AntlrBreakpoint;
}
public ILaunch getLaunch() {
return fLaunch;
}
public IGrammar getGrammar() {
return fGrammar;
}
public boolean canTerminate() {
return getProcess().canTerminate();
}
public boolean isTerminated() {
return fTerminated || getProcess().isTerminated();
}
public void terminate() throws DebugException {
terminated();
}
public boolean canResume() {
return !isTerminated() && isSuspended();
}
public boolean canSuspend() {
return !isTerminated() && !isSuspended();
}
public boolean isSuspended() {
return !isTerminated()
&& (getThread() == null || getThread().isSuspended());
}
public void resume() throws DebugException {
getThread().resume();
}
public void removeEventListener(AntlrDebugEventListener listener) {
fEventListeners.remove(listener);
}
public void ack() throws DebugException {
fRequestWriter.println("ack");
fRequestWriter.flush();
// System.out.println("sending signal!!");
}
public void suspend() throws DebugException {
}
@Override
public AntlrDebugTarget getDebugTarget() {
return this;
}
public void breakpointAdded(IBreakpoint breakpoint) {
if (supportsBreakpoint(breakpoint)) {
try {
if (isBreakpointEnabled(breakpoint)
|| !breakpoint.isRegistered()) {
AntlrLocationBreakpoint antlrBreakpoint = (AntlrLocationBreakpoint) breakpoint;
antlrBreakpoint.install(this);
}
} catch (CoreException e) {
}
}
}
public void breakpointChanged(IBreakpoint breakpoint, IMarkerDelta delta) {
if (supportsBreakpoint(breakpoint)) {
if (isBreakpointEnabled(breakpoint)) {
breakpointAdded(breakpoint);
} else {
breakpointRemoved(breakpoint, null);
}
}
}
public void breakpointRemoved(IBreakpoint breakpoint, IMarkerDelta delta) {
if (supportsBreakpoint(breakpoint)) {
AntlrBreakpoint antlrBreakpoint = (AntlrBreakpoint) breakpoint;
antlrBreakpoint.remove();
}
}
public boolean canDisconnect() {
return false;
}
public void disconnect() throws DebugException {
}
public boolean isDisconnected() {
return false;
}
public IMemoryBlock getMemoryBlock(long startAddress, long length)
throws DebugException {
return null;
}
public boolean supportsStorageRetrieval() {
return false;
}
public void breakpointManagerEnablementChanged(boolean enabled) {
IBreakpoint[] breakpoints = getBreakpointManager().getBreakpoints(
getModelIdentifier());
for (int i = 0; i < breakpoints.length; i++) {
if (enabled) {
breakpointAdded(breakpoints[i]);
} else {
breakpointRemoved(breakpoints[i], null);
}
}
}
private boolean isBreakpointEnabled(IBreakpoint breakpoint) {
try {
return breakpoint.isEnabled() && getBreakpointManager().isEnabled();
} catch (CoreException e) {
return false;
}
}
private void closeReader() {
try {
fRequestReader.close();
} catch (IOException e) {
e.printStackTrace();
}
fRequestReader = null;
}
private void closeWriter() {
fRequestWriter.close();
fRequestWriter = null;
}
private void closeSocket() {
try {
fSocket.close();
} catch (IOException e) {
e.printStackTrace();
}
fSocket = null;
}
private synchronized void terminated() {
if (!isTerminated()) {
try {
fProcess.terminate();
} catch (DebugException e) {
e.printStackTrace();
}
closeReader();
closeWriter();
closeSocket();
fTerminated = true;
fThread = null;
fThreads = new IThread[0];
fEventListeners.clear();
// fEventListeners = null;
// fGrammar = null;
IBreakpointManager breakpointManager = getBreakpointManager();
breakpointManager.removeBreakpointListener(this);
breakpointManager.removeBreakpointManagerListener(this);
fireTerminateEvent();
}
}
class EventDispatchJob extends Job {
public EventDispatchJob() {
super("ANTLR Event Dispatch");
setSystem(true);
}
protected IStatus run(IProgressMonitor monitor) {
String event = "";
String lastEvent = "";
int eventCount = 0;
started();
while (!isTerminated() && event != null) {
try {
event = fRequestReader.readLine();
if (event != null) {
if (event.equals(lastEvent)) {
ack();
} else {
boolean handle = false;
AntlrDebugEvent debugEvent = createDebugEvent(event);
fDebugEvents.add(debugEvent);
preHandleEvent(debugEvent);
// if(debugEvent.getEventKind() == AntlrDebugEventKind.EXCEPTION) {
// System.out.println(event);
// }
for (int i = 0; i < fEventListeners.size(); i++) {
AntlrDebugEventListener listener = fEventListeners
.get(i);
boolean h = listener.handleEvent(debugEvent);
if (!handle)
handle = h;
}
if (!handle && eventCount > 0) {
/**
* eventCount > 0. eat the first two events.
* this events don't require ack
*/
postHandleEvent(debugEvent);
}
lastEvent = event;
}
}
eventCount++;
} catch (Exception e) {
e.printStackTrace();
if (!isTerminated()) {
terminated();
}
}
}
return Status.OK_STATUS;
}
}
private AntlrDebugEvent createDebugEvent(String event) {
return AntlrDebugEventFactory.createFromString(this, event);
}
private Map<String, Object> createStackFrameContext(int stackFrameId,
String ruleName, AntlrDebugLocationEvent locationEvent) {
return createStackFrameContext(stackFrameId, ruleName, locationEvent
.getLine(), locationEvent.getColumn());
}
private Map<String, Object> createStackFrameContext(int stackFrameId,
String ruleName, int line, int column) {
Map<String, Object> context = new HashMap<String, Object>();
context.put(AntlrStackFrame.ATTR_GRAMMAR_FILE, fGrammar.getFile().removeFirstSegments(1).toString());
context.put(AntlrStackFrame.ATTR_ID, stackFrameId);
context.put(AntlrStackFrame.ATTR_LINE_NUMBER, line);
context.put(AntlrStackFrame.ATTR_COLUMN_NUMBER, column);
context.put(AntlrStackFrame.ATTR_STACK_FRAME, ruleName);
return context;
}
private List<AntlrDebugEvent> getRuleInvocationStack(
boolean isTokenBreakpoint) {
AntlrDebugEvent[] events = getEvents();
List<AntlrDebugEvent> eRules = new ArrayList<AntlrDebugEvent>();
for (int i = 0; i < events.length; i++) {
AntlrDebugEvent e = events[i];
if (e.getEventKind() == AntlrDebugEventKind.ENTER_RULE) {
eRules.add(0, e);
} else if (e.getEventKind() == AntlrDebugEventKind.EXIT_RULE) {
eRules.remove(0);
} else if (isTokenBreakpoint && i == events.length - 1) {
AntlrDebugEventKind kind = events[i].getEventKind();
if (kind == AntlrDebugEventKind.CONSUME_TOKEN
|| kind == AntlrDebugEventKind.LT) {
eRules.add(0, e);
}
// events[len - 1] consumeToken
// events[len - 2] LT
// events[len - 3] location
}
}
List<AntlrDebugEvent> stack = new ArrayList<AntlrDebugEvent>();
int rIndex = 0;
AntlrDebugEvent de = null;
AntlrDebugEvent location = null;
for (int i = 0; i < eRules.size() - 1; i++) {
de = eRules.get(i);
rIndex = indexOf(events, de);
int offset = 1;
int j = rIndex - 1;
while (j >= 0
&& events[j].getEventKind() != AntlrDebugEventKind.LOCATION) {
j--;
offset++;
}
location = events[rIndex - offset];
stack.add(location);
stack.add(de);
}
// report the first rule declaration
de = eRules.get(eRules.size() - 1);
rIndex = indexOf(events, de);
location = events[rIndex + 1];
stack.add(location);
stack.add(de);
eRules.clear();
eRules = null;
events = null;
return stack;
}
private int indexOf(AntlrDebugEvent[] events, AntlrDebugEvent e) {
for (int i = 0; i < events.length; i++) {
if (events[i].equals(e)) {
return i;
}
}
return -1;
}
public IStackFrame[] getStackFrames() {
List<IStackFrame> stackFrameList = new ArrayList<IStackFrame>();
IBreakpoint[] breakpoints = fThread.getBreakpoints();
AntlrLocationBreakpoint locationBreakpoint = null;
boolean isTokenBreakpoint = false;
if (breakpoints.length > 0) {
locationBreakpoint = (AntlrLocationBreakpoint) breakpoints[0];
isTokenBreakpoint = locationBreakpoint instanceof AntlrTokenBreakpoint;
}
List<AntlrDebugEvent> stack = getRuleInvocationStack(isTokenBreakpoint);
Map<String, Object> stackFrameContext = null;
for (int i = 0; i < stack.size();) {
AntlrDebugLocationEvent locationEvent = (AntlrDebugLocationEvent) stack
.get(i++);
String elementName;
AntlrDebugEvent e = stack.get(i++);
if (e.getEventKind() == AntlrDebugEventKind.ENTER_RULE) {
elementName = ((AntlrDebugRuleEvent) e).getRule();
} else if (e.getEventKind() == AntlrDebugEventKind.CONSUME_TOKEN) {
// Token
elementName = ((AntlrDebugTokenEvent) e).getTokenName();
} else {
elementName = ((AntlrDebugLTEvent) e).getTokenName();
}
stackFrameContext = createStackFrameContext(stackFrameList.size(),
elementName, locationEvent);
stackFrameList.add(new AntlrStackFrame(fThread, stackFrameContext));
}
if (!isTokenBreakpoint) {
int line = -1;
int column = -1;
try {
line = locationBreakpoint.getLineNumber();
column = locationBreakpoint.getColumnNumber();
} catch (CoreException ex) {
}
// add the element at breakpoint location
IModelElement elementAt = ModelElementQuery.getElementAt(fGrammar,
line, column);
stackFrameContext = createStackFrameContext(stackFrameList.size(),
elementAt.getElementName(), line, column);
stackFrameList.add(0, new AntlrStackFrame(fThread,
stackFrameContext));
}
IStackFrame[] stackFrames = stackFrameList
.toArray(new IStackFrame[stackFrameList.size()]);
stackFrameList.clear();
stackFrameList = null;
return stackFrames;
}
public boolean isInDecision() {
return fDecision != 0;
}
private void preHandleEvent(AntlrDebugEvent event) {
// LT: ?-1 index-2 type-3 channel-4 line-5 column-6 text-7
// ex: exception index line column
switch (event.getEventKind()) {
case ENTER_DECISION:
fDecision++;
break;
case EXIT_DECISION:
fDecision--;
break;
}
}
private void postHandleEvent(AntlrDebugEvent event) {
// LT: ?-1 index-2 type-3 channel-4 line-5 column-6 text-7
// ex: exception index line column
switch (event.getEventKind()) {
case TERMINATE: {
terminated();
}
break;
default: {
try {
ack();
} catch (DebugException ex) {
ex.printStackTrace();
}
}
}
}
@Override
public String toString() {
StringBuilder builder = new StringBuilder();
if (isTerminated()) {
builder.append("<terminated>");
}
builder.append("ANTLR Debugger at localhost: ");
builder.append(fPort);
return builder.toString();
}
}