blob: 4c997ac0ef41f300fb1006f04568d44d40fed1ea [file] [log] [blame]
/*
* Copyright 2007 Sascha Weinreuter
*
* 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 org.intellij.plugins.xsltDebugger.rt.engine.local;
import org.intellij.plugins.xsltDebugger.rt.engine.*;
import org.intellij.plugins.xsltDebugger.rt.engine.local.saxon.SaxonSupport;
import org.intellij.plugins.xsltDebugger.rt.engine.local.saxon9.Saxon9Support;
import org.intellij.plugins.xsltDebugger.rt.engine.local.xalan.XalanSupport;
import javax.xml.transform.Result;
import javax.xml.transform.Source;
import javax.xml.transform.Transformer;
import javax.xml.transform.TransformerException;
import java.util.Iterator;
import java.util.LinkedList;
import java.util.List;
/**
* Created by IntelliJ IDEA.
* User: sweinreuter
* Date: 19.05.2007
*/
public class LocalDebugger implements Debugger {
private final Thread myThread;
private final BreakpointManager myBreakpointManager;
private final OutputEventQueueImpl myEventQueue;
private volatile Condition myCurrentStopCondition;
private final Object theLock = new Object();
private volatile State myState = State.CREATED;
private final LinkedList<StyleFrame> myFrames = new LinkedList<StyleFrame>();
private final LinkedList<SourceFrame> mySourceFrames = new LinkedList<SourceFrame>();
public LocalDebugger(final Transformer transformer, final Source source, final Result result) {
prepareTransformer(transformer);
myBreakpointManager = new BreakpointManagerImpl();
myEventQueue = new OutputEventQueueImpl(this);
myThread = new Thread(new Runnable() {
public void run() {
try {
synchronized (theLock) {
myState = State.RUNNING;
theLock.notifyAll();
}
transformer.transform(source, result);
stopped();
} catch (DebuggerStoppedException e) {
// OK
} catch (TransformerException e) {
// TODO: log, pass to client
e.printStackTrace();
stopped();
}
}
});
}
protected void prepareTransformer(Transformer transformer) {
try {
if (Saxon9Support.init(transformer, this)) {
return;
}
} catch (NoClassDefFoundError e1) {
// ignore
}
try {
if (SaxonSupport.init(transformer, this)) {
return;
}
} catch (NoClassDefFoundError e) {
// ignore
}
try {
if (XalanSupport.init(transformer, this)) {
return;
}
} catch (NoClassDefFoundError e1) {
// ignore
}
throw new UnsupportedOperationException("Unsupported Transformer: " + transformer.getClass().getName());
}
private void suspendAndWait() throws DebuggerStoppedException {
try {
synchronized (theLock) {
myCurrentStopCondition = null;
myState = State.SUSPENDED;
theLock.notifyAll();
do {
theLock.wait();
}
while (myState == State.SUSPENDED);
if (myState == State.STOPPED) {
throw new DebuggerStoppedException();
}
}
} catch (InterruptedException e) {
throw new DebuggerStoppedException();
}
}
public void resume() throws DebuggerStoppedException {
synchronized (theLock) {
if (myState == State.STOPPED) {
throw new DebuggerStoppedException();
} else if (myState != State.SUSPENDED) {
throw new IllegalStateException();
}
myState = State.RUNNING;
theLock.notifyAll();
}
}
public void pause() {
synchronized (theLock) {
if (myState == State.STOPPED) {
throw new DebuggerStoppedException();
} else if (myState != State.RUNNING) {
throw new IllegalStateException();
}
myCurrentStopCondition = Condition.TRUE;
}
}
public void stopped() {
assert Thread.currentThread() == myThread;
stop0();
}
private void stop0() {
synchronized (theLock) {
myState = State.STOPPED;
theLock.notifyAll();
}
}
public State getState() {
synchronized (theLock) {
return myState;
}
}
@SuppressWarnings({ "deprecation" })
public void stop(boolean force) {
stop0();
myThread.interrupt();
if (!force) {
return;
}
try {
myThread.join(1000);
if (myThread.isAlive()) {
myThread.stop();
}
} catch (InterruptedException e) {
//
}
}
public State waitForStateChange(State state) {
try {
synchronized (theLock) {
if (myState == State.STOPPED) {
return State.STOPPED;
}
while (myState == state) {
theLock.wait();
}
return myState;
}
} catch (InterruptedException e) {
return null;
}
}
public boolean waitForDebuggee() {
try {
synchronized (theLock) {
while (myState == State.RUNNING) {
theLock.wait();
}
return myState != State.STOPPED;
}
} catch (InterruptedException e) {
return false;
}
}
public boolean isStopped() {
synchronized (theLock) {
return myState == State.STOPPED;
}
}
public boolean start() {
assert myState == State.CREATED : "Already started";
myThread.start();
try {
synchronized (theLock) {
while (myState == State.CREATED) {
theLock.wait();
}
}
return true;
} catch (InterruptedException e) {
//
}
return false;
}
public void enter(StyleFrame frame) {
assert Thread.currentThread() == myThread;
myFrames.addFirst(frame);
final String uri = frame.getURI();
final StyleFrame previous = frame.getPrevious();
//if (previous != null && previous.getLineNumber() == frame.getLineNumber() && uri != null && uri.equals(previous.getURI())) {
// if (frame.getInstruction().equals(previous.getInstruction())) {
// System.err.println(
// "WARN: Same instruction <" + frame.getInstruction() + "> on line " + frame.getLineNumber() + " encountered more than once");
// }
//}
if (isStopped()) {
throw new DebuggerStoppedException();
} else if (myCurrentStopCondition != null && myCurrentStopCondition.value()) {
suspendAndWait();
} else {
final int lineNumber = frame.getLineNumber();
final Breakpoint breakpoint = myBreakpointManager.getBreakpoint(uri, lineNumber);
if (breakpoint != null && breakpoint.isEnabled()) {
// do not evaluate a log or condition bp more than once on the same line
if (previous == null || previous.getLineNumber() != lineNumber) {
final String condition = breakpoint.getCondition();
try {
if (evalCondition(condition)) {
final String logMessage = breakpoint.getLogMessage();
final String traceMessage = breakpoint.getTraceMessage();
if (logBreakpoint(frame, logMessage, traceMessage) || breakpoint.isSuspend()) {
suspendAndWait();
}
}
} catch (EvaluationException e) {
// TODO: send to IDEA
System.err.println("[" + lineNumber + "]: Failed to evaluate expression: " + condition + " -- " + e.getMessage());
breakpoint.setEnabled(false);
}
}
}
}
}
private boolean evalCondition(String condition) throws EvaluationException {
if (condition != null && condition.length() > 0) {
if (!"true".equals(eval("boolean(" + condition + ")").getValue().toString())) {
return false;
}
}
return true;
}
private boolean logBreakpoint(StyleFrame frame, String logMessage, String traceMessage) throws EvaluationException {
if (logMessage != null) {
final String uri = frame.getURI();
final String pos = uri.substring(uri.lastIndexOf('/') + 1) + ":" + frame.getLineNumber();
System.out.println("[" + pos + "]: " + (logMessage.length() > 0 ? eval(logMessage).getValue().toString() : "<no message>"));
if (traceMessage != null) {
myEventQueue.trace(makeTraceMessage(traceMessage));
}
return false;
} else if (traceMessage != null) {
myEventQueue.trace(makeTraceMessage(traceMessage));
return false;
}
return true;
}
private String makeTraceMessage(String traceMessage) throws EvaluationException {
if (traceMessage.length() > 0) {
return eval(traceMessage).getValue().toString();
} else {
return null;
}
}
public void leave() {
assert Thread.currentThread() == myThread;
if (isStopped()) {
throw new DebuggerStoppedException();
// } else if (myBreakpointManager.isBreakpoint(uri, lineNumber)) {
// suspendAndWait();
// } else if (myCurrentStopCondition != null && myCurrentStopCondition.value()) {
// suspendAndWait();
}
((AbstractFrame)myFrames.removeFirst()).invalidate();
}
public void step() {
final int targetSize = myFrames.size();
myCurrentStopCondition = new Condition() {
public boolean value() {
return myFrames.size() <= targetSize;
}
};
resume();
}
public void stepInto() {
myCurrentStopCondition = Condition.TRUE;
resume();
}
public org.intellij.plugins.xsltDebugger.rt.engine.Value eval(String expr) throws EvaluationException {
final StyleFrame frame = getCurrentFrame();
if (frame == null) {
throw new EvaluationException("No frame available");
}
return frame.eval(expr);
}
public StyleFrame getCurrentFrame() {
return myFrames.size() > 0 ? myFrames.getFirst() : null;
}
public SourceFrame getSourceFrame() {
return mySourceFrames.size() > 0 ? mySourceFrames.getFirst() : null;
}
public List<Debugger.Variable> getGlobalVariables() {
final List<Variable> vars = getCurrentFrame().getVariables();
for (Iterator<Variable> it = vars.iterator(); it.hasNext(); ) {
Variable var = it.next();
if (!var.isGlobal()) {
it.remove();
}
}
return vars;
}
public BreakpointManager getBreakpointManager() {
return myBreakpointManager;
}
public void pushSource(SourceFrame sourceFrame) {
mySourceFrames.addFirst(sourceFrame);
}
public void popSource() {
((AbstractFrame)mySourceFrames.removeFirst()).invalidate();
}
interface Condition {
Condition TRUE = new Condition() {
public boolean value() {
return true;
}
};
boolean value();
}
public OutputEventQueueImpl getEventQueue() {
return myEventQueue;
}
public boolean ping() {
return true;
}
}