diff --git a/.idea/modules.xml b/.idea/modules.xml index 19e68530442f..1c82aaa53ea9 100644 --- a/.idea/modules.xml +++ b/.idea/modules.xml @@ -100,6 +100,8 @@ + + diff --git a/community-main.iml b/community-main.iml index e3109b8263bf..4b5a9e3fa21d 100644 --- a/community-main.iml +++ b/community-main.iml @@ -77,6 +77,7 @@ + diff --git a/plugins/xslt-debugger/build.properties b/plugins/xslt-debugger/build.properties new file mode 100644 index 000000000000..ea2fd1e2e723 --- /dev/null +++ b/plugins/xslt-debugger/build.properties @@ -0,0 +1,8 @@ +#idea.home=C:\\Programme\\IntelliJ Selena +idea.home=C:\\work\\Programme\\IntelliJ 10 +idea.plugins.home=${idea.home}/plugins + +xpathview.src.home=../xpath + +compile.debug=true +compile.optimize=true diff --git a/plugins/xslt-debugger/build.xml b/plugins/xslt-debugger/build.xml new file mode 100644 index 000000000000..e0b869c3b3f9 --- /dev/null +++ b/plugins/xslt-debugger/build.xml @@ -0,0 +1,198 @@ + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + The XPathView plugin is required to build the XSLT-Debugger. Please set the property "xpathview.plugin.home" to + point to the installation of the plugin in IntelliJ IDEA. + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + \ No newline at end of file diff --git a/plugins/xslt-debugger/rt/lib/rmi-stubs.jar b/plugins/xslt-debugger/rt/lib/rmi-stubs.jar new file mode 100644 index 000000000000..cb5ad8a8f43d Binary files /dev/null and b/plugins/xslt-debugger/rt/lib/rmi-stubs.jar differ diff --git a/plugins/xslt-debugger/rt/lib/saxon-conditions.html b/plugins/xslt-debugger/rt/lib/saxon-conditions.html new file mode 100644 index 000000000000..cd33c4268958 --- /dev/null +++ b/plugins/xslt-debugger/rt/lib/saxon-conditions.html @@ -0,0 +1,148 @@ + + + + SAXON: Conditions of Use + + + + + +

SAXON: Conditions of Use

+ + +

Saxon

+ +

The contents of the downloaded file (saxon.zip), with the exception of the + Ælfred parser (see below), + are subject to the Mozilla Public License Version 1.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.mozilla.org/MPL/

+ +

Software distributed under the License is distributed on an "AS IS" basis, + WITHOUT WARRANTY OF ANY KIND, either express or implied. + See the License for the specific language governing rights and limitations under the License. + +

+ +

The Original Code of SAXON comprises all those components which are not explicitly attributed + to other parties. It does not include the Ælfred parser, which is redistributed under + the licence described below.

+ +

The Initial Developer of the Original Code was Michael Kay. + Individual modules identified as being created by James Clark, David Megginson, or John Cowan include + separate IPR notices. All Rights Reserved.

+ +

+ Until January 2001 Michael Kay worked for International Computers Limited (ICL, now part + of Fujitsu), and from February 2001 to January 2004 he worked for Software AG. These two companies + both sponsored the development of Saxon and authorised its release under this license, but are not + themselves parties to the license. Michael Kay now runs his own company, Saxonica, which has taken + over all responsibility for Saxon development and support. +

+ +

+ For reasons of continuity, + the icl.com domain name is retained in package names and namespace URIs with the tacit + consent of ICL, but this should not be taken to imply any ongoing ICL involvement. +

+ +

If you produce a product that includes or requires SAXON, please refer to it as + "The SAXON XSLT Processor from Michael Kay", and include the URL of the + home page, which is at + http://saxon.sourceforge.net/.

+ +

There is no guarantee of technical support, though I am usually able to answer enquiries + within a few days. Please subscribe to the mailing list available at + + http://lists.sourceforge.net/lists/listinfo/saxon-help and raise any enquiries there. + Also check the Saxon project pages on sourceforge for details of known errors; all bugs are + listed there as soon as I have sufficient evidence to describe the nature of the problem.

+
+ +

Ælfred

+ +

Included in the Saxon distribution is a modified version of the Ælfred XML parser + originally developed by David Megginson at Microstar (no longer in existence). + I have taken the relevant part of + David Brownell's + Ælfred2 distribution, repackaged it, + and fixed a few bugs. All changes are clearly documented in the source code.

+ +

This is distributed under the terms of Microstar's terms and conditions, which are as follows:

+ + + + + + + +
+

AElfred, Version 1.2
+ Microstar's Java-Based XML Parser
+ Copyright (c) 1997, 1998 by Microstar Software Ltd.
+ Home Page: http://www.microstar.com/XML/

+ +

AElfred is free for both commercial and non-commercial use and + redistribution, provided that Microstar's copyright and disclaimer are + retained intact. You are free to modify AElfred for your own use and + to redistribute AElfred with your modifications, provided that the + modifications are clearly documented.

+ + +

DISCLAIMER

+ + +

This program is distributed in the hope that it will be useful, but + WITHOUT ANY WARRANTY; without even the implied warranty of + merchantability or fitness for a particular purpose. Please use it AT + YOUR OWN RISK.

+ + +
+ +

David Brownell added the following statement:

+
+
+/*
+ * Copyright (c) 1999-2000 by David Brownell.  All Rights Reserved.
+ *
+ * This program is open source software; you may use, copy, modify, and
+ * redistribute it under the terms of the LICENSE with which it was
+ * originally distributed.
+ *
+ * This program is distributed in the hope that it will be useful,
+ * but WITHOUT ANY WARRANTY; without even the implied warranty of
+ * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the
+ * LICENSE for more details.
+ */
+
+
+ +

The version included with Saxon is David Brownell's code of 26 Feb 2000, + modified as follows:

+ +
    +
  • Changed the package name to com.icl.saxon.aelfred to prevent any accidental confusion with the + original Microstar code or with David Brownell's version. +
  • +
  • Fixed several bugs: (1) a bug in namespace handling (where an attribute xxx:name precedes the xmlns:xxx + declaration) (2) poor diagnostics when end of file occurs prematurely +
  • +
  • Subsetting the code to include only the XML non-validating parser and SAX driver, with appropriate + changes to the setFeature() and getFeature() methods. +
  • +
  • Some further changes made by David Brownell up to June 2001 have been incorporated.
  • +
+ +
+ +

Michael H. Kay
+ Saxonica Limited
+ 22 June 2005

+ + +
+ + + diff --git a/plugins/xslt-debugger/rt/lib/saxon.jar b/plugins/xslt-debugger/rt/lib/saxon.jar new file mode 100644 index 000000000000..15aa83a54de6 Binary files /dev/null and b/plugins/xslt-debugger/rt/lib/saxon.jar differ diff --git a/plugins/xslt-debugger/rt/lib/saxon9he.jar b/plugins/xslt-debugger/rt/lib/saxon9he.jar new file mode 100644 index 000000000000..5d86f50a5fb6 Binary files /dev/null and b/plugins/xslt-debugger/rt/lib/saxon9he.jar differ diff --git a/plugins/xslt-debugger/rt/lib/serializer.jar b/plugins/xslt-debugger/rt/lib/serializer.jar new file mode 100644 index 000000000000..99f98db9bfca Binary files /dev/null and b/plugins/xslt-debugger/rt/lib/serializer.jar differ diff --git a/plugins/xslt-debugger/rt/lib/xalan.jar b/plugins/xslt-debugger/rt/lib/xalan.jar new file mode 100644 index 000000000000..458fa73d96e7 Binary files /dev/null and b/plugins/xslt-debugger/rt/lib/xalan.jar differ diff --git a/plugins/xslt-debugger/rt/src/org/intellij/plugins/xsltDebugger/rt/XSLTDebuggerMain.java b/plugins/xslt-debugger/rt/src/org/intellij/plugins/xsltDebugger/rt/XSLTDebuggerMain.java new file mode 100644 index 000000000000..4e578a84b84f --- /dev/null +++ b/plugins/xslt-debugger/rt/src/org/intellij/plugins/xsltDebugger/rt/XSLTDebuggerMain.java @@ -0,0 +1,58 @@ +/* + * Copyright 2002-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; + +import org.intellij.plugins.xslt.run.rt.XSLTMain; +import org.intellij.plugins.xslt.run.rt.XSLTRunner; +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 org.intellij.plugins.xsltDebugger.rt.engine.remote.DebuggerServer; + +import javax.xml.transform.*; +import java.rmi.RemoteException; + + +/* +* Created by IntelliJ IDEA. +* User: sweinreuter +* Date: 23.11.2007 +*/ +public class XSLTDebuggerMain implements XSLTMain { + + public TransformerFactory createTransformerFactory() throws ClassNotFoundException, InstantiationException, IllegalAccessException { + final String type = System.getProperty("xslt.transformer.type"); + if ("xalan".equalsIgnoreCase(type)) { + return XalanSupport.createTransformerFactory(); + } else if ("saxon".equalsIgnoreCase(type)) { + return SaxonSupport.createTransformerFactory(); + } else if ("saxon9".equalsIgnoreCase(type)) { + return Saxon9Support.createTransformerFactory(); + } else if (type != null) { + throw new UnsupportedOperationException("Unsupported Transformer type '" + type + "'"); + } + return XalanSupport.prepareFactory(XSLTRunner.createTransformerFactoryStatic()); + } + + public void start(Transformer transformer, Source source, Result result) throws TransformerException { + try { + DebuggerServer.create(transformer, source, result, Integer.getInteger("xslt.debugger.port")); + } catch (RemoteException e) { + throw new TransformerException(e.getMessage(), e.getCause()); + } + } +} \ No newline at end of file diff --git a/plugins/xslt-debugger/rt/src/org/intellij/plugins/xsltDebugger/rt/engine/Breakpoint.java b/plugins/xslt-debugger/rt/src/org/intellij/plugins/xsltDebugger/rt/engine/Breakpoint.java new file mode 100644 index 000000000000..18d8e1bf61bb --- /dev/null +++ b/plugins/xslt-debugger/rt/src/org/intellij/plugins/xsltDebugger/rt/engine/Breakpoint.java @@ -0,0 +1,48 @@ +/* + * 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; + +/** + * Created by IntelliJ IDEA. + * User: sweinreuter + * Date: 29.05.2007 + */ +public interface Breakpoint { + String getUri(); + + int getLine(); + + boolean isEnabled(); + + void setEnabled(boolean enabled); + + String getCondition(); + + void setCondition(String expr); + + String getLogMessage(); + + void setLogMessage(String expr); + + String getTraceMessage(); + + void setTraceMessage(String expr); + + boolean isSuspend(); + + void setSuspend(boolean suspend); +} diff --git a/plugins/xslt-debugger/rt/src/org/intellij/plugins/xsltDebugger/rt/engine/BreakpointManager.java b/plugins/xslt-debugger/rt/src/org/intellij/plugins/xsltDebugger/rt/engine/BreakpointManager.java new file mode 100644 index 000000000000..ea9b01e0d485 --- /dev/null +++ b/plugins/xslt-debugger/rt/src/org/intellij/plugins/xsltDebugger/rt/engine/BreakpointManager.java @@ -0,0 +1,39 @@ +/* + * 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; + +import java.io.File; +import java.util.List; + +/** + * Created by IntelliJ IDEA. + * User: sweinreuter + * Date: 23.05.2007 + */ +public interface BreakpointManager { + Breakpoint setBreakpoint(File file, int line); + + Breakpoint setBreakpoint(String uri, int line); + + void removeBreakpoint(Breakpoint bp); + + void removeBreakpoint(String uri, int line); + + List getBreakpoints(); + + Breakpoint getBreakpoint(String uri, int lineNumber); +} diff --git a/plugins/xslt-debugger/rt/src/org/intellij/plugins/xsltDebugger/rt/engine/Debugger.java b/plugins/xslt-debugger/rt/src/org/intellij/plugins/xsltDebugger/rt/engine/Debugger.java new file mode 100644 index 000000000000..af12ff3911c1 --- /dev/null +++ b/plugins/xslt-debugger/rt/src/org/intellij/plugins/xsltDebugger/rt/engine/Debugger.java @@ -0,0 +1,103 @@ +/* + * 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; + +import java.util.List; + +/** + * Created by IntelliJ IDEA. + * User: sweinreuter + * Date: 23.05.2007 + */ +public interface Debugger extends Watchable { + enum State { + CREATED, RUNNING, SUSPENDED, STOPPED + } + + State getState(); + + boolean start(); + + void stop(boolean force); + + void step(); + + void stepInto(); + + void resume(); + + void pause(); + + boolean isStopped(); + + StyleFrame getCurrentFrame(); + + SourceFrame getSourceFrame(); + + Value eval(String expr) throws EvaluationException; + + List getGlobalVariables(); + + BreakpointManager getBreakpointManager(); + + OutputEventQueue getEventQueue(); + + boolean waitForDebuggee(); + + State waitForStateChange(State state); + + interface Locatable { + String getURI(); + + int getLineNumber(); + } + + interface Frame extends Locatable { + T getNext(); + + T getPrevious(); + } + + interface StyleFrame extends Frame { + String getInstruction(); + + Value eval(String expr) throws EvaluationException; + + List getVariables(); + } + + interface SourceFrame extends Frame { + String getXPath(); + } + + interface Variable extends Locatable { + enum Kind {VARIABLE, PARAMETER, EXPRESSION} + + boolean isGlobal(); + + Kind getKind(); + + String getName(); + + Value getValue(); + } + + class EvaluationException extends Exception { + public EvaluationException(String message) { + super(message); + } + } +} diff --git a/plugins/xslt-debugger/rt/src/org/intellij/plugins/xsltDebugger/rt/engine/DebuggerStoppedException.java b/plugins/xslt-debugger/rt/src/org/intellij/plugins/xsltDebugger/rt/engine/DebuggerStoppedException.java new file mode 100644 index 000000000000..0b00bdbaa433 --- /dev/null +++ b/plugins/xslt-debugger/rt/src/org/intellij/plugins/xsltDebugger/rt/engine/DebuggerStoppedException.java @@ -0,0 +1,25 @@ +/* + * 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; + +/** + * Created by IntelliJ IDEA. + * User: sweinreuter + * Date: 24.05.2007 + */ +public class DebuggerStoppedException extends RuntimeException { +} diff --git a/plugins/xslt-debugger/rt/src/org/intellij/plugins/xsltDebugger/rt/engine/OutputEventQueue.java b/plugins/xslt-debugger/rt/src/org/intellij/plugins/xsltDebugger/rt/engine/OutputEventQueue.java new file mode 100644 index 000000000000..da03a1c30755 --- /dev/null +++ b/plugins/xslt-debugger/rt/src/org/intellij/plugins/xsltDebugger/rt/engine/OutputEventQueue.java @@ -0,0 +1,116 @@ +/* + * Copyright 2002-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; + +import java.io.Serializable; +import java.util.List; + +/** + * Created by IntelliJ IDEA. + * User: sweinreuter + * Date: 08.06.2007 + */ +public interface OutputEventQueue { + int START_DOCUMENT = 0; + int END_DOCUMENT = 99; + int START_ELEMENT = 1; + int END_ELEMENT = 2; + int ATTRIBUTE = 3; + int CHARACTERS = 4; + int COMMENT = 5; + int PI = 6; + + int TRACE_POINT = 20; + + void setEnabled(boolean b); + + List getEvents(); + + final class NodeEvent implements Serializable { + public static final class QName implements Serializable { + public String myPrefix; + public String myLocalName; + public String myURI; + + public QName(String prefix, String localName, String URI) { + myPrefix = prefix; + myLocalName = localName; + myURI = URI; + } + + public QName(String qName, String uri) { + myURI = uri; + final String[] parts = qName.split(":"); + if (parts.length == 2) { + myPrefix = parts[0]; + myLocalName = parts[1]; + } else { + myPrefix = null; + myLocalName = parts[0]; + } + } + + public QName(String name) { + myLocalName = name; + myPrefix = null; + myURI = null; + } + + public String getQName() { + return myPrefix != null && myPrefix.length() > 0 ? myPrefix + ":" + myLocalName : myLocalName; + } + } + + private final int myType; + + public String myValue; + public QName myQName; + public String myURI; + private int myLineNumber; + + public NodeEvent(int type, QName qName, String value) { + myType = type; + myQName = qName; + myValue = value; + } + + public int getType() { + return myType; + } + + public QName getQName() { + return myQName; + } + + public String getValue() { + return myValue; + } + + public void setLocation(String uri, int lineNumber) { + myURI = uri; + myLineNumber = lineNumber; + } + + public int getLineNumber() { + return myLineNumber; + } + + public String getURI() { + return myURI; + } + } +} diff --git a/plugins/xslt-debugger/rt/src/org/intellij/plugins/xsltDebugger/rt/engine/Value.java b/plugins/xslt-debugger/rt/src/org/intellij/plugins/xsltDebugger/rt/engine/Value.java new file mode 100644 index 000000000000..090146fc766a --- /dev/null +++ b/plugins/xslt-debugger/rt/src/org/intellij/plugins/xsltDebugger/rt/engine/Value.java @@ -0,0 +1,105 @@ +/* + * 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; + +import java.io.Serializable; +import java.util.List; + +/** + * Created by IntelliJ IDEA. + * User: sweinreuter + * Date: 31.05.2007 + */ +public interface Value extends Serializable { + interface Type extends Serializable { + String getName(); + } + + enum XPathType implements Type { + BOOLEAN, NUMBER, STRING, NODESET, OBJECT, UNKNOWN; + + public String getName() { + return name().toLowerCase(); + } + } + + final class ObjectType implements Type { + private final String myName; + + public ObjectType(String name) { + myName = name; + } + + public String getName() { + return myName; + } + } + + Object getValue(); + + Type getType(); + + class NodeSet implements Serializable { + public final String myStringValue; + private final List myNodes; + + public NodeSet(String stringValue, List nodes) { + myStringValue = stringValue; + myNodes = nodes; + } + + public List getNodes() { + return myNodes; + } + + @Override + public String toString() { + return myStringValue; + } + } + + class Node implements Serializable, Debugger.Locatable { + public final String myURI; + public final int myLineNumber; + public final String myXPath; + public final String myStringValue; + + public Node(String URI, int lineNumber, String XPath, String stringValue) { + myURI = URI; + myLineNumber = lineNumber; + myXPath = XPath; + myStringValue = stringValue; + } + + public String getURI() { + return myURI; + } + + public int getLineNumber() { + return myLineNumber; + } + + public String toString() { + return "Node{" + + "myURI='" + myURI + '\'' + + ", myLineNumber=" + myLineNumber + + ", myXPath='" + myXPath + '\'' + + ", myStringValue='" + myStringValue + '\'' + + '}'; + } + } +} diff --git a/plugins/xslt-debugger/rt/src/org/intellij/plugins/xsltDebugger/rt/engine/Watchable.java b/plugins/xslt-debugger/rt/src/org/intellij/plugins/xsltDebugger/rt/engine/Watchable.java new file mode 100644 index 000000000000..9518232dbb0e --- /dev/null +++ b/plugins/xslt-debugger/rt/src/org/intellij/plugins/xsltDebugger/rt/engine/Watchable.java @@ -0,0 +1,25 @@ +/* + * Copyright 2000-2011 JetBrains s.r.o. + * + * 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; + +/* +* Created by IntelliJ IDEA. +* User: sweinreuter +* Date: 09.05.11 +*/ +public interface Watchable { + boolean ping(); +} \ No newline at end of file diff --git a/plugins/xslt-debugger/rt/src/org/intellij/plugins/xsltDebugger/rt/engine/local/AbstractFrame.java b/plugins/xslt-debugger/rt/src/org/intellij/plugins/xsltDebugger/rt/engine/local/AbstractFrame.java new file mode 100644 index 000000000000..87bd44fd373d --- /dev/null +++ b/plugins/xslt-debugger/rt/src/org/intellij/plugins/xsltDebugger/rt/engine/local/AbstractFrame.java @@ -0,0 +1,62 @@ +/* + * 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.Debugger.Frame; + +/** + * Created by IntelliJ IDEA. + * User: sweinreuter + * Date: 02.06.2007 + */ +public abstract class AbstractFrame implements Frame { + private final F myPrev; + private F myNext; + + private boolean myValid = true; + + public AbstractFrame(F prev) { + myPrev = prev; + + if (prev != null) { + ((AbstractFrame)prev).myNext = this; + } + } + + public void invalidate() { + assert myValid; + assert myNext == null; + if (myPrev != null) { + ((AbstractFrame)myPrev).myNext = null; + } + myValid = false; + } + + public F getNext() { + assert myValid; + return myNext; + } + + public F getPrevious() { + assert myValid; + return myPrev; + } + + public boolean isValid() { + return myValid; + } +} diff --git a/plugins/xslt-debugger/rt/src/org/intellij/plugins/xsltDebugger/rt/engine/local/BreakpointImpl.java b/plugins/xslt-debugger/rt/src/org/intellij/plugins/xsltDebugger/rt/engine/local/BreakpointImpl.java new file mode 100644 index 000000000000..8b7388e68d08 --- /dev/null +++ b/plugins/xslt-debugger/rt/src/org/intellij/plugins/xsltDebugger/rt/engine/local/BreakpointImpl.java @@ -0,0 +1,114 @@ +/* + * 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.Breakpoint; + +/** + * Created by IntelliJ IDEA. + * User: sweinreuter + * Date: 29.05.2007 + */ +class BreakpointImpl implements Breakpoint { + private final String myUri; + private final int myLine; + private boolean myEnabled; + private String myCondition; + private String myLogMsg; + private String myTraceMsg; + private boolean mySuspend; + + public BreakpointImpl(String uri, int line) { + myUri = uri; + myLine = line; + myEnabled = true; + } + + public void setEnabled(boolean b) { + myEnabled = b; + } + + public void setCondition(String expr) { + myCondition = expr; + } + + public void setLogMessage(String expr) { + myLogMsg = expr; + } + + public String getTraceMessage() { + return myTraceMsg; + } + + public void setTraceMessage(String expr) { + myTraceMsg = expr; + } + + public boolean isSuspend() { + return mySuspend; + } + + public void setSuspend(boolean suspend) { + mySuspend = suspend; + } + + public String getCondition() { + return myCondition; + } + + public String getLogMessage() { + return myLogMsg; + } + + public String getUri() { + return myUri; + } + + public int getLine() { + return myLine; + } + + public boolean isEnabled() { + return myEnabled; + } + + public boolean equals(Object o) { + if (this == o) return true; + if (o == null || getClass() != o.getClass()) return false; + + final BreakpointImpl that = (BreakpointImpl)o; + + if (myLine != that.myLine) return false; + return myUri.equals(that.myUri); + } + + public int hashCode() { + int result; + result = myUri.hashCode(); + result = 31 * result + myLine; + return result; + } + + + public String toString() { + return "Breakpoint{" + + "myUri='" + myUri + '\'' + + ", myLine=" + myLine + + ", myEnabled=" + myEnabled + + '}'; + } +} diff --git a/plugins/xslt-debugger/rt/src/org/intellij/plugins/xsltDebugger/rt/engine/local/BreakpointManagerImpl.java b/plugins/xslt-debugger/rt/src/org/intellij/plugins/xsltDebugger/rt/engine/local/BreakpointManagerImpl.java new file mode 100644 index 000000000000..9b514509dc06 --- /dev/null +++ b/plugins/xslt-debugger/rt/src/org/intellij/plugins/xsltDebugger/rt/engine/local/BreakpointManagerImpl.java @@ -0,0 +1,107 @@ +/* + * 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 gnu.trove.TIntObjectHashMap; +import gnu.trove.TIntObjectProcedure; +import org.intellij.plugins.xsltDebugger.rt.engine.Breakpoint; +import org.intellij.plugins.xsltDebugger.rt.engine.BreakpointManager; + +import java.io.File; +import java.net.URI; +import java.net.URISyntaxException; +import java.util.*; + +/** + * Created by IntelliJ IDEA. + * User: sweinreuter + * Date: 19.05.2007 + */ +public class BreakpointManagerImpl implements BreakpointManager { + private final TIntObjectHashMap> myBreakpoints = + new TIntObjectHashMap>(); + + public Breakpoint setBreakpoint(File file, int line) { + return setBreakpoint(file.toURI().toASCIIString(), line); + } + + public synchronized void removeBreakpoint(String uri, int line) { + final Map s = myBreakpoints.get(line); + if (s != null) { + s.remove(normalizeUri(uri)); + } + } + + public synchronized List getBreakpoints() { + final ArrayList breakpoints = new ArrayList(); + myBreakpoints.forEachEntry(new TIntObjectProcedure>() { + public boolean execute(int i, Map map) { + breakpoints.addAll(map.values()); + return true; + } + }); + return breakpoints; + } + + public void removeBreakpoint(Breakpoint bp) { + removeBreakpoint(bp.getUri(), bp.getLine()); + } + + public synchronized Breakpoint setBreakpoint(String uri, int line) { + assert line > 0 : "No line number for breakpoint in file " + uri; + + uri = normalizeUri(uri); + final Map s = myBreakpoints.get(line); + final BreakpointImpl bp = new BreakpointImpl(uri, line); + if (s == null) { + final HashMap map = new HashMap(); + map.put(uri, bp); + myBreakpoints.put(line, map); + } else { + s.put(uri, bp); + } + return bp; + } + + private static String normalizeUri(String uri) { + // hmm, this code sucks, but seems to be a good guess to ensure the same format of + // strings (file:/C:/... vs, file:///C:/...) on both sides... + try { + try { + uri = uri.replaceAll(" ", "%20"); + return new File(new URI(uri)).toURI().toASCIIString(); + } catch (IllegalArgumentException e) { + return new URI(uri).normalize().toASCIIString(); + } + } catch (URISyntaxException e) { + System.err.println("Failed to parse <" + uri + ">: " + e); + return uri; + } + } + + public synchronized boolean isBreakpoint(String uri, int lineNumber) { + final Breakpoint breakpoint = getBreakpoint(uri, lineNumber); + return breakpoint != null && breakpoint.isEnabled(); + } + + public synchronized Breakpoint getBreakpoint(String uri, int lineNumber) { + final Map s = myBreakpoints.get(lineNumber); + if (s != null) { + return s.get(normalizeUri(uri)); + } + return null; + } +} diff --git a/plugins/xslt-debugger/rt/src/org/intellij/plugins/xsltDebugger/rt/engine/local/LocalDebugger.java b/plugins/xslt-debugger/rt/src/org/intellij/plugins/xsltDebugger/rt/engine/local/LocalDebugger.java new file mode 100644 index 000000000000..c0dfabc9a8ba --- /dev/null +++ b/plugins/xslt-debugger/rt/src/org/intellij/plugins/xsltDebugger/rt/engine/local/LocalDebugger.java @@ -0,0 +1,408 @@ +/* + * 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.Breakpoint; +import org.intellij.plugins.xsltDebugger.rt.engine.BreakpointManager; +import org.intellij.plugins.xsltDebugger.rt.engine.Debugger; +import org.intellij.plugins.xsltDebugger.rt.engine.DebuggerStoppedException; +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 myFrames = new LinkedList(); + private final LinkedList mySourceFrames = new LinkedList(); + + 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() : "")); + + 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 getGlobalVariables() { + final List vars = getCurrentFrame().getVariables(); + for (Iterator 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() { + System.out.println("."); + return true; + } +} diff --git a/plugins/xslt-debugger/rt/src/org/intellij/plugins/xsltDebugger/rt/engine/local/OutputEventQueueImpl.java b/plugins/xslt-debugger/rt/src/org/intellij/plugins/xsltDebugger/rt/engine/local/OutputEventQueueImpl.java new file mode 100644 index 000000000000..04ccf25434a8 --- /dev/null +++ b/plugins/xslt-debugger/rt/src/org/intellij/plugins/xsltDebugger/rt/engine/local/OutputEventQueueImpl.java @@ -0,0 +1,106 @@ +/* + * Copyright 2002-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.Debugger; +import org.intellij.plugins.xsltDebugger.rt.engine.OutputEventQueue; + +import java.util.ArrayList; +import java.util.List; + +/** + * Created by IntelliJ IDEA. + * User: sweinreuter + * Date: 08.06.2007 + */ +public class OutputEventQueueImpl implements OutputEventQueue { + private final Debugger myDebugger; + + private final List myEvents = new ArrayList(); + + private boolean myEnabled = true; + + public OutputEventQueueImpl(Debugger debugger) { + myDebugger = debugger; + } + + public void startDocument() { + if (myEnabled) { + myEvents.add(new NodeEvent(START_DOCUMENT, null, null)); + } + } + + public void endDocument() { + if (myEnabled) { + myEvents.add(new NodeEvent(END_DOCUMENT, null, null)); + } + } + + public void startElement(String prefix, String localName, String uri) { + addEvent(new NodeEvent(OutputEventQueue.START_ELEMENT, new NodeEvent.QName(prefix, localName, uri), null)); + } + + public void attribute(String prefix, String localName, String uri, String value) { + addEvent(new NodeEvent(ATTRIBUTE, new NodeEvent.QName(prefix, localName, uri), value)); + } + + public void endElement() { + addEvent(new NodeEvent(END_ELEMENT, null, null)); + } + + public void characters(String s) { + addEvent(new NodeEvent(CHARACTERS, null, s)); + } + + public void comment(String s) { + addEvent(new NodeEvent(COMMENT, null, s)); + } + + public void pi(String target, String data) { + addEvent(new NodeEvent(PI, new NodeEvent.QName(target), data)); + } + + public void trace(String text) { + addEvent(new NodeEvent(TRACE_POINT, null, text)); + } + + public void setEnabled(boolean b) { + myEnabled = b; + } + + public boolean isEnabled() { + return myEnabled; + } + + private void addEvent(NodeEvent event) { + if (myEnabled) { + final Debugger.StyleFrame frame = myDebugger.getCurrentFrame(); + if (frame != null) { + event.setLocation(frame.getURI(), frame.getLineNumber()); + } + myEvents.add(event); + } + } + + public List getEvents() { + try { + return new ArrayList(myEvents); + } finally { + myEvents.clear(); + } + } +} diff --git a/plugins/xslt-debugger/rt/src/org/intellij/plugins/xsltDebugger/rt/engine/local/VariableComparator.java b/plugins/xslt-debugger/rt/src/org/intellij/plugins/xsltDebugger/rt/engine/local/VariableComparator.java new file mode 100644 index 000000000000..638674d47f99 --- /dev/null +++ b/plugins/xslt-debugger/rt/src/org/intellij/plugins/xsltDebugger/rt/engine/local/VariableComparator.java @@ -0,0 +1,24 @@ +package org.intellij.plugins.xsltDebugger.rt.engine.local; + +import org.intellij.plugins.xsltDebugger.rt.engine.Debugger; + +import java.util.Comparator; + +public final class VariableComparator implements Comparator { + public static final VariableComparator INSTANCE = new VariableComparator(); + + private VariableComparator() { + } + + public int compare(Debugger.Variable o1, Debugger.Variable o2) { + final boolean og = o2.isGlobal(); + final boolean g = o1.isGlobal(); + if (og && !g) { + return 1; + } else if (!og && g) { + return -1; + } else { + return o1.getName().compareTo(o2.getName()); + } + } +} \ No newline at end of file diff --git a/plugins/xslt-debugger/rt/src/org/intellij/plugins/xsltDebugger/rt/engine/local/VariableImpl.java b/plugins/xslt-debugger/rt/src/org/intellij/plugins/xsltDebugger/rt/engine/local/VariableImpl.java new file mode 100644 index 000000000000..fdde205df0f5 --- /dev/null +++ b/plugins/xslt-debugger/rt/src/org/intellij/plugins/xsltDebugger/rt/engine/local/VariableImpl.java @@ -0,0 +1,85 @@ +/* + * 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.Debugger; +import org.intellij.plugins.xsltDebugger.rt.engine.Value; + +/** + * Created by IntelliJ IDEA. + * User: sweinreuter + * Date: 23.05.2007 + */ +public class VariableImpl implements Debugger.Variable { + private final boolean myGlobal; + private final Kind myKind; + private final String myRealname; + private final Value myValue; + private final String myUri; + private final int myLineNumber; + + public VariableImpl(String realname, Value value, boolean global, Kind kind, String uri, int lineNumber) { + myValue = value; + myRealname = realname; + myGlobal = global; + myKind = kind; + myUri = uri; + myLineNumber = lineNumber; + } + + public String getURI() { + return myUri; + } + + public int getLineNumber() { + return myLineNumber; + } + + public String getName() { + return myRealname; + } + + public Value getValue() { + return myValue; + } + + public boolean isGlobal() { + return myGlobal; + } + + public Kind getKind() { + return myKind; + } + + @Override + public String toString() { + return (myGlobal ? "global:" : "") + "{" + myKind + ":" + myRealname + "=" + myValue + "}"; + } + + public boolean equals(Object o) { + if (this == o) return true; + if (o == null || getClass() != o.getClass()) return false; + + final VariableImpl that = (VariableImpl)o; + + return myRealname.equals(that.myRealname); + } + + public int hashCode() { + return myRealname.hashCode(); + } +} diff --git a/plugins/xslt-debugger/rt/src/org/intellij/plugins/xsltDebugger/rt/engine/local/saxon/AbstractSaxonFrame.java b/plugins/xslt-debugger/rt/src/org/intellij/plugins/xsltDebugger/rt/engine/local/saxon/AbstractSaxonFrame.java new file mode 100644 index 000000000000..1719b6ab9499 --- /dev/null +++ b/plugins/xslt-debugger/rt/src/org/intellij/plugins/xsltDebugger/rt/engine/local/saxon/AbstractSaxonFrame.java @@ -0,0 +1,44 @@ +/* + * 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.saxon; + +import com.icl.saxon.om.NodeInfo; +import org.intellij.plugins.xsltDebugger.rt.engine.Debugger; +import org.intellij.plugins.xsltDebugger.rt.engine.local.AbstractFrame; + +/** + * Created by IntelliJ IDEA. + * User: sweinreuter + * Date: 02.06.2007 + */ +class AbstractSaxonFrame extends AbstractFrame { + protected final N myElement; + + protected AbstractSaxonFrame(F prev, N element) { + super(prev); + myElement = element; + } + + public int getLineNumber() { + return myElement.getLineNumber(); + } + + public String getURI() { + final String uri = myElement.getSystemId(); + return uri != null ? uri.replaceAll(" ", "%20") : null; + } +} diff --git a/plugins/xslt-debugger/rt/src/org/intellij/plugins/xsltDebugger/rt/engine/local/saxon/SaxonFrameImpl.java b/plugins/xslt-debugger/rt/src/org/intellij/plugins/xsltDebugger/rt/engine/local/saxon/SaxonFrameImpl.java new file mode 100644 index 000000000000..9f938240effe --- /dev/null +++ b/plugins/xslt-debugger/rt/src/org/intellij/plugins/xsltDebugger/rt/engine/local/saxon/SaxonFrameImpl.java @@ -0,0 +1,269 @@ +/* + * 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.saxon; + +import com.icl.saxon.Bindery; +import com.icl.saxon.Binding; +import com.icl.saxon.Context; +import com.icl.saxon.expr.*; +import com.icl.saxon.om.*; +import com.icl.saxon.output.GeneralOutputter; +import com.icl.saxon.style.ExpressionContext; +import com.icl.saxon.style.StyleElement; +import com.icl.saxon.style.XSLGeneralVariable; +import com.icl.saxon.style.XSLParam; +import org.intellij.plugins.xsltDebugger.rt.engine.Debugger; +import org.intellij.plugins.xsltDebugger.rt.engine.Value; +import org.intellij.plugins.xsltDebugger.rt.engine.local.VariableComparator; +import org.intellij.plugins.xsltDebugger.rt.engine.local.VariableImpl; +import org.w3c.dom.Node; + +import javax.xml.transform.OutputKeys; +import javax.xml.transform.TransformerConfigurationException; +import javax.xml.transform.TransformerException; +import javax.xml.transform.stream.StreamResult; +import java.io.StringWriter; +import java.lang.reflect.Field; +import java.net.URI; +import java.net.URISyntaxException; +import java.util.*; + +/** + * Created by IntelliJ IDEA. + * User: sweinreuter + * Date: 23.05.2007 + */ +class SaxonFrameImpl extends AbstractSaxonFrame implements Debugger.StyleFrame { + + private static Field fGeneralUseAllowed; + + static { + try { + fGeneralUseAllowed = SingletonNodeSet.class.getDeclaredField("generalUseAllowed"); + fGeneralUseAllowed.setAccessible(true); + } catch (NoSuchFieldException e) { + fGeneralUseAllowed = null; + System.err.println("Failed to get field com.icl.saxon.expr.SingletonNodeSet.generalUseAllowed - incompatible Saxon version?"); + e.printStackTrace(); + } + } + + private final Context myContext; + private final int myFrameId; + + SaxonFrameImpl(Debugger.StyleFrame prev, Context context, StyleElement element) { + super(prev, element); + myContext = context; + myFrameId = context.getBindery().getFrameId(); + } + + public String getInstruction() { + return myElement.getDisplayName(); + } + + public List getVariables() { + assert isValid(); + + final ArrayList variables = new ArrayList(); + final Enumeration[] variableNames = myElement.getVariableNames(); + + this.addVariables(myElement, variables, variableNames[0], true); + this.addVariables(myElement, variables, variableNames[1], false); + + Collections.sort(variables, VariableComparator.INSTANCE); + + return variables; + } + + public Value eval(String expr) throws Debugger.EvaluationException { + assert isValid(); + + try { + // trick to avoid exception when evaluating variable references on xsl:template frames + final MyDummyElement dummy = new MyDummyElement(myElement); + final Expression expression = Expression.make(expr, dummy.getStaticContext()); + return convertValue(expression.evaluate(myContext)); + } catch (XPathException e) { + throw new Debugger.EvaluationException(e.getMessage()); + } + } + + private Value convertValue(com.icl.saxon.expr.Value v) throws XPathException { + return MyValue.create(v, myContext); + } + + void addVariables(StyleElement element, ArrayList variables, Enumeration enumeration, boolean isGlobal) { + final Context context = myContext; + final StaticContext ctx = context.getStaticContext(); + + final NamePool pool = element.getNamePool(); + final Bindery bindery = context.getBindery(); + + while (enumeration.hasMoreElements()) { + String name = (String)enumeration.nextElement(); + try { + final String[] parts = name.split("\\^"); + final String realname = parts[1]; + final int fingerprint = ctx != null ? ctx.getFingerprint(realname, false) : pool.getFingerprint(parts[0], realname); + final Binding binding = element.getVariableBinding(fingerprint); + final Debugger.Variable.Kind kind = + binding instanceof XSLParam ? Debugger.Variable.Kind.PARAMETER : Debugger.Variable.Kind.VARIABLE; + final com.icl.saxon.expr.Value value = bindery.getValue(binding, myFrameId); + + if (binding instanceof XSLGeneralVariable) { + final XSLGeneralVariable v = (XSLGeneralVariable)binding; + final String id = v.getSystemId(); + variables.add(new VariableImpl(realname, convertValue(value), isGlobal, kind, id != null ? id.replaceAll(" ", "%20") : null, + v.getLineNumber())); + } else { + variables.add(new VariableImpl(realname, convertValue(value), isGlobal, kind, null, -1)); + } + } catch (XPathException e) { + // this should never happen I guess... + e.printStackTrace(); + } + } + } + + private static class MyValue implements Value { + private final Object myValue; + private final Type myType; + + public MyValue(Object value, String type) { + myValue = value; + myType = new ObjectType(type); + } + + public MyValue(Object value, int type) { + myValue = value; + myType = mapType(type); + } + + public Object getValue() { + return myValue; + } + + public Type getType() { + return myType; + } + + private static Type mapType(int type) { + switch (type) { + case com.icl.saxon.expr.Value.BOOLEAN: + return XPathType.BOOLEAN; + case com.icl.saxon.expr.Value.NODESET: + return XPathType.NODESET; + case com.icl.saxon.expr.Value.NUMBER: + return XPathType.NUMBER; + case com.icl.saxon.expr.Value.STRING: + return XPathType.STRING; + case com.icl.saxon.expr.Value.OBJECT: + return XPathType.OBJECT; + default: + return XPathType.UNKNOWN; + } + } + + public static Value create(com.icl.saxon.expr.Value v, Context context) throws XPathException { + if (v instanceof NodeSetValue) { + if (v instanceof FragmentValue) { + final FragmentValue value = (FragmentValue)v; + final boolean b = value.isGeneralUseAllowed(); + try { + if (!b) value.allowGeneralUse(); + + final DocumentInfo node = value.getRootNode(); + final GeneralOutputter outputter = new GeneralOutputter(node.getNamePool()); + final StringWriter writer = new StringWriter(); + final Properties props = new Properties(); + props.setProperty(OutputKeys.OMIT_XML_DECLARATION, "yes"); + outputter.setOutputDestination(props, new StreamResult(writer)); + node.copy(outputter); + + return new MyValue(writer.toString(), v.getDataType()); + } catch (TransformerException e) { + return new MyValue(v.asString(), v.getDataType()); + } finally { + if (!b && fGeneralUseAllowed != null) { + resetGeneralUseAllowed(value); + } + } + } else if (v instanceof TextFragmentValue) { + // this really is just a string + return new MyValue(v.asString(), com.icl.saxon.expr.Value.STRING); + } + + final List list = new ArrayList(); + final NodeEnumeration nodeEnumeration = ((NodeSetValue)v).enumerate(); + while (nodeEnumeration.hasMoreElements()) { + final NodeInfo node = nodeEnumeration.nextElement(); + final String path = Navigator.getPath(node); + final String id = node.getSystemId(); + if (id != null) { + try { + list.add(new Node(new URI(id.replaceAll(" ", "%20")).normalize().toASCIIString(), node.getLineNumber(), path, + node.getStringValue())); + } catch (URISyntaxException e) { + e.printStackTrace(); + list.add(new Node(id, node.getLineNumber(), path, node.getStringValue())); + } + } else { + list.add(new Node(null, -1, path, node.getStringValue())); + } + } + return new MyValue(new NodeSet(v.asString(), list), v.getDataType()); + } else if (v instanceof ObjectValue) { + final Object o = ((ObjectValue)v).getObject(); + return new MyValue(o, o != null ? o.getClass().getName() : "null"); + } else { + return new MyValue(v.evaluateAsString(context), v.getDataType()); + } + } + } + + private static void resetGeneralUseAllowed(FragmentValue value) { + try { + fGeneralUseAllowed.set(value, false); + } catch (IllegalAccessException e) { + e.printStackTrace(); + } + } + + private static class MyDummyElement extends StyleElement { + private final StyleElement myElement; + + public MyDummyElement(StyleElement element) { + myElement = element; + substituteFor(element); + } + + public void prepareAttributes() throws TransformerConfigurationException { + } + + public void process(Context context) throws TransformerException { + } + + public StaticContext getStaticContext() { + return new ExpressionContext(this); + } + + @Override + public Node getPreviousSibling() { + return myElement.getPreviousSibling(); + } + } +} diff --git a/plugins/xslt-debugger/rt/src/org/intellij/plugins/xsltDebugger/rt/engine/local/saxon/SaxonSourceFrame.java b/plugins/xslt-debugger/rt/src/org/intellij/plugins/xsltDebugger/rt/engine/local/saxon/SaxonSourceFrame.java new file mode 100644 index 000000000000..1125b5f90544 --- /dev/null +++ b/plugins/xslt-debugger/rt/src/org/intellij/plugins/xsltDebugger/rt/engine/local/saxon/SaxonSourceFrame.java @@ -0,0 +1,36 @@ +/* + * 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.saxon; + +import com.icl.saxon.om.Navigator; +import com.icl.saxon.om.NodeInfo; +import org.intellij.plugins.xsltDebugger.rt.engine.Debugger; + +/** + * Created by IntelliJ IDEA. + * User: sweinreuter + * Date: 03.06.2007 + */ +class SaxonSourceFrame extends AbstractSaxonFrame implements Debugger.SourceFrame { + public SaxonSourceFrame(Debugger.SourceFrame prev, NodeInfo element) { + super(prev, element); + } + + public String getXPath() { + return Navigator.getPath(myElement); + } +} diff --git a/plugins/xslt-debugger/rt/src/org/intellij/plugins/xsltDebugger/rt/engine/local/saxon/SaxonSupport.java b/plugins/xslt-debugger/rt/src/org/intellij/plugins/xsltDebugger/rt/engine/local/saxon/SaxonSupport.java new file mode 100644 index 000000000000..21961e4b7f87 --- /dev/null +++ b/plugins/xslt-debugger/rt/src/org/intellij/plugins/xsltDebugger/rt/engine/local/saxon/SaxonSupport.java @@ -0,0 +1,30 @@ +package org.intellij.plugins.xsltDebugger.rt.engine.local.saxon; + +import com.icl.saxon.Controller; +import com.icl.saxon.TransformerFactoryImpl; +import org.intellij.plugins.xsltDebugger.rt.engine.local.LocalDebugger; + +import javax.xml.transform.Transformer; +import javax.xml.transform.TransformerFactory; + +/* +* Created by IntelliJ IDEA. +* User: sweinreuter +* Date: 12.01.2009 +*/ +public class SaxonSupport { + public static boolean init(Transformer transformer, LocalDebugger dbg) { + if (transformer instanceof Controller) { + System.out.println("SAXON"); + final Controller controller = (Controller)transformer; + controller.setLineNumbering(true); + controller.addTraceListener(new SaxonTraceListener(dbg, controller)); + return true; + } + return false; + } + + public static TransformerFactory createTransformerFactory() { + return new TransformerFactoryImpl(); + } +} \ No newline at end of file diff --git a/plugins/xslt-debugger/rt/src/org/intellij/plugins/xsltDebugger/rt/engine/local/saxon/SaxonTraceListener.java b/plugins/xslt-debugger/rt/src/org/intellij/plugins/xsltDebugger/rt/engine/local/saxon/SaxonTraceListener.java new file mode 100644 index 000000000000..b19e63e01987 --- /dev/null +++ b/plugins/xslt-debugger/rt/src/org/intellij/plugins/xsltDebugger/rt/engine/local/saxon/SaxonTraceListener.java @@ -0,0 +1,242 @@ +/* + * 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.saxon; + +import com.icl.saxon.Context; +import com.icl.saxon.Controller; +import com.icl.saxon.Mode; +import com.icl.saxon.NodeHandler; +import com.icl.saxon.om.NamePool; +import com.icl.saxon.om.Navigator; +import com.icl.saxon.om.NodeInfo; +import com.icl.saxon.output.Emitter; +import com.icl.saxon.output.GeneralOutputter; +import com.icl.saxon.style.StyleElement; +import com.icl.saxon.trace.TraceListener; +import org.intellij.plugins.xsltDebugger.rt.engine.local.LocalDebugger; +import org.intellij.plugins.xsltDebugger.rt.engine.local.OutputEventQueueImpl; + +import javax.xml.transform.OutputKeys; +import javax.xml.transform.TransformerException; +import java.lang.reflect.Field; +import java.util.Properties; + +/** + * A Simple trace listener that writes messages to System.err + */ + +public class SaxonTraceListener implements TraceListener { + private static final boolean TRACE = "true".equals(System.getProperty("xslt.debugger.trace", "false")); + + private String indent = ""; + private final LocalDebugger myDebugger; + private final Controller myController; + private boolean myIsInitialized; + + public SaxonTraceListener(LocalDebugger debugger, Controller controller) { + myDebugger = debugger; + myController = controller; + } + + /** + * Called at start + */ + + public void open() { + myDebugger.getEventQueue().startDocument(); + if (TRACE) { + trace(""); + } + } + + private static void trace(String s) { + if (TRACE) { + System.err.println(s); + } + } + + /** + * Called at end + */ + + public void close() { + myDebugger.getEventQueue().endDocument(); +// myDebugger.stopped(); + if (TRACE) { + trace(""); + } + } + + + /** + * Called for all top level elements + */ + public void toplevel(NodeInfo element) { + if (!myIsInitialized) { + myIsInitialized = true; + + final Properties properties = myController.getOutputProperties(); + final String method = properties.getProperty(OutputKeys.METHOD); + if (method == null || "xml".equals(method) || "html".equals(method)) { + try { + final Emitter emitter = myController.getOutputter().getEmitter(); + final GeneralOutputter outputter = new TracingOutputter(emitter, myController.getNamePool()); + + final Field fOutputter = Controller.class.getDeclaredField("currentOutputter"); + fOutputter.setAccessible(true); + fOutputter.set(myController, outputter); + } catch (Exception e1) { + System.err.println("Failed to change output emitter"); + e1.printStackTrace(); + } + } + } + + if (TRACE) { + StyleElement e = (StyleElement)element; + trace(""); + } + } + + /** + * Called when a node of the source tree gets processed + */ + public void enterSource(NodeHandler handler, Context context) { + NodeInfo curr = context.getContextNodeInfo(); + final String path = Navigator.getPath(curr); + if (TRACE) { + trace(indent + ""); + indent += " "; + } + + myDebugger.pushSource(new SaxonSourceFrame(myDebugger.getSourceFrame(), curr)); + } + + /** + * Called after a node of the source tree got processed + */ + public void leaveSource(NodeHandler handler, Context context) { + if (TRACE) { + indent = indent.substring(0, indent.length() - 1); + trace(indent + ""); + } + + myDebugger.popSource(); + } + + /** + * Called when an element of the stylesheet gets processed + */ + public void enter(NodeInfo element, Context context) { + if (element.getNodeType() == NodeInfo.ELEMENT) { + if (TRACE) { + trace(indent + ""); + indent += " "; + } + + myDebugger.enter(new SaxonFrameImpl(myDebugger.getCurrentFrame(), context, (StyleElement)element)); + } + } + + /** + * Called after an element of the stylesheet got processed + */ + public void leave(NodeInfo element, Context context) { + if (element.getNodeType() == NodeInfo.ELEMENT) { +// final int lineNumber = element.getLineNumber(); +// final String uri = element.getSystemId(); + + myDebugger.leave(); + + if (TRACE) { + indent = indent.substring(0, indent.length() - 1); + trace(indent + " "); + } + } + } + + static String getModeName(Context context) { + Mode mode = context.getMode(); + if (mode == null) return "#none"; + int nameCode = mode.getNameCode(); + if (nameCode == -1) { + return "#default"; + } else { + return context.getController().getNamePool().getDisplayName(nameCode); + } + } + + private final class TracingOutputter extends GeneralOutputter { + private final NamePool myNamePool; + private final OutputEventQueueImpl myEventQueue; + + public TracingOutputter(Emitter emitter, NamePool namePool) { + super(namePool); + this.emitter = emitter; + myNamePool = namePool; + myEventQueue = myDebugger.getEventQueue(); + } + + public void writeAttribute(int nameCode, String value, boolean noEscape) throws TransformerException { + if (myEventQueue.isEnabled()) { + final String localName = myNamePool.getLocalName(nameCode); + final String prefix = myNamePool.getPrefix(nameCode); + myEventQueue.attribute(prefix, localName, myNamePool.getURI(nameCode), value); + } + super.writeAttribute(nameCode, value, noEscape); + } + + public void writeComment(String comment) throws TransformerException { + myEventQueue.comment(comment); + super.writeComment(comment); + } + + public void writeContent(char[] chars, int start, int length) throws TransformerException { + myEventQueue.characters(new String(chars, start, length)); + super.writeContent(chars, start, length); + } + + public void writeContent(StringBuffer chars, int start, int len) throws TransformerException { + myEventQueue.characters(chars.substring(start, start + len)); + super.writeContent(chars, start, len); + } + + public void writeEndTag(int nameCode) throws TransformerException { + myEventQueue.endElement(); + super.writeEndTag(nameCode); + } + + public void writePI(String target, String data) throws TransformerException { + myEventQueue.pi(target, data); + super.writePI(target, data); + } + + public void writeStartTag(int nameCode) throws TransformerException { + if (myEventQueue.isEnabled()) { + final String localName = myNamePool.getLocalName(nameCode); + final String prefix = myNamePool.getPrefix(nameCode); + myEventQueue.startElement(prefix, localName, myNamePool.getURI(nameCode)); + } + super.writeStartTag(nameCode); + } + } +} \ No newline at end of file diff --git a/plugins/xslt-debugger/rt/src/org/intellij/plugins/xsltDebugger/rt/engine/local/saxon9/AbstractSaxon9Frame.java b/plugins/xslt-debugger/rt/src/org/intellij/plugins/xsltDebugger/rt/engine/local/saxon9/AbstractSaxon9Frame.java new file mode 100644 index 000000000000..8018944996b5 --- /dev/null +++ b/plugins/xslt-debugger/rt/src/org/intellij/plugins/xsltDebugger/rt/engine/local/saxon9/AbstractSaxon9Frame.java @@ -0,0 +1,30 @@ +package org.intellij.plugins.xsltDebugger.rt.engine.local.saxon9; + +import org.intellij.plugins.xsltDebugger.rt.engine.Debugger; +import org.intellij.plugins.xsltDebugger.rt.engine.local.AbstractFrame; + +import javax.xml.transform.Source; +import javax.xml.transform.SourceLocator; + +/* +* Created by IntelliJ IDEA. +* User: sweinreuter +* Date: 04.05.11 +*/ +class AbstractSaxon9Frame extends AbstractFrame { + protected final N myElement; + + protected AbstractSaxon9Frame(F prev, N element) { + super(prev); + myElement = element; + } + + public int getLineNumber() { + return ((SourceLocator)myElement).getLineNumber(); + } + + public String getURI() { + final String uri = myElement.getSystemId(); + return uri != null ? uri.replaceAll(" ", "%20") : null; + } +} \ No newline at end of file diff --git a/plugins/xslt-debugger/rt/src/org/intellij/plugins/xsltDebugger/rt/engine/local/saxon9/Saxon9SourceFrame.java b/plugins/xslt-debugger/rt/src/org/intellij/plugins/xsltDebugger/rt/engine/local/saxon9/Saxon9SourceFrame.java new file mode 100644 index 000000000000..1b8385e7e968 --- /dev/null +++ b/plugins/xslt-debugger/rt/src/org/intellij/plugins/xsltDebugger/rt/engine/local/saxon9/Saxon9SourceFrame.java @@ -0,0 +1,38 @@ +/* + * 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.saxon9; + +import net.sf.saxon.om.NodeInfo; +import net.sf.saxon.tree.util.Navigator; +import org.intellij.plugins.xsltDebugger.rt.engine.Debugger; + +/** + * Created by IntelliJ IDEA. + * User: sweinreuter + * Date: 02.06.2007 + */ +class Saxon9SourceFrame extends AbstractSaxon9Frame implements Debugger.SourceFrame { + + protected Saxon9SourceFrame(Debugger.SourceFrame prev, N element) { + super(prev, element); + } + + + public String getXPath() { + return Navigator.getPath(myElement); + } +} diff --git a/plugins/xslt-debugger/rt/src/org/intellij/plugins/xsltDebugger/rt/engine/local/saxon9/Saxon9StyleFrame.java b/plugins/xslt-debugger/rt/src/org/intellij/plugins/xsltDebugger/rt/engine/local/saxon9/Saxon9StyleFrame.java new file mode 100644 index 000000000000..15363a8f03bd --- /dev/null +++ b/plugins/xslt-debugger/rt/src/org/intellij/plugins/xsltDebugger/rt/engine/local/saxon9/Saxon9StyleFrame.java @@ -0,0 +1,98 @@ +/* + * 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.saxon9; + +import net.sf.saxon.expr.StackFrame; +import net.sf.saxon.expr.XPathContext; +import net.sf.saxon.expr.instruct.SlotManager; +import net.sf.saxon.om.ValueRepresentation; +import net.sf.saxon.style.StyleElement; +import net.sf.saxon.trans.XPathException; +import org.intellij.plugins.xsltDebugger.rt.engine.Debugger; +import org.intellij.plugins.xsltDebugger.rt.engine.Value; +import org.intellij.plugins.xsltDebugger.rt.engine.local.VariableImpl; + +import java.util.ArrayList; +import java.util.Arrays; +import java.util.List; + +/** + * Created by IntelliJ IDEA. + * User: sweinreuter + * Date: 02.06.2007 + */ +class Saxon9StyleFrame extends AbstractSaxon9Frame implements Debugger.StyleFrame { + + private final XPathContext myXPathContext; + private final StackFrame myStackFrame; + + protected Saxon9StyleFrame(Debugger.StyleFrame prev, N element, XPathContext xPathContext) { + super(prev, element); + myXPathContext = xPathContext; + myStackFrame = myXPathContext.getStackFrame(); + } + + public String getInstruction() { + return myElement.getDisplayName(); + } + + public Value eval(String expr) throws Debugger.EvaluationException { + return null; + } + + public List getVariables() { + final ArrayList variables = new ArrayList(); + + XPathContext context = myXPathContext; + while (context != null) { + final StackFrame frame = context.getStackFrame(); + final SlotManager map = frame.getStackFrameMap(); + final int numberOfVariables = map.getNumberOfVariables(); + System.out.println("numberOfVariables = " + numberOfVariables); + + for (int i = 0; i < numberOfVariables; i++) { + final ValueRepresentation valueRepresentation = context.evaluateLocalVariable(i); + System.out.println("valueRepresentation = " + valueRepresentation); + } + final ValueRepresentation[] values = frame.getStackFrameValues(); + System.out.println("values = " + Arrays.toString(values)); + + for (int i = 0, valuesLength = values.length; i < valuesLength; i++) { + final ValueRepresentation value = values[i]; + + variables.add(new VariableImpl(map.getVariableMap().get(i).getDisplayName(), new Value() { + public Object getValue() { + try { + return value.getStringValue(); + } catch (XPathException e) { + return " - error: " + e.getMessage() + " - "; + } + } + + public Type getType() { + return XPathType.UNKNOWN; + } + }, false, Debugger.Variable.Kind.VARIABLE, "", -1)); + } + + context = context.getCaller(); + System.out.println("context = " + context); + } + + return variables; + } +} diff --git a/plugins/xslt-debugger/rt/src/org/intellij/plugins/xsltDebugger/rt/engine/local/saxon9/Saxon9Support.java b/plugins/xslt-debugger/rt/src/org/intellij/plugins/xsltDebugger/rt/engine/local/saxon9/Saxon9Support.java new file mode 100644 index 000000000000..c871a9991fa8 --- /dev/null +++ b/plugins/xslt-debugger/rt/src/org/intellij/plugins/xsltDebugger/rt/engine/local/saxon9/Saxon9Support.java @@ -0,0 +1,72 @@ +package org.intellij.plugins.xsltDebugger.rt.engine.local.saxon9; + +import net.sf.saxon.Controller; +import net.sf.saxon.TransformerFactoryImpl; +import net.sf.saxon.event.PipelineConfiguration; +import net.sf.saxon.event.ProxyReceiver; +import net.sf.saxon.event.Receiver; +import net.sf.saxon.expr.instruct.Debugger; +import net.sf.saxon.expr.instruct.SlotManager; +import net.sf.saxon.lib.FeatureKeys; +import net.sf.saxon.lib.SerializerFactory; +import net.sf.saxon.om.StructuredQName; +import net.sf.saxon.serialize.CharacterMapExpander; +import net.sf.saxon.serialize.Emitter; +import net.sf.saxon.trans.XPathException; +import org.intellij.plugins.xsltDebugger.rt.engine.local.LocalDebugger; + +import javax.xml.transform.Transformer; +import javax.xml.transform.TransformerFactory; +import java.util.Properties; + +/* +* Created by IntelliJ IDEA. +* User: sweinreuter +* Date: 12.01.2009 +*/ +public class Saxon9Support { + public static boolean init(Transformer transformer, final LocalDebugger dbg) { + if (transformer instanceof Controller) { + System.out.println("SAXON 9"); + final Controller controller = (Controller)transformer; + ((Saxon9TraceListener)controller.getConfiguration().getTraceListener()).setDebugger(dbg); + controller.getConfiguration().setLineNumbering(true); + controller.getConfiguration().setCompileWithTracing(true); + controller.getConfiguration().setMultiThreading(false); + controller.getConfiguration().setSerializerFactory(new SerializerFactory(controller.getConfiguration()) { + @Override + protected Receiver createXMLSerializer(Emitter emitter, + Properties props, + PipelineConfiguration pipe, + CharacterMapExpander characterMapExpander, + ProxyReceiver normalizer) throws XPathException { + return super.createXMLSerializer(emitter, props, pipe, characterMapExpander, normalizer); + } + + @Override + protected Emitter newXMLEmitter() { + return new TracingOutputter(dbg.getEventQueue(), super.newXMLEmitter()); + } + }); + controller.getConfiguration().setDebugger(new Debugger() { + public SlotManager makeSlotManager() { + return new SlotManager() { + @Override + public int allocateSlotNumber(StructuredQName qName) { + System.out.println("qName = " + qName); + return super.allocateSlotNumber(qName); + } + }; + } + }); + return true; + } + return false; + } + + public static TransformerFactory createTransformerFactory() { + final TransformerFactoryImpl factory = new TransformerFactoryImpl(); + factory.setAttribute(FeatureKeys.TRACE_LISTENER, new Saxon9TraceListener()); + return factory; + } +} \ No newline at end of file diff --git a/plugins/xslt-debugger/rt/src/org/intellij/plugins/xsltDebugger/rt/engine/local/saxon9/Saxon9TraceListener.java b/plugins/xslt-debugger/rt/src/org/intellij/plugins/xsltDebugger/rt/engine/local/saxon9/Saxon9TraceListener.java new file mode 100644 index 000000000000..19e132a8c654 --- /dev/null +++ b/plugins/xslt-debugger/rt/src/org/intellij/plugins/xsltDebugger/rt/engine/local/saxon9/Saxon9TraceListener.java @@ -0,0 +1,90 @@ +/* + * 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.saxon9; + +import net.sf.saxon.expr.XPathContext; +import net.sf.saxon.lib.TraceListener; +import net.sf.saxon.om.Item; +import net.sf.saxon.om.NodeInfo; +import net.sf.saxon.style.StyleElement; +import net.sf.saxon.trace.InstructionInfo; +import org.intellij.plugins.xsltDebugger.rt.engine.local.LocalDebugger; + +public class Saxon9TraceListener implements TraceListener { + private static final boolean TRACE = "true".equals(System.getProperty("xslt.debugger.trace", "false")); + + private LocalDebugger myDebugger; + + public Saxon9TraceListener() { + } + + public void setDebugger(LocalDebugger debugger) { + myDebugger = debugger; + } + + private static void trace(String s) { + if (TRACE) { + System.err.println(s); + } + } + + public void open() { + myDebugger.getEventQueue().startDocument(); + if (TRACE) { + trace(""); + } + } + + public void close() { + myDebugger.getEventQueue().endDocument(); + + if (TRACE) { + trace(""); + } + } + + public void enter(InstructionInfo instructionInfo, XPathContext xPathContext) { + if (TRACE) { + trace("<" + instructionInfo + ">"); + } + if (instructionInfo instanceof StyleElement) { + myDebugger.enter(new Saxon9StyleFrame(myDebugger.getCurrentFrame(), (StyleElement)instructionInfo, xPathContext)); + } + } + + public void leave(InstructionInfo instructionInfo) { + if (TRACE) { + trace(""); + } + if (instructionInfo instanceof StyleElement) { + myDebugger.leave(); + } + } + + public void startCurrentItem(Item item) { + if (item instanceof NodeInfo) { + if (TRACE) { + trace("<" + ((NodeInfo)item).getDisplayName() + ">"); + } + myDebugger.pushSource(new Saxon9SourceFrame(myDebugger.getSourceFrame(), (NodeInfo)item)); + } + } + + public void endCurrentItem(Item item) { + myDebugger.popSource(); + } +} \ No newline at end of file diff --git a/plugins/xslt-debugger/rt/src/org/intellij/plugins/xsltDebugger/rt/engine/local/saxon9/TracingOutputter.java b/plugins/xslt-debugger/rt/src/org/intellij/plugins/xsltDebugger/rt/engine/local/saxon9/TracingOutputter.java new file mode 100644 index 000000000000..74171cfd1b1a --- /dev/null +++ b/plugins/xslt-debugger/rt/src/org/intellij/plugins/xsltDebugger/rt/engine/local/saxon9/TracingOutputter.java @@ -0,0 +1,124 @@ +package org.intellij.plugins.xsltDebugger.rt.engine.local.saxon9; + +import net.sf.saxon.event.PipelineConfiguration; +import net.sf.saxon.serialize.Emitter; +import net.sf.saxon.trans.XPathException; +import org.intellij.plugins.xsltDebugger.rt.engine.local.OutputEventQueueImpl; + +import javax.xml.transform.stream.StreamResult; +import java.io.OutputStream; +import java.io.Writer; +import java.util.Properties; + +final class TracingOutputter extends Emitter { + private final OutputEventQueueImpl myEventQueue; + private final Emitter myEmitter; + + public TracingOutputter(OutputEventQueueImpl queue, Emitter emitter) { + myEmitter = emitter; + myEventQueue = queue; + } + + @Override + public void setPipelineConfiguration(PipelineConfiguration pipe) { + super.setPipelineConfiguration(pipe); + myEmitter.setPipelineConfiguration(pipe); + } + + @Override + public void setSystemId(String systemId) { + myEmitter.setSystemId(systemId); + } + + @Override + public void setOutputProperties(Properties details) throws XPathException { + myEmitter.setOutputProperties(details); + } + + @Override + public void setStreamResult(StreamResult result) throws XPathException { + myEmitter.setStreamResult(result); + } + + @Override + public void setWriter(Writer writer) throws XPathException { + myEmitter.setWriter(writer); + } + + @Override + public void setOutputStream(OutputStream stream) throws XPathException { + myEmitter.setOutputStream(stream); + } + + @Override + public void setUnparsedEntity(String name, String uri, String publicId) throws XPathException { + myEmitter.setUnparsedEntity(name, uri, publicId); + } + + public void open() throws XPathException { + myEmitter.open(); + } + + public void startDocument(int properties) throws XPathException { + myEmitter.startDocument(properties); + } + + public void endDocument() throws XPathException { + myEmitter.endDocument(); + } + + public void namespace(int namespaceCode, int properties) throws XPathException { + myEmitter.namespace(namespaceCode, properties); + } + + @Override + public void close() throws XPathException { + myEmitter.close(); + } + + public void startElement(int nameCode, int typeCode, int locationId, int properties) throws XPathException { + if (myEventQueue.isEnabled()) { + final String localName = namePool.getLocalName(nameCode); + final String prefix = namePool.getPrefix(nameCode); + myEventQueue.startElement(prefix, localName, namePool.getURI(nameCode)); + } + myEmitter.startElement(nameCode, typeCode, locationId, properties); + } + + + public void attribute(int nameCode, int typeCode, CharSequence value, int locationId, int properties) throws XPathException { + if (myEventQueue.isEnabled()) { + final String localName = namePool.getLocalName(nameCode); + final String prefix = namePool.getPrefix(nameCode); + myEventQueue.attribute(prefix, localName, namePool.getURI(nameCode), value.toString()); + } + myEmitter.attribute(nameCode, typeCode, value, locationId, properties); + } + + public void startContent() throws XPathException { + } + + public void endElement() throws XPathException { + myEventQueue.endElement(); + myEmitter.endElement(); + } + + public void characters(CharSequence chars, int locationId, int properties) throws XPathException { + myEventQueue.characters(chars.toString()); + myEmitter.characters(chars, locationId, properties); + } + + public void processingInstruction(String name, CharSequence data, int locationId, int properties) throws XPathException { + myEventQueue.pi(name, data.toString()); + myEmitter.processingInstruction(name, data, locationId, properties); + } + + public void comment(CharSequence content, int locationId, int properties) throws XPathException { + myEventQueue.comment(content.toString()); + myEmitter.comment(content, locationId, properties); + } + + public boolean usesTypeAnnotations() { + return false; + } +} diff --git a/plugins/xslt-debugger/rt/src/org/intellij/plugins/xsltDebugger/rt/engine/local/xalan/TracingSerializationHandler.java b/plugins/xslt-debugger/rt/src/org/intellij/plugins/xsltDebugger/rt/engine/local/xalan/TracingSerializationHandler.java new file mode 100644 index 000000000000..9d0026c245d0 --- /dev/null +++ b/plugins/xslt-debugger/rt/src/org/intellij/plugins/xsltDebugger/rt/engine/local/xalan/TracingSerializationHandler.java @@ -0,0 +1,397 @@ +package org.intellij.plugins.xsltDebugger.rt.engine.local.xalan; + +import org.apache.xml.serializer.DOMSerializer; +import org.apache.xml.serializer.NamespaceMappings; +import org.apache.xml.serializer.SerializationHandler; +import org.intellij.plugins.xsltDebugger.rt.engine.local.LocalDebugger; +import org.w3c.dom.Node; +import org.xml.sax.*; + +import javax.xml.transform.SourceLocator; +import javax.xml.transform.Transformer; +import java.io.IOException; +import java.io.OutputStream; +import java.io.Writer; +import java.util.Properties; +import java.util.Vector; + +class TracingSerializationHandler implements SerializationHandler { + private final LocalDebugger myDebugger; + private final SerializationHandler mySerializationHandler; + + public TracingSerializationHandler(LocalDebugger debugger, SerializationHandler handler) { + myDebugger = debugger; + mySerializationHandler = handler; + +// final URL location = SerializationHandler.class.getProtectionDomain().getCodeSource().getLocation(); +// System.out.println("location = " + location); + } + + private static String calcPrefix(String qname) { + return qname.indexOf(':') == -1 ? "" : qname.split(":")[0]; + } + + public void setContentHandler(ContentHandler ch) { + mySerializationHandler.setContentHandler(ch); + } + + public void close() { + mySerializationHandler.close(); + } + + public void serialize(Node node) throws IOException { + mySerializationHandler.serialize(node); + } + + public boolean setEscaping(boolean escape) throws SAXException { + return mySerializationHandler.setEscaping(escape); + } + + public void setIndentAmount(int spaces) { + mySerializationHandler.setIndentAmount(spaces); + } + + public void setTransformer(Transformer transformer) { + mySerializationHandler.setTransformer(transformer); + } + + public Transformer getTransformer() { + return mySerializationHandler.getTransformer(); + } + + public void setNamespaceMappings(NamespaceMappings mappings) { + mySerializationHandler.setNamespaceMappings(mappings); + } + + public void flushPending() throws SAXException { + mySerializationHandler.flushPending(); + } + + public void setDTDEntityExpansion(boolean expand) { + mySerializationHandler.setDTDEntityExpansion(expand); + } + + public void addAttribute(String uri, String localName, String rawName, String type, String value, boolean XSLAttribute) + throws SAXException { + myDebugger.getEventQueue().attribute(calcPrefix(rawName), localName, uri, value); + mySerializationHandler.addAttribute(uri, localName, rawName, type, value, XSLAttribute); + } + + public void addAttribute(String uri, String localName, String rawName, String type, String value) throws SAXException { + myDebugger.getEventQueue().attribute("", localName, uri, value); + mySerializationHandler.addAttribute(uri, localName, rawName, type, value); + } + + public void addAttributes(Attributes atts) throws SAXException { + mySerializationHandler.addAttributes(atts); + } + + public void addAttribute(String qName, String value) { + mySerializationHandler.addAttribute(qName, value); + } + + public void characters(String chars) throws SAXException { + mySerializationHandler.characters(chars); + } + + public void characters(Node node) throws SAXException { + mySerializationHandler.characters(node); + } + + public void endElement(String elemName) throws SAXException { + mySerializationHandler.endElement(elemName); + } + + public void startElement(String uri, String localName, String qName) throws SAXException { + myDebugger.getEventQueue().startElement(calcPrefix(qName), localName, uri); + mySerializationHandler.startElement(uri, localName, qName); + } + + public void startElement(String qName) throws SAXException { + mySerializationHandler.startElement(qName); + } + + public void namespaceAfterStartElement(String uri, String prefix) throws SAXException { + mySerializationHandler.namespaceAfterStartElement(uri, prefix); + } + + public boolean startPrefixMapping(String prefix, String uri, boolean shouldFlush) throws SAXException { + return mySerializationHandler.startPrefixMapping(prefix, uri, shouldFlush); + } + + public void entityReference(String entityName) throws SAXException { + mySerializationHandler.entityReference(entityName); + } + + public NamespaceMappings getNamespaceMappings() { + return mySerializationHandler.getNamespaceMappings(); + } + + public String getPrefix(String uri) { + return mySerializationHandler.getPrefix(uri); + } + + public String getNamespaceURI(String name, boolean isElement) { + return mySerializationHandler.getNamespaceURI(name, isElement); + } + + public String getNamespaceURIFromPrefix(String prefix) { + return mySerializationHandler.getNamespaceURIFromPrefix(prefix); + } + + public void setSourceLocator(SourceLocator locator) { + mySerializationHandler.setSourceLocator(locator); + } + + public void addUniqueAttribute(String qName, String value, int flags) throws SAXException { + mySerializationHandler.addUniqueAttribute(qName, value, flags); + } + + public void addXSLAttribute(String qName, String value, String uri) { + mySerializationHandler.addXSLAttribute(qName, value, uri); + } + + public void setDocumentLocator(Locator locator) { + mySerializationHandler.setDocumentLocator(locator); + } + + public void startDocument() throws SAXException { + mySerializationHandler.startDocument(); + } + + public void endDocument() throws SAXException { + mySerializationHandler.endDocument(); + } + + public void startPrefixMapping(String prefix, String uri) throws SAXException { + mySerializationHandler.startPrefixMapping(prefix, uri); + } + + public void endPrefixMapping(String prefix) throws SAXException { + mySerializationHandler.endPrefixMapping(prefix); + } + + public void startElement(String uri, String localName, String qName, Attributes atts) throws SAXException { + mySerializationHandler.startElement(uri, localName, qName, atts); + } + + public void endElement(String uri, String localName, String qName) throws SAXException { + mySerializationHandler.endElement(uri, localName, qName); + } + + public void characters(char[] ch, int start, int length) throws SAXException { + mySerializationHandler.characters(ch, start, length); + } + + public void ignorableWhitespace(char[] ch, int start, int length) throws SAXException { + mySerializationHandler.ignorableWhitespace(ch, start, length); + } + + public void processingInstruction(String target, String data) throws SAXException { + mySerializationHandler.processingInstruction(target, data); + } + + public void skippedEntity(String name) throws SAXException { + mySerializationHandler.skippedEntity(name); + } + + public void comment(String comment) throws SAXException { + mySerializationHandler.comment(comment); + } + + public void startDTD(String name, String publicId, String systemId) throws SAXException { + mySerializationHandler.startDTD(name, publicId, systemId); + } + + public void endDTD() throws SAXException { + mySerializationHandler.endDTD(); + } + + public void startEntity(String name) throws SAXException { + mySerializationHandler.startEntity(name); + } + + public void endEntity(String name) throws SAXException { + mySerializationHandler.endEntity(name); + } + + public void startCDATA() throws SAXException { + mySerializationHandler.startCDATA(); + } + + public void endCDATA() throws SAXException { + mySerializationHandler.endCDATA(); + } + + public void comment(char[] ch, int start, int length) throws SAXException { + mySerializationHandler.comment(ch, start, length); + } + + public String getDoctypePublic() { + return mySerializationHandler.getDoctypePublic(); + } + + public String getDoctypeSystem() { + return mySerializationHandler.getDoctypeSystem(); + } + + public String getEncoding() { + return mySerializationHandler.getEncoding(); + } + + public boolean getIndent() { + return mySerializationHandler.getIndent(); + } + + public int getIndentAmount() { + return mySerializationHandler.getIndentAmount(); + } + + public String getMediaType() { + return mySerializationHandler.getMediaType(); + } + + public boolean getOmitXMLDeclaration() { + return mySerializationHandler.getOmitXMLDeclaration(); + } + + public String getStandalone() { + return mySerializationHandler.getStandalone(); + } + + public String getVersion() { + return mySerializationHandler.getVersion(); + } + + public void setCdataSectionElements(Vector URI_and_localNames) { + mySerializationHandler.setCdataSectionElements(URI_and_localNames); + } + + public void setDoctype(String system, String pub) { + mySerializationHandler.setDoctype(system, pub); + } + + public void setDoctypePublic(String doctype) { + mySerializationHandler.setDoctypePublic(doctype); + } + + public void setDoctypeSystem(String doctype) { + mySerializationHandler.setDoctypeSystem(doctype); + } + + public void setEncoding(String encoding) { + mySerializationHandler.setEncoding(encoding); + } + + public void setIndent(boolean indent) { + mySerializationHandler.setIndent(indent); + } + + public void setMediaType(String mediatype) { + mySerializationHandler.setMediaType(mediatype); + } + + public void setOmitXMLDeclaration(boolean b) { + mySerializationHandler.setOmitXMLDeclaration(b); + } + + public void setStandalone(String standalone) { + mySerializationHandler.setStandalone(standalone); + } + + public void setVersion(String version) { + mySerializationHandler.setVersion(version); + } + + public String getOutputProperty(String name) { + return mySerializationHandler.getOutputProperty(name); + } + + public String getOutputPropertyDefault(String name) { + return mySerializationHandler.getOutputPropertyDefault(name); + } + + public void setOutputProperty(String name, String val) { + mySerializationHandler.setOutputProperty(name, val); + } + + public void setOutputPropertyDefault(String name, String val) { + mySerializationHandler.setOutputPropertyDefault(name, val); + } + + public void elementDecl(String name, String model) throws SAXException { + mySerializationHandler.elementDecl(name, model); + } + + public void attributeDecl(String eName, String aName, String type, String mode, String value) throws SAXException { + mySerializationHandler.attributeDecl(eName, aName, type, mode, value); + } + + public void internalEntityDecl(String name, String value) throws SAXException { + mySerializationHandler.internalEntityDecl(name, value); + } + + public void externalEntityDecl(String name, String publicId, String systemId) throws SAXException { + mySerializationHandler.externalEntityDecl(name, publicId, systemId); + } + + public void notationDecl(String name, String publicId, String systemId) throws SAXException { + mySerializationHandler.notationDecl(name, publicId, systemId); + } + + public void unparsedEntityDecl(String name, String publicId, String systemId, String notationName) throws SAXException { + mySerializationHandler.unparsedEntityDecl(name, publicId, systemId, notationName); + } + + public void warning(SAXParseException exception) throws SAXException { + mySerializationHandler.warning(exception); + } + + public void error(SAXParseException exception) throws SAXException { + mySerializationHandler.error(exception); + } + + public void fatalError(SAXParseException exception) throws SAXException { + mySerializationHandler.fatalError(exception); + } + + public void setOutputStream(OutputStream output) { + mySerializationHandler.setOutputStream(output); + } + + public OutputStream getOutputStream() { + return mySerializationHandler.getOutputStream(); + } + + public void setWriter(Writer writer) { + mySerializationHandler.setWriter(writer); + } + + public Writer getWriter() { + return mySerializationHandler.getWriter(); + } + + public void setOutputFormat(Properties format) { + mySerializationHandler.setOutputFormat(format); + } + + public Properties getOutputFormat() { + return mySerializationHandler.getOutputFormat(); + } + + public ContentHandler asContentHandler() throws IOException { + return mySerializationHandler.asContentHandler(); + } + + public DOMSerializer asDOMSerializer() throws IOException { + return mySerializationHandler.asDOMSerializer(); + } + + public boolean reset() { + return mySerializationHandler.reset(); + } + + public Object asDOM3Serializer() throws IOException { + return mySerializationHandler.asDOM3Serializer(); + } +} \ No newline at end of file diff --git a/plugins/xslt-debugger/rt/src/org/intellij/plugins/xsltDebugger/rt/engine/local/xalan/XObjectValue.java b/plugins/xslt-debugger/rt/src/org/intellij/plugins/xsltDebugger/rt/engine/local/xalan/XObjectValue.java new file mode 100644 index 000000000000..4d1a39d09c6f --- /dev/null +++ b/plugins/xslt-debugger/rt/src/org/intellij/plugins/xsltDebugger/rt/engine/local/xalan/XObjectValue.java @@ -0,0 +1,80 @@ +package org.intellij.plugins.xsltDebugger.rt.engine.local.xalan; + +import org.apache.xml.dtm.DTM; +import org.apache.xml.dtm.DTMIterator; +import org.apache.xml.dtm.ref.DTMNodeIterator; +import org.apache.xml.serializer.ToXMLStream; +import org.apache.xpath.objects.XNodeSet; +import org.apache.xpath.objects.XObject; +import org.apache.xpath.objects.XRTreeFrag; +import org.intellij.plugins.xsltDebugger.rt.engine.Value; + +import javax.xml.transform.SourceLocator; +import javax.xml.transform.TransformerException; +import java.io.StringWriter; +import java.util.ArrayList; + +class XObjectValue implements Value { + private String myTypeString; + private Object myValue; + + public XObjectValue(XObject value) { + try { + if (value != null) { + myTypeString = value.getTypeString().replaceAll("#", ""); + } else { + myTypeString = "undefined"; + } + + if (value instanceof XNodeSet) { + final ArrayList nodes = new ArrayList(); + final DTMIterator v = value.mutableNodeset(); + for (int i = 0; i < v.getLength(); i++) { + final int p = v.item(i); + final DTM dtm = v.getDTM(p); + if (dtm == null) continue; + + final SourceLocator loc = dtm.getSourceLocatorFor(p); + nodes.add(new Node(loc != null ? loc.getSystemId() : null, loc != null ? loc.getLineNumber() : -1, XalanSupport.getPath(dtm, p), + dtm.getStringValue(p).toString())); + } + + myValue = new NodeSet(value.str(), nodes); + } else if (value instanceof XRTreeFrag) { + final org.w3c.dom.Node node = ((DTMNodeIterator)value.object()).nextNode(); + if (node == null) { + myValue = ""; + } else { + try { + final ToXMLStream stream = new ToXMLStream(); + final StringWriter writer = new StringWriter(); + stream.setWriter(writer); + stream.setOmitXMLDeclaration(true); + stream.serialize(node); + + myValue = writer.toString(); + } catch (Exception e) { + e.printStackTrace(); + myValue = "???"; + } + } + } else if (value != null) { + myValue = value.object(); + } + } catch (TransformerException e) { + myTypeString = "UNKNOWN"; + } + } + + public Object getValue() { + return myValue; + } + + public Type getType() { + try { + return XPathType.valueOf(myTypeString.toUpperCase()); + } catch (IllegalArgumentException e) { + return new ObjectType(myTypeString); + } + } +} \ No newline at end of file diff --git a/plugins/xslt-debugger/rt/src/org/intellij/plugins/xsltDebugger/rt/engine/local/xalan/XalanStyleFrame.java b/plugins/xslt-debugger/rt/src/org/intellij/plugins/xsltDebugger/rt/engine/local/xalan/XalanStyleFrame.java new file mode 100644 index 000000000000..047a6380551b --- /dev/null +++ b/plugins/xslt-debugger/rt/src/org/intellij/plugins/xsltDebugger/rt/engine/local/xalan/XalanStyleFrame.java @@ -0,0 +1,182 @@ +package org.intellij.plugins.xsltDebugger.rt.engine.local.xalan; + +import org.apache.xalan.templates.ElemLiteralResult; +import org.apache.xalan.templates.ElemParam; +import org.apache.xalan.templates.ElemTemplateElement; +import org.apache.xalan.templates.ElemVariable; +import org.apache.xalan.trace.TracerEvent; +import org.apache.xalan.transformer.TransformerImpl; +import org.apache.xml.dtm.DTM; +import org.apache.xml.dtm.DTMIterator; +import org.apache.xml.dtm.ref.DTMNodeProxy; +import org.apache.xml.utils.PrefixResolver; +import org.apache.xml.utils.PrefixResolverDefault; +import org.apache.xpath.XPath; +import org.apache.xpath.XPathContext; +import org.intellij.plugins.xsltDebugger.rt.engine.Debugger; +import org.intellij.plugins.xsltDebugger.rt.engine.Value; +import org.intellij.plugins.xsltDebugger.rt.engine.local.AbstractFrame; +import org.intellij.plugins.xsltDebugger.rt.engine.local.VariableComparator; +import org.intellij.plugins.xsltDebugger.rt.engine.local.VariableImpl; +import org.w3c.dom.Node; + +import javax.xml.transform.TransformerException; +import java.util.ArrayList; +import java.util.Collections; +import java.util.List; +import java.util.Vector; + +class XalanStyleFrame extends AbstractFrame implements Debugger.StyleFrame { + private final boolean myWithSourceFrame; + + private final TransformerImpl myTransformer; + private final ElemTemplateElement myCurrentElement; + private final XPathContext myContext; + private final int myCurrentNode; + + private final int myLineNumber; + private final String myURI; + private final String myInstr; + + public XalanStyleFrame(TracerEvent ev, Debugger.StyleFrame currentFrame, boolean withSourceFrame) { + super(currentFrame); + myWithSourceFrame = withSourceFrame; + + myInstr = getInstruction(ev.m_styleNode); + + myLineNumber = ev.m_styleNode.getLineNumber(); + if (ev.m_styleNode.getSystemId() != null) { + myURI = ev.m_styleNode.getSystemId(); + } else if (currentFrame != null && currentFrame.getURI() != null) { + myURI = currentFrame.getURI(); + } else { + myURI = ev.m_processor.getStylesheet().getSystemId(); + } + myTransformer = ev.m_processor; + + myCurrentElement = myTransformer.getCurrentElement(); + myContext = ev.m_processor.getXPathContext(); + myCurrentNode = myContext.getCurrentNode(); + } + + private void addVariable(ElemVariable variable, boolean global, List variables) { + final Debugger.Variable.Kind kind = variable instanceof ElemParam ? + Debugger.Variable.Kind.PARAMETER : Debugger.Variable.Kind.VARIABLE; + + assert global == variable.getIsTopLevel() : global + " vs. " + variable.getIsTopLevel() + " (" + variable.getName() + ")"; + + final String name = variable.getName().getLocalName(); + try { + final Value value = new XObjectValue(variable.getValue(myTransformer, myCurrentNode)); + + variables.add(new VariableImpl(name, value, global, kind, variable.getSystemId(), variable.getLineNumber())); + } catch (TransformerException e) { + // TODO + e.printStackTrace(); + } + } + + public boolean isWithSourceFrame() { + return myWithSourceFrame; + } + + public String getInstruction() { + return myInstr; + } + + public List getVariables() { + return collectVariables(); + } + + private List collectVariables() { + List variables = new ArrayList(); + + @SuppressWarnings({ "unchecked" }) + final Vector globals = myTransformer.getStylesheet().getVariablesAndParamsComposed(); + for (ElemVariable variable : globals) { + addVariable(variable, true, variables); + } + + ElemTemplateElement p = myCurrentElement; + while (p != null) { + ElemTemplateElement s = p; + while ((s = s.getPreviousSiblingElem()) != null) { + if (s instanceof ElemVariable) { + final ElemVariable variable = (ElemVariable)s; + if (variable.getIsTopLevel()) { + continue; + } + + addVariable(variable, false, variables); + } + } + p = p.getParentElem(); + } + + Collections.sort(variables, VariableComparator.INSTANCE); + + return variables; + } + + public String getURI() { + return myURI; + } + + public int getLineNumber() { + return myLineNumber; + } + + public Value eval(String expr) throws Debugger.EvaluationException { + try { + final DTMIterator context = myTransformer.getContextNodeList(); + + final int ctx; + final DTM dtm = context.getDTM(myCurrentNode); + if (dtm.getDocumentRoot(myCurrentNode) == myCurrentNode) { + ctx = dtm.getFirstChild(myCurrentNode); + } else { + ctx = myCurrentNode; + } + + final DTMNodeProxy c = new DTMNodeProxy(dtm, ctx); + final PrefixResolver prefixResolver = new PrefixResolverDefault(c) { + public String getNamespaceForPrefix(String prefix, Node context) { + if (context instanceof DTMNodeProxy) { + final DTMNodeProxy proxy = (DTMNodeProxy)context; + final DTM dtm = proxy.getDTM(); + int p = proxy.getDTMNodeNumber(); + while (p != DTM.NULL) { + int nsNode = dtm.getFirstNamespaceNode(p, true); + while (nsNode != DTM.NULL) { + final String s = dtm.getLocalName(nsNode); + if (s.equals(prefix)) { + return dtm.getNodeValue(nsNode); + } + nsNode = dtm.getNextNamespaceNode(p, nsNode, true); + } + p = dtm.getParent(p); + } + } + return super.getNamespaceForPrefix(prefix, context); + } + }; + + final XPath xPath = new XPath(expr, myCurrentElement, prefixResolver, XPath.SELECT, myTransformer.getErrorListener()); + return new XObjectValue(xPath.execute(myContext, myCurrentNode, myCurrentElement)); + } catch (Exception e) { + e.printStackTrace(); + final String message = e.getMessage(); + throw new Debugger.EvaluationException(message != null ? message : e.getClass().getSimpleName()); + } + } + + static String getInstruction(ElemTemplateElement node) { + final String name = node.getNodeName(); + if (node instanceof ElemLiteralResult) { + return name; + } else if (name != null && name.indexOf(':') == -1) { + return "xsl:" + name; + } + return name; + } +} diff --git a/plugins/xslt-debugger/rt/src/org/intellij/plugins/xsltDebugger/rt/engine/local/xalan/XalanSupport.java b/plugins/xslt-debugger/rt/src/org/intellij/plugins/xsltDebugger/rt/engine/local/xalan/XalanSupport.java new file mode 100644 index 000000000000..66cd89c7a91a --- /dev/null +++ b/plugins/xslt-debugger/rt/src/org/intellij/plugins/xsltDebugger/rt/engine/local/xalan/XalanSupport.java @@ -0,0 +1,123 @@ +package org.intellij.plugins.xsltDebugger.rt.engine.local.xalan; + +import org.apache.xalan.processor.TransformerFactoryImpl; +import org.apache.xalan.transformer.TransformerImpl; +import org.apache.xml.dtm.DTM; +import org.apache.xml.utils.DefaultErrorHandler; +import org.intellij.plugins.xsltDebugger.rt.engine.DebuggerStoppedException; +import org.intellij.plugins.xsltDebugger.rt.engine.local.LocalDebugger; +import org.w3c.dom.Node; + +import javax.xml.transform.Transformer; +import javax.xml.transform.TransformerException; +import javax.xml.transform.TransformerFactory; +import java.util.TooManyListenersException; + +/* +* Created by IntelliJ IDEA. +* User: sweinreuter +* Date: 12.01.2009 +*/ +public class XalanSupport { + public static boolean init(Transformer transformer, LocalDebugger dbg) { + if (transformer instanceof TransformerImpl) { + try { + System.out.println("XALAN: " + + Class.forName("org.apache.xalan.Version", true, transformer.getClass().getClassLoader()).getMethod("getVersion") + .invoke(null)); + final TransformerImpl tr = (TransformerImpl)transformer; + + tr.setErrorListener(new DefaultErrorHandler(false) { + @Override + public void fatalError(TransformerException exception) throws TransformerException { + if (!(exception.getCause() instanceof DebuggerStoppedException)) { + super.fatalError(exception); + } + } + }); + + try { + tr.getTraceManager().addTraceListener(new XalanTraceListener(dbg, tr)); + } catch (TooManyListenersException e) { + throw new AssertionError(e); + } + + return true; + } catch (Exception e) { + e.printStackTrace(); + } + } + return false; + } + + public static TransformerFactory createTransformerFactory() { + final TransformerFactoryImpl factory = new TransformerFactoryImpl(); + prepareFactory(factory); + return factory; + } + + public static TransformerFactory prepareFactory(TransformerFactory factory) { + try { + factory.setAttribute("http://xml.apache.org/xalan/properties/source-location", Boolean.TRUE); + factory.setAttribute("http://xml.apache.org/xalan/features/optimize", Boolean.FALSE); + } catch (Exception e) { + // ignore + } + return factory; + } + + public static String getPath(DTM dtm, int node) { + String pre; + switch (dtm.getNodeType(node)) { + case Node.DOCUMENT_NODE: + return "/"; + case Node.ELEMENT_NODE: + pre = getPath(dtm, dtm.getParent(node)); + return (pre.equals("/") ? "" : pre) + + "/" + dtm.getNodeName(node) + "[" + getNumberSimple(dtm, node) + "]"; + case Node.ATTRIBUTE_NODE: + return getPath(dtm, dtm.getParent(node)) + "/@" + dtm.getNodeNameX(node); + case Node.TEXT_NODE: + pre = getPath(dtm, dtm.getParent(node)); + return (pre.equals("/") ? "" : pre) + + "/text()[" + getNumberSimple(dtm, node) + "]"; + case Node.COMMENT_NODE: + pre = getPath(dtm, dtm.getParent(node)); + return (pre.equals("/") ? "" : pre) + + "/comment()[" + getNumberSimple(dtm, node) + "]"; + case Node.PROCESSING_INSTRUCTION_NODE: + pre = getPath(dtm, dtm.getParent(node)); + return (pre.equals("/") ? "" : pre) + + "/processing-instruction()[" + getNumberSimple(dtm, node) + "]"; + } + return "?"; + } + + private static int getNumberSimple(DTM dtm, int node) { + final String localName = dtm.getLocalName(node); + + String uri = dtm.getNamespaceURI(node); + if (uri == null) uri = ""; + + final short type = dtm.getNodeType(node); + + int i = 1; + int p = node; + while ((p = dtm.getPreviousSibling(p)) != DTM.NULL) { + if (dtm.getNodeType(p) == type) { + switch (type) { + case Node.TEXT_NODE: + case Node.COMMENT_NODE: + case Node.PROCESSING_INSTRUCTION_NODE: + i++; + break; + default: + if (localName.equals(dtm.getLocalName(p)) && uri.equals(dtm.getNamespaceURI(p))) { + i++; + } + } + } + } + return i; + } +} \ No newline at end of file diff --git a/plugins/xslt-debugger/rt/src/org/intellij/plugins/xsltDebugger/rt/engine/local/xalan/XalanTraceListener.java b/plugins/xslt-debugger/rt/src/org/intellij/plugins/xsltDebugger/rt/engine/local/xalan/XalanTraceListener.java new file mode 100644 index 000000000000..dbb5ad3152ad --- /dev/null +++ b/plugins/xslt-debugger/rt/src/org/intellij/plugins/xsltDebugger/rt/engine/local/xalan/XalanTraceListener.java @@ -0,0 +1,162 @@ +package org.intellij.plugins.xsltDebugger.rt.engine.local.xalan; + +import org.apache.xalan.trace.GenerateEvent; +import org.apache.xalan.trace.PrintTraceListener; +import org.apache.xalan.trace.TracerEvent; +import org.apache.xalan.transformer.TransformerImpl; +import org.apache.xml.dtm.DTM; +import org.apache.xml.dtm.DTMIterator; +import org.apache.xml.serializer.SerializationHandler; +import org.apache.xml.serializer.SerializerTrace; +import org.intellij.plugins.xsltDebugger.rt.engine.Debugger; +import org.intellij.plugins.xsltDebugger.rt.engine.local.AbstractFrame; +import org.intellij.plugins.xsltDebugger.rt.engine.local.LocalDebugger; + +import javax.xml.transform.SourceLocator; +import java.io.PrintWriter; + +/* +* Created by IntelliJ IDEA. +* User: sweinreuter +* Date: 09.01.2009 +*/ +public class XalanTraceListener extends PrintTraceListener { + private final LocalDebugger myDebugger; + private final TransformerImpl myTransformer; + + private volatile boolean myTracing = false; + private boolean firstTrace = true; + + public XalanTraceListener(LocalDebugger localDebugger, TransformerImpl tr) { + super(new PrintWriter(System.out, true)); + m_traceElements = false; + m_traceGeneration = false; + m_traceSelection = false; + + myDebugger = localDebugger; + myTransformer = tr; + } + + @Override + public void trace(TracerEvent ev) { + if (myTracing) return; + myTracing = true; // prevents handling of recursive trace() events + + try { + // init + if (firstTrace) { + firstTrace = false; + final SerializationHandler handler = myTransformer.getSerializationHandler(); + myTransformer.setSerializationHandler(new TracingSerializationHandler(myDebugger, handler)); + } + + super.trace(ev); + + final DTMIterator iterator = myTransformer.getContextNodeList(); + final int node = myTransformer.getMatchedNode(); + final Debugger.SourceFrame sourceFrame = myDebugger.getSourceFrame(); + final boolean withSource; + if (sourceFrame == null || ((MySourceFrame)sourceFrame).getMatchedNode() != node) { + myDebugger.pushSource(new MySourceFrame(sourceFrame, iterator.getDTM(node), node)); + withSource = true; + } else { + withSource = false; + } + myDebugger.enter(new XalanStyleFrame(ev, myDebugger.getCurrentFrame(), withSource)); + } finally { + myTracing = false; + } + } + + @Override + public void traceEnd(TracerEvent ev) { + if (myTracing) return; + + if (myDebugger.getCurrentFrame() == null) { + return; + } + + // xsl:choose (and maybe others) don't generate traceEnd()-events + final String instr = XalanStyleFrame.getInstruction(ev.m_styleNode); + if (instr != null) { + while (!instr.equals(myDebugger.getCurrentFrame().getInstruction())) { + leave(); + } + } + + super.traceEnd(ev); + leave(); + } + + private void leave() { + if (((XalanStyleFrame)myDebugger.getCurrentFrame()).isWithSourceFrame()) { + myDebugger.popSource(); + } + myDebugger.leave(); + } + + @Override + public void generated(GenerateEvent ev) { + if (!(myTransformer.getSerializationHandler() instanceof TracingSerializationHandler)) { + // internal RTF evaluation, don't care + return; + } + switch (ev.m_eventtype) { + case SerializerTrace.EVENTTYPE_STARTDOCUMENT: + myDebugger.getEventQueue().startDocument(); + break; + case SerializerTrace.EVENTTYPE_ENDDOCUMENT: + myDebugger.getEventQueue().endDocument(); + break; + + case SerializerTrace.EVENTTYPE_ENDELEMENT: + myDebugger.getEventQueue().endElement(); + break; + + case SerializerTrace.EVENTTYPE_CDATA: + case SerializerTrace.EVENTTYPE_CHARACTERS: + myDebugger.getEventQueue().characters(new String(ev.m_characters, ev.m_start, ev.m_length)); + break; + case SerializerTrace.EVENTTYPE_COMMENT: + myDebugger.getEventQueue().comment(ev.m_data); + break; + case SerializerTrace.EVENTTYPE_PI: + myDebugger.getEventQueue().pi(ev.m_name, ev.m_data); + break; + } + } + + private static class MySourceFrame extends AbstractFrame implements Debugger.SourceFrame { + private final String mySystemId; + private final int myLineNumber; + private final int myMatchedNode; + private String myPath; + + public MySourceFrame(Debugger.SourceFrame sourceFrame, DTM dtm, int node) { + super(sourceFrame); + + final SourceLocator loc = dtm.getSourceLocatorFor(node); + mySystemId = loc.getSystemId(); + myLineNumber = loc.getLineNumber(); + + myPath = XalanSupport.getPath(dtm, node); + myMatchedNode = node; + } + + public String getXPath() { + return myPath; + } + + public String getURI() { + return mySystemId; + } + + public int getLineNumber() { + return myLineNumber; + } + + public int getMatchedNode() { + return myMatchedNode; + } + } +} \ No newline at end of file diff --git a/plugins/xslt-debugger/rt/src/org/intellij/plugins/xsltDebugger/rt/engine/remote/DebuggerServer.java b/plugins/xslt-debugger/rt/src/org/intellij/plugins/xsltDebugger/rt/engine/remote/DebuggerServer.java new file mode 100644 index 000000000000..674029d45681 --- /dev/null +++ b/plugins/xslt-debugger/rt/src/org/intellij/plugins/xsltDebugger/rt/engine/remote/DebuggerServer.java @@ -0,0 +1,153 @@ +/* + * 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.remote; + +import com.icl.saxon.TransformerFactoryImpl; +import org.intellij.plugins.xsltDebugger.rt.engine.Debugger; +import org.intellij.plugins.xsltDebugger.rt.engine.Value; +import org.intellij.plugins.xsltDebugger.rt.engine.local.LocalDebugger; + +import javax.rmi.PortableRemoteObject; +import javax.xml.transform.Result; +import javax.xml.transform.Source; +import javax.xml.transform.Transformer; +import javax.xml.transform.TransformerConfigurationException; +import javax.xml.transform.stream.StreamResult; +import javax.xml.transform.stream.StreamSource; +import java.io.File; +import java.rmi.NotBoundException; +import java.rmi.RemoteException; +import java.rmi.registry.LocateRegistry; +import java.rmi.registry.Registry; +import java.util.List; + +/** + * Created by IntelliJ IDEA. + * User: sweinreuter + * Date: 23.05.2007 + */ +public class DebuggerServer extends PortableRemoteObject implements RemoteDebugger { + private static final String XSLT_DEBUGGER = "XsltDebugger"; + public static final int PORT = 34275; + + private final Debugger myDebugger; + private final RemoteBreakpointManagerImpl myBreakpointManager; + private final RemoteEventQueueImpl myEventQueue; + private final int myPort; + + private DebuggerServer(Transformer transformer, Source xml, Result out, int port) + throws TransformerConfigurationException, RemoteException { + myPort = port; + myDebugger = new LocalDebugger(transformer, xml, out) { + @Override + public void stop(boolean b) { + try { + super.stop(b); + } finally { + if (b) System.exit(0); + } + } + }; + myBreakpointManager = new RemoteBreakpointManagerImpl(myDebugger.getBreakpointManager()); + myEventQueue = new RemoteEventQueueImpl(myDebugger.getEventQueue()); + } + + public static DebuggerServer create(Transformer xsl, Source xml, Result out, int port) + throws TransformerConfigurationException, RemoteException { + final DebuggerServer server = new DebuggerServer(xsl, xml, out, port); + final Registry registry = LocateRegistry.createRegistry(port); + registry.rebind(XSLT_DEBUGGER, server); + return server; + } + + public static DebuggerServer create(File f, File x) throws TransformerConfigurationException, RemoteException { + return create(new TransformerFactoryImpl().newTransformer(new StreamSource(f)), new StreamSource(x), new StreamResult(), PORT); + } + + public void stop(boolean force) throws RemoteException { + myDebugger.stop(force); + try { + LocateRegistry.getRegistry(myPort).unbind(XSLT_DEBUGGER); + } catch (NotBoundException e) { + // hu? + } + } + + public boolean ping() throws RemoteException { + return myDebugger.ping(); + } + + public Debugger.State waitForStateChange(Debugger.State state) throws RemoteException { + return myDebugger.waitForStateChange(state); + } + + public boolean waitForDebuggee() throws RemoteException { + return myDebugger.waitForDebuggee(); + } + + public boolean start() throws RemoteException { + return myDebugger.start(); + } + + public void step() throws RemoteException { + myDebugger.step(); + } + + public void stepInto() throws RemoteException { + myDebugger.stepInto(); + } + + public void resume() throws RemoteException { + myDebugger.resume(); + } + + public boolean isStopped() throws RemoteException { + return myDebugger.isStopped(); + } + + public Frame getCurrentFrame() throws RemoteException { + return RemoteFrameImpl.create(myDebugger.getCurrentFrame()); + } + + public Frame getSourceFrame() throws RemoteException { + return RemoteFrameImpl.create(myDebugger.getSourceFrame()); + } + + public Value eval(String expr) throws RemoteException, Debugger.EvaluationException { + return getCurrentFrame().eval(expr); + } + + public List getGlobalVariables() throws RemoteException { + return RemoteVariableImpl.convert(myDebugger.getGlobalVariables()); + } + + public RemoteBreakpointManager getBreakpointManager() throws RemoteException { + return myBreakpointManager; + } + + public Debugger.State getState() throws RemoteException { + return myDebugger.getState(); + } + + public void pause() throws RemoteException { + myDebugger.pause(); + } + + public EventQueue getEventQueue() throws RemoteException { + return myEventQueue; + } +} diff --git a/plugins/xslt-debugger/rt/src/org/intellij/plugins/xsltDebugger/rt/engine/remote/RemoteBreakpoint.java b/plugins/xslt-debugger/rt/src/org/intellij/plugins/xsltDebugger/rt/engine/remote/RemoteBreakpoint.java new file mode 100644 index 000000000000..181cfa3fa44a --- /dev/null +++ b/plugins/xslt-debugger/rt/src/org/intellij/plugins/xsltDebugger/rt/engine/remote/RemoteBreakpoint.java @@ -0,0 +1,51 @@ +/* + * 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.remote; + +import java.rmi.Remote; +import java.rmi.RemoteException; + +/** + * Created by IntelliJ IDEA. + * User: sweinreuter + * Date: 29.05.2007 + */ +public interface RemoteBreakpoint extends Remote { + String getUri() throws RemoteException; + + int getLine() throws RemoteException; + + boolean isEnabled() throws RemoteException; + + void setEnabled(boolean b) throws RemoteException; + + String getCondition() throws RemoteException; + + void setCondition(String expr) throws RemoteException; + + String getLogMessage() throws RemoteException; + + void setLogMessage(String expr) throws RemoteException; + + String getTraceMessage() throws RemoteException; + + void setTraceMessage(String expr) throws RemoteException; + + boolean isSuspend() throws RemoteException; + + void setSuspend(boolean suspend) throws RemoteException; +} diff --git a/plugins/xslt-debugger/rt/src/org/intellij/plugins/xsltDebugger/rt/engine/remote/RemoteBreakpointImpl.java b/plugins/xslt-debugger/rt/src/org/intellij/plugins/xsltDebugger/rt/engine/remote/RemoteBreakpointImpl.java new file mode 100644 index 000000000000..08724c146e98 --- /dev/null +++ b/plugins/xslt-debugger/rt/src/org/intellij/plugins/xsltDebugger/rt/engine/remote/RemoteBreakpointImpl.java @@ -0,0 +1,93 @@ +/* + * 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.remote; + +import org.intellij.plugins.xsltDebugger.rt.engine.Breakpoint; + +import javax.rmi.PortableRemoteObject; +import java.rmi.RemoteException; +import java.util.ArrayList; +import java.util.List; + +/** + * Created by IntelliJ IDEA. + * User: sweinreuter + * Date: 29.05.2007 + */ +class RemoteBreakpointImpl extends PortableRemoteObject implements RemoteBreakpoint { + private final Breakpoint myBreakpoint; + + public RemoteBreakpointImpl(Breakpoint breakpoint) throws RemoteException { + myBreakpoint = breakpoint; + } + + public String getUri() throws RemoteException { + return myBreakpoint.getUri(); + } + + public int getLine() throws RemoteException { + return myBreakpoint.getLine(); + } + + public boolean isEnabled() throws RemoteException { + return myBreakpoint.isEnabled(); + } + + public String getCondition() { + return myBreakpoint.getCondition(); + } + + public String getLogMessage() { + return myBreakpoint.getLogMessage(); + } + + public void setCondition(String expr) { + myBreakpoint.setCondition(expr); + } + + public void setEnabled(boolean enabled) { + myBreakpoint.setEnabled(enabled); + } + + public void setLogMessage(String expr) { + myBreakpoint.setLogMessage(expr); + } + + public String getTraceMessage() throws RemoteException { + return myBreakpoint.getTraceMessage(); + } + + public void setTraceMessage(String expr) throws RemoteException { + myBreakpoint.setTraceMessage(expr); + } + + public boolean isSuspend() { + return myBreakpoint.isSuspend(); + } + + public void setSuspend(boolean suspend) { + myBreakpoint.setSuspend(suspend); + } + + public static List convert(List list) throws RemoteException { + final ArrayList breakpoints = new ArrayList(list.size()); + for (Breakpoint breakpoint : list) { + breakpoints.add(new RemoteBreakpointImpl(breakpoint)); + } + return breakpoints; + } +} diff --git a/plugins/xslt-debugger/rt/src/org/intellij/plugins/xsltDebugger/rt/engine/remote/RemoteBreakpointManager.java b/plugins/xslt-debugger/rt/src/org/intellij/plugins/xsltDebugger/rt/engine/remote/RemoteBreakpointManager.java new file mode 100644 index 000000000000..2d4b317e8453 --- /dev/null +++ b/plugins/xslt-debugger/rt/src/org/intellij/plugins/xsltDebugger/rt/engine/remote/RemoteBreakpointManager.java @@ -0,0 +1,39 @@ +/* + * 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.remote; + +import java.io.File; +import java.rmi.Remote; +import java.rmi.RemoteException; +import java.util.List; + +/** + * Created by IntelliJ IDEA. + * User: sweinreuter + * Date: 24.05.2007 + */ +public interface RemoteBreakpointManager extends Remote { + RemoteBreakpoint setBreakpoint(File file, int line) throws RemoteException; + + RemoteBreakpoint setBreakpoint(String uri, int line) throws RemoteException; + + void removeBreakpoint(String uri, int line) throws RemoteException; + + List getBreakpoints() throws RemoteException; + + RemoteBreakpoint getBreakpoint(String uri, int lineNumber) throws RemoteException; +} diff --git a/plugins/xslt-debugger/rt/src/org/intellij/plugins/xsltDebugger/rt/engine/remote/RemoteBreakpointManagerImpl.java b/plugins/xslt-debugger/rt/src/org/intellij/plugins/xsltDebugger/rt/engine/remote/RemoteBreakpointManagerImpl.java new file mode 100644 index 000000000000..15255be03195 --- /dev/null +++ b/plugins/xslt-debugger/rt/src/org/intellij/plugins/xsltDebugger/rt/engine/remote/RemoteBreakpointManagerImpl.java @@ -0,0 +1,58 @@ +/* + * 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.remote; + +import org.intellij.plugins.xsltDebugger.rt.engine.BreakpointManager; + +import javax.rmi.PortableRemoteObject; +import java.io.File; +import java.rmi.RemoteException; +import java.util.List; + +/** + * Created by IntelliJ IDEA. + * User: sweinreuter + * Date: 24.05.2007 + */ +class RemoteBreakpointManagerImpl extends PortableRemoteObject implements RemoteBreakpointManager { + private final BreakpointManager myManager; + + public RemoteBreakpointManagerImpl(BreakpointManager manager) throws RemoteException { + super(); + myManager = manager; + } + + public RemoteBreakpoint setBreakpoint(File file, int line) throws RemoteException { + return new RemoteBreakpointImpl(myManager.setBreakpoint(file, line)); + } + + public RemoteBreakpoint setBreakpoint(String uri, int line) throws RemoteException { + return new RemoteBreakpointImpl(myManager.setBreakpoint(uri, line)); + } + + public void removeBreakpoint(String uri, int line) { + myManager.removeBreakpoint(uri, line); + } + + public List getBreakpoints() throws RemoteException { + return RemoteBreakpointImpl.convert(myManager.getBreakpoints()); + } + + public RemoteBreakpoint getBreakpoint(String uri, int lineNumber) throws RemoteException { + return new RemoteBreakpointImpl(myManager.getBreakpoint(uri, lineNumber)); + } +} diff --git a/plugins/xslt-debugger/rt/src/org/intellij/plugins/xsltDebugger/rt/engine/remote/RemoteDebugger.java b/plugins/xslt-debugger/rt/src/org/intellij/plugins/xsltDebugger/rt/engine/remote/RemoteDebugger.java new file mode 100644 index 000000000000..31d875cec72d --- /dev/null +++ b/plugins/xslt-debugger/rt/src/org/intellij/plugins/xsltDebugger/rt/engine/remote/RemoteDebugger.java @@ -0,0 +1,104 @@ +/* + * 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.remote; + +import org.intellij.plugins.xsltDebugger.rt.engine.Debugger; +import org.intellij.plugins.xsltDebugger.rt.engine.OutputEventQueue; +import org.intellij.plugins.xsltDebugger.rt.engine.Value; + +import java.rmi.Remote; +import java.rmi.RemoteException; +import java.util.List; + +/** + * Created by IntelliJ IDEA. + * User: sweinreuter + * Date: 24.05.2007 + */ +public interface RemoteDebugger extends Remote { + boolean ping() throws RemoteException; + + void stop(boolean force) throws RemoteException; + + Debugger.State waitForStateChange(Debugger.State currentState) throws RemoteException; + + boolean waitForDebuggee() throws RemoteException; + + boolean start() throws RemoteException; + + void step() throws RemoteException; + + void stepInto() throws RemoteException; + + void resume() throws RemoteException; + + boolean isStopped() throws RemoteException; + + Frame getCurrentFrame() throws RemoteException; + + Frame getSourceFrame() throws RemoteException; + + Value eval(String expr) throws RemoteException, Debugger.EvaluationException; + + List getGlobalVariables() throws RemoteException; + + RemoteBreakpointManager getBreakpointManager() throws RemoteException; + + Debugger.State getState() throws RemoteException; + + void pause() throws RemoteException; + + EventQueue getEventQueue() throws RemoteException; + + interface EventQueue extends Remote { + List getEvents() throws RemoteException; + + void setEnabled(boolean b) throws RemoteException; + } + + interface Frame extends Remote { + int getLineNumber() throws RemoteException; + + String getURI() throws RemoteException; + + Frame getNext() throws RemoteException; + + Frame getPrevious() throws RemoteException; + + String getXPath() throws RemoteException; + + Value eval(String expr) throws RemoteException, Debugger.EvaluationException; + + List getVariables() throws RemoteException; + + String getInstruction() throws RemoteException; + } + + public interface Variable extends Remote { + boolean isGlobal() throws RemoteException; + + Debugger.Variable.Kind getKind() throws RemoteException; + + String getName() throws RemoteException; + + Value getValue() throws RemoteException; + + String getURI() throws RemoteException; + + int getLineNumber() throws RemoteException; + } +} diff --git a/plugins/xslt-debugger/rt/src/org/intellij/plugins/xsltDebugger/rt/engine/remote/RemoteDebuggerClient.java b/plugins/xslt-debugger/rt/src/org/intellij/plugins/xsltDebugger/rt/engine/remote/RemoteDebuggerClient.java new file mode 100644 index 000000000000..35f04d6d41a4 --- /dev/null +++ b/plugins/xslt-debugger/rt/src/org/intellij/plugins/xsltDebugger/rt/engine/remote/RemoteDebuggerClient.java @@ -0,0 +1,545 @@ +/* + * 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.remote; + +import org.intellij.plugins.xsltDebugger.rt.engine.*; + +import java.io.EOFException; +import java.io.File; +import java.io.IOException; +import java.net.SocketException; +import java.rmi.Naming; +import java.rmi.NotBoundException; +import java.rmi.RemoteException; +import java.util.ArrayList; +import java.util.List; + +/** + * Created by IntelliJ IDEA. + * User: sweinreuter + * Date: 23.05.2007 + */ +public class RemoteDebuggerClient implements Debugger { + private final RemoteDebugger myRemote; + private final BreakpointManager myBreakpointManager; + private final OutputEventQueue myEventQueue; + + public RemoteDebuggerClient(int port) throws IOException, NotBoundException { + myRemote = (RemoteDebugger)Naming.lookup("rmi://127.0.0.1:" + port + "/XsltDebugger"); + + final RemoteBreakpointManager manager = myRemote.getBreakpointManager(); + myBreakpointManager = new MyBreakpointManager(manager); + + final RemoteDebugger.EventQueue eventQueue = myRemote.getEventQueue(); + myEventQueue = new MyOutputEventQueue(eventQueue); + } + + public boolean ping() { + try { + return myRemote.ping(); + } catch (RemoteException e) { + return false; + } + } + + public State getState() { + try { + return myRemote.getState(); + } catch (RemoteException e) { + throw handleRemoteException(e); + } + } + + public void stop(boolean force) { + try { + myRemote.stop(force); + } catch (RemoteException e) { + throw handleRemoteException(e); + } + } + + static RuntimeException handleRemoteException(RemoteException e) { + Throwable t = e.getCause(); + while (t != null) { + if (t instanceof SocketException || t instanceof EOFException) { + throw new DebuggerStoppedException(); + } + t = t.getCause(); + } + return e.getCause() instanceof RuntimeException ? (RuntimeException)e.getCause() : new RuntimeException(e); + } + + public Debugger.State waitForStateChange(State state) { + try { + return myRemote.waitForStateChange(state); + } catch (RemoteException e) { + throw handleRemoteException(e); + } + } + + public boolean waitForDebuggee() { + try { + return myRemote.waitForDebuggee(); + } catch (RemoteException e) { + throw handleRemoteException(e); + } + } + + public boolean start() { + try { + return myRemote.start(); + } catch (RemoteException e) { + throw handleRemoteException(e); + } + } + + public void step() { + try { + myRemote.step(); + } catch (RemoteException e) { + throw handleRemoteException(e); + } + } + + public void stepInto() { + try { + myRemote.stepInto(); + } catch (RemoteException e) { + throw handleRemoteException(e); + } + } + + public void resume() { + try { + myRemote.resume(); + } catch (RemoteException e) { + throw handleRemoteException(e); + } + } + + public void pause() { + try { + myRemote.pause(); + } catch (RemoteException e) { + throw handleRemoteException(e); + } + } + + public boolean isStopped() { + try { + return myRemote.isStopped(); + } catch (RemoteException e) { + throw handleRemoteException(e); + } + } + + public StyleFrame getCurrentFrame() { + try { + return MyFrame.create(myRemote.getCurrentFrame()); + } catch (RemoteException e) { + throw handleRemoteException(e); + } + } + + public SourceFrame getSourceFrame() { + try { + return MySourceFrame.create(myRemote.getSourceFrame()); + } catch (RemoteException e) { + throw handleRemoteException(e); + } + } + + public Value eval(String expr) throws EvaluationException { + try { + return myRemote.eval(expr); + } catch (RemoteException e) { + throw handleRemoteException(e); + } + } + + public List getGlobalVariables() { + try { + return MyVariable.convert(myRemote.getGlobalVariables()); + } catch (RemoteException e) { + throw handleRemoteException(e); + } + } + + public BreakpointManager getBreakpointManager() { + return myBreakpointManager; + } + + public OutputEventQueue getEventQueue() { + return myEventQueue; + } + + private static class MyBreakpointManager implements BreakpointManager { + private final RemoteBreakpointManager myManager; + + public MyBreakpointManager(RemoteBreakpointManager manager) { + myManager = manager; + } + + public Breakpoint setBreakpoint(File file, int line) { + try { + return new MyBreakpoint(myManager.setBreakpoint(file, line)); + } catch (RemoteException e) { + throw handleRemoteException(e); + } + } + + public Breakpoint setBreakpoint(String uri, int line) { + try { + return new MyBreakpoint(myManager.setBreakpoint(uri, line)); + } catch (RemoteException e) { + throw handleRemoteException(e); + } + } + + public void removeBreakpoint(Breakpoint bp) { + try { + myManager.removeBreakpoint(bp.getUri(), bp.getLine()); + } catch (RemoteException e) { + throw handleRemoteException(e); + } + } + + public void removeBreakpoint(String uri, int line) { + try { + myManager.removeBreakpoint(uri, line); + } catch (RemoteException e) { + throw handleRemoteException(e); + } + } + + public List getBreakpoints() { + try { + final List list = myManager.getBreakpoints(); + final ArrayList breakpoints = new ArrayList(list.size()); + for (RemoteBreakpoint breakpoint : list) { + breakpoints.add(new MyBreakpoint(breakpoint)); + } + return breakpoints; + } catch (RemoteException e) { + throw handleRemoteException(e); + } + } + + public Breakpoint getBreakpoint(String uri, int lineNumber) { + try { + final RemoteBreakpoint breakpoint = myManager.getBreakpoint(uri, lineNumber); + return breakpoint != null ? new MyBreakpoint(breakpoint) : null; + } catch (RemoteException e) { + throw handleRemoteException(e); + } + } + + private static class MyBreakpoint implements Breakpoint { + private final RemoteBreakpoint myBreakpoint; + + public MyBreakpoint(RemoteBreakpoint breakpoint) { + myBreakpoint = breakpoint; + } + + public String getUri() { + try { + return myBreakpoint.getUri(); + } catch (RemoteException e) { + throw handleRemoteException(e); + } + } + + public int getLine() { + try { + return myBreakpoint.getLine(); + } catch (RemoteException e) { + throw handleRemoteException(e); + } + } + + public boolean isEnabled() { + try { + return myBreakpoint.isEnabled(); + } catch (RemoteException e) { + throw handleRemoteException(e); + } + } + + public String getCondition() { + try { + return myBreakpoint.getCondition(); + } catch (RemoteException e) { + throw handleRemoteException(e); + } + } + + public String getLogMessage() { + try { + return myBreakpoint.getLogMessage(); + } catch (RemoteException e) { + throw handleRemoteException(e); + } + } + + public void setCondition(String expr) { + try { + myBreakpoint.setCondition(expr); + } catch (RemoteException e) { + throw handleRemoteException(e); + } + } + + public void setEnabled(boolean enabled) { + try { + myBreakpoint.setEnabled(enabled); + } catch (RemoteException e) { + throw handleRemoteException(e); + } + } + + public void setLogMessage(String expr) { + try { + myBreakpoint.setLogMessage(expr); + } catch (RemoteException e) { + throw handleRemoteException(e); + } + } + + public String getTraceMessage() { + try { + return myBreakpoint.getTraceMessage(); + } catch (RemoteException e) { + throw handleRemoteException(e); + } + } + + public void setTraceMessage(String expr) { + try { + myBreakpoint.setTraceMessage(expr); + } catch (RemoteException e) { + throw handleRemoteException(e); + } + } + + public boolean isSuspend() { + try { + return myBreakpoint.isSuspend(); + } catch (RemoteException e) { + throw handleRemoteException(e); + } + } + + public void setSuspend(boolean suspend) { + try { + myBreakpoint.setSuspend(suspend); + } catch (RemoteException e) { + throw handleRemoteException(e); + } + } + } + } + + private static abstract class MyAbstractFrame implements Frame { + protected final RemoteDebugger.Frame myFrame; + + protected MyAbstractFrame(RemoteDebugger.Frame frame) { + myFrame = frame; + } + + public int getLineNumber() { + try { + return myFrame.getLineNumber(); + } catch (RemoteException e) { + throw handleRemoteException(e); + } + } + + public String getURI() { + try { + return myFrame.getURI(); + } catch (RemoteException e) { + throw handleRemoteException(e); + } + } + + public F getNext() { + try { + return createImpl(myFrame.getNext()); + } catch (RemoteException e) { + throw handleRemoteException(e); + } + } + + public F getPrevious() { + try { + return createImpl(myFrame.getPrevious()); + } catch (RemoteException e) { + throw handleRemoteException(e); + } + } + + protected abstract F createImpl(RemoteDebugger.Frame frame); + + public String getXPath() { + try { + return myFrame.getXPath(); + } catch (RemoteException e) { + throw handleRemoteException(e); + } + } + + public Value eval(String expr) throws EvaluationException { + try { + return myFrame.eval(expr); + } catch (RemoteException e) { + throw handleRemoteException(e); + } + } + + public List getVariables() { + try { + return MyVariable.convert(myFrame.getVariables()); + } catch (RemoteException e) { + throw handleRemoteException(e); + } + } + } + + private static class MyFrame extends MyAbstractFrame implements StyleFrame { + protected MyFrame(RemoteDebugger.Frame frame) { + super(frame); + } + + public String getInstruction() { + try { + return myFrame.getInstruction(); + } catch (RemoteException e) { + throw handleRemoteException(e); + } + } + + protected StyleFrame createImpl(RemoteDebugger.Frame frame) { + return create(frame); + } + + public static StyleFrame create(RemoteDebugger.Frame currentFrame) { + return currentFrame != null ? new MyFrame(currentFrame) : null; + } + } + + private static class MySourceFrame extends MyAbstractFrame implements SourceFrame { + protected MySourceFrame(RemoteDebugger.Frame frame) { + super(frame); + } + + public SourceFrame createImpl(RemoteDebugger.Frame frame) { + return create(frame); + } + + public static SourceFrame create(RemoteDebugger.Frame currentFrame) { + return currentFrame != null ? new MySourceFrame(currentFrame) : null; + } + } + + private static class MyVariable implements Variable { + private final RemoteDebugger.Variable myVariable; + + public MyVariable(RemoteDebugger.Variable variable) { + myVariable = variable; + } + + public String getURI() { + try { + return myVariable.getURI(); + } catch (RemoteException e) { + throw handleRemoteException(e); + } + } + + public int getLineNumber() { + try { + return myVariable.getLineNumber(); + } catch (RemoteException e) { + throw handleRemoteException(e); + } + } + + public boolean isGlobal() { + try { + return myVariable.isGlobal(); + } catch (RemoteException e) { + throw handleRemoteException(e); + } + } + + public Kind getKind() { + try { + return myVariable.getKind(); + } catch (RemoteException e) { + throw handleRemoteException(e); + } + } + + public String getName() { + try { + return myVariable.getName(); + } catch (RemoteException e) { + throw handleRemoteException(e); + } + } + + public Value getValue() { + try { + return myVariable.getValue(); + } catch (RemoteException e) { + throw handleRemoteException(e); + } + } + + static List convert(List list) { + final ArrayList variables = new ArrayList(list.size()); + for (final RemoteDebugger.Variable variable : list) { + variables.add(new MyVariable(variable)); + } + return variables; + } + } + + private static class MyOutputEventQueue implements OutputEventQueue { + private final RemoteDebugger.EventQueue myEventQueue; + + public MyOutputEventQueue(RemoteDebugger.EventQueue eventQueue) { + myEventQueue = eventQueue; + } + + public void setEnabled(boolean b) { + try { + myEventQueue.setEnabled(b); + } catch (RemoteException e) { + throw handleRemoteException(e); + } + } + + public List getEvents() { + try { + return myEventQueue.getEvents(); + } catch (RemoteException e) { + throw handleRemoteException(e); + } + } + } +} diff --git a/plugins/xslt-debugger/rt/src/org/intellij/plugins/xsltDebugger/rt/engine/remote/RemoteEventQueueImpl.java b/plugins/xslt-debugger/rt/src/org/intellij/plugins/xsltDebugger/rt/engine/remote/RemoteEventQueueImpl.java new file mode 100644 index 000000000000..9c182ba1f943 --- /dev/null +++ b/plugins/xslt-debugger/rt/src/org/intellij/plugins/xsltDebugger/rt/engine/remote/RemoteEventQueueImpl.java @@ -0,0 +1,44 @@ +/* + * Copyright 2002-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.remote; + +import org.intellij.plugins.xsltDebugger.rt.engine.OutputEventQueue; + +import javax.rmi.PortableRemoteObject; +import java.rmi.RemoteException; +import java.util.List; + +/** + * Created by IntelliJ IDEA. + * User: sweinreuter + * Date: 08.06.2007 + */ +public class RemoteEventQueueImpl extends PortableRemoteObject implements RemoteDebugger.EventQueue { + private final OutputEventQueue myQueue; + + public RemoteEventQueueImpl(OutputEventQueue queue) throws RemoteException { + myQueue = queue; + } + + public List getEvents() throws RemoteException { + return myQueue.getEvents(); + } + + public void setEnabled(boolean b) throws RemoteException { + myQueue.setEnabled(b); + } +} diff --git a/plugins/xslt-debugger/rt/src/org/intellij/plugins/xsltDebugger/rt/engine/remote/RemoteFrameImpl.java b/plugins/xslt-debugger/rt/src/org/intellij/plugins/xsltDebugger/rt/engine/remote/RemoteFrameImpl.java new file mode 100644 index 000000000000..e11056f0f89d --- /dev/null +++ b/plugins/xslt-debugger/rt/src/org/intellij/plugins/xsltDebugger/rt/engine/remote/RemoteFrameImpl.java @@ -0,0 +1,75 @@ +/* + * 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.remote; + +import org.intellij.plugins.xsltDebugger.rt.engine.Debugger; +import org.intellij.plugins.xsltDebugger.rt.engine.Debugger.Frame; +import org.intellij.plugins.xsltDebugger.rt.engine.Value; + +import javax.rmi.PortableRemoteObject; +import java.rmi.RemoteException; +import java.util.List; + +/** + * Created by IntelliJ IDEA. + * User: sweinreuter + * Date: 28.05.2007 + */ +class RemoteFrameImpl extends PortableRemoteObject implements RemoteDebugger.Frame { + private final Frame myFrame; + + private RemoteFrameImpl(Frame frame) throws RemoteException { + myFrame = frame; + } + + public int getLineNumber() { + return myFrame.getLineNumber(); + } + + public String getURI() { + return myFrame.getURI(); + } + + public RemoteDebugger.Frame getNext() throws RemoteException { + return create(myFrame.getNext()); + } + + public RemoteDebugger.Frame getPrevious() throws RemoteException { + return create(myFrame.getPrevious()); + } + + public String getXPath() throws RemoteException { + return ((Debugger.SourceFrame)myFrame).getXPath(); + } + + public ValueImpl eval(String expr) throws Debugger.EvaluationException { + final Value value = ((Debugger.StyleFrame)myFrame).eval(expr); + return new ValueImpl(value.getValue(), value.getType()); + } + + public List getVariables() throws RemoteException { + return RemoteVariableImpl.convert(((Debugger.StyleFrame)myFrame).getVariables()); + } + + public String getInstruction() throws RemoteException { + return ((Debugger.StyleFrame)myFrame).getInstruction(); + } + + public static RemoteFrameImpl create(Frame frame) throws RemoteException { + return frame != null ? new RemoteFrameImpl(frame) : null; + } +} diff --git a/plugins/xslt-debugger/rt/src/org/intellij/plugins/xsltDebugger/rt/engine/remote/RemoteVariableImpl.java b/plugins/xslt-debugger/rt/src/org/intellij/plugins/xsltDebugger/rt/engine/remote/RemoteVariableImpl.java new file mode 100644 index 000000000000..8e83c9bec1bb --- /dev/null +++ b/plugins/xslt-debugger/rt/src/org/intellij/plugins/xsltDebugger/rt/engine/remote/RemoteVariableImpl.java @@ -0,0 +1,71 @@ +/* + * 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.remote; + +import org.intellij.plugins.xsltDebugger.rt.engine.Debugger; +import org.intellij.plugins.xsltDebugger.rt.engine.Value; + +import javax.rmi.PortableRemoteObject; +import java.rmi.RemoteException; +import java.util.ArrayList; +import java.util.List; + +/** + * Created by IntelliJ IDEA. + * User: sweinreuter + * Date: 28.05.2007 + */ +class RemoteVariableImpl extends PortableRemoteObject implements RemoteDebugger.Variable { + private final Debugger.Variable myVariable; + + public RemoteVariableImpl(Debugger.Variable variable) throws RemoteException { + myVariable = variable; + } + + public String getURI() throws RemoteException { + return myVariable.getURI(); + } + + public int getLineNumber() throws RemoteException { + return myVariable.getLineNumber(); + } + + public boolean isGlobal() { + return myVariable.isGlobal(); + } + + public Debugger.Variable.Kind getKind() throws RemoteException { + return myVariable.getKind(); + } + + public String getName() { + return myVariable.getName(); + } + + public Value getValue() { + final Value value = myVariable.getValue(); + return new ValueImpl(value.getValue(), value.getType()); + } + + static List convert(List list) throws RemoteException { + final ArrayList variables = new ArrayList(list.size()); + for (final Debugger.Variable variable : list) { + variables.add(new RemoteVariableImpl(variable)); + } + return variables; + } +} diff --git a/plugins/xslt-debugger/rt/src/org/intellij/plugins/xsltDebugger/rt/engine/remote/ValueImpl.java b/plugins/xslt-debugger/rt/src/org/intellij/plugins/xsltDebugger/rt/engine/remote/ValueImpl.java new file mode 100644 index 000000000000..4862ab3bfca3 --- /dev/null +++ b/plugins/xslt-debugger/rt/src/org/intellij/plugins/xsltDebugger/rt/engine/remote/ValueImpl.java @@ -0,0 +1,48 @@ +/* + * 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.remote; + +import org.intellij.plugins.xsltDebugger.rt.engine.Value; + +import java.io.Serializable; + +/** + * Created by IntelliJ IDEA. + * User: sweinreuter + * Date: 28.05.2007 + */ +class ValueImpl implements Value { + private final Serializable myValue; + private final Type myType; + + public ValueImpl(Object value, Type type) { + if (value instanceof Serializable) { + myValue = (Serializable)value; + } else { + myValue = String.valueOf(value); + } + myType = type; + } + + public Serializable getValue() { + return myValue; + } + + public Type getType() { + return myType; + } +} diff --git a/plugins/xslt-debugger/rt/xslt-debugger-rt.iml b/plugins/xslt-debugger/rt/xslt-debugger-rt.iml new file mode 100644 index 000000000000..f78948eebb0c --- /dev/null +++ b/plugins/xslt-debugger/rt/xslt-debugger-rt.iml @@ -0,0 +1,52 @@ + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + diff --git a/plugins/xslt-debugger/src/META-INF/plugin.xml b/plugins/xslt-debugger/src/META-INF/plugin.xml new file mode 100644 index 000000000000..2966ef8f136c --- /dev/null +++ b/plugins/xslt-debugger/src/META-INF/plugin.xml @@ -0,0 +1,32 @@ + + XSLT-Debugger + XSLT-Debugger + + + + 1.4 + Sascha Weinreuter + + + XPathView + + + Xalan Support. To debug with Xalan instead of SAXON, a supported Xalan version (2.7.x) should be present in the + classpath or "-Dxslt.transformer.type=xalan" should be added as VM argument for the run configuration. Debugging + with SAXON can be forced by adding "-Dxslt.transformer.type=saxon". + + + + + + + + + + + + + + diff --git a/plugins/xslt-debugger/src/org/intellij/plugins/xsltDebugger/BreakpointContext.java b/plugins/xslt-debugger/src/org/intellij/plugins/xsltDebugger/BreakpointContext.java new file mode 100644 index 000000000000..7cc43e2e3fbf --- /dev/null +++ b/plugins/xslt-debugger/src/org/intellij/plugins/xsltDebugger/BreakpointContext.java @@ -0,0 +1,53 @@ +package org.intellij.plugins.xsltDebugger; + +import com.intellij.codeInsight.intention.IntentionAction; +import com.intellij.psi.PsiElement; +import com.intellij.psi.PsiFile; +import com.intellij.psi.util.PsiTreeUtil; +import com.intellij.psi.xml.XmlElement; +import com.intellij.psi.xml.XmlTag; +import org.intellij.lang.xpath.XPathFile; +import org.intellij.lang.xpath.context.VariableContext; +import org.intellij.lang.xpath.context.XPathQuickFixFactoryImpl; +import org.intellij.lang.xpath.psi.XPathElement; +import org.intellij.lang.xpath.psi.XPathVariableReference; +import org.intellij.lang.xpath.validation.inspections.quickfix.XPathQuickFixFactory; +import org.intellij.lang.xpath.xslt.context.XsltContextProvider; +import org.intellij.lang.xpath.xslt.context.XsltVariableContext; +import org.jetbrains.annotations.NotNull; +import org.jetbrains.annotations.Nullable; + +public class BreakpointContext extends XsltContextProvider { + public BreakpointContext(PsiElement contextElement) { + super((XmlElement)contextElement); + } + + @Override + @NotNull + public XPathQuickFixFactory getQuickFixFactory() { + return XPathQuickFixFactoryImpl.INSTANCE; + } + + @Override + public PsiFile[] getRelatedFiles(XPathFile file) { + return PsiFile.EMPTY_ARRAY; + } + + @Override + @NotNull + public VariableContext getVariableContext() { + return new XsltVariableContext() { + @Override + @Nullable + protected XmlTag getContextTagImpl(XPathElement element) { + return PsiTreeUtil.getParentOfType(getContextElement(), XmlTag.class, false); + } + + @Override + @NotNull + public IntentionAction[] getUnresolvedVariableFixes(XPathVariableReference reference) { + return IntentionAction.EMPTY_ARRAY; + } + }; + } +} diff --git a/plugins/xslt-debugger/src/org/intellij/plugins/xsltDebugger/DebugProcessListener.java b/plugins/xslt-debugger/src/org/intellij/plugins/xsltDebugger/DebugProcessListener.java new file mode 100644 index 000000000000..a4a20d8ff593 --- /dev/null +++ b/plugins/xslt-debugger/src/org/intellij/plugins/xsltDebugger/DebugProcessListener.java @@ -0,0 +1,68 @@ +/* + * 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; + +import com.intellij.execution.process.ProcessAdapter; +import com.intellij.execution.process.ProcessEvent; +import com.intellij.openapi.application.ApplicationManager; +import com.intellij.openapi.project.Project; +import org.intellij.plugins.xsltDebugger.rt.engine.DebuggerStoppedException; + +/** + * ProcessListener that manages the connection to the debugged XSLT-process + */ +class DebugProcessListener extends ProcessAdapter { + private final Project myProject; + private final int myPort; + + public DebugProcessListener(Project project, int port) { + myProject = project; + myPort = port; + } + + @Override + public void startNotified(ProcessEvent event) { + final DebuggerConnector connector = new DebuggerConnector(myProject, event.getProcessHandler(), myPort); + ApplicationManager.getApplication().executeOnPooledThread(connector); + } + + @Override + public void processWillTerminate(ProcessEvent event, boolean willBeDestroyed) { + try { + final XsltDebuggerSession session = XsltDebuggerSession.getInstance(event.getProcessHandler()); + if (session != null) { + session.stop(); + } + } catch (VMPausedException e) { + // VM is paused, no way for a "clean" shutdown + } catch (DebuggerStoppedException e) { + // OK + } + + super.processWillTerminate(event, willBeDestroyed); + } + + @Override + public void processTerminated(ProcessEvent event) { + super.processTerminated(event); + + final XsltDebuggerSession session = XsltDebuggerSession.getInstance(event.getProcessHandler()); + if (session != null) { + session.close(); + } + } +} diff --git a/plugins/xslt-debugger/src/org/intellij/plugins/xsltDebugger/DebuggerConnector.java b/plugins/xslt-debugger/src/org/intellij/plugins/xsltDebugger/DebuggerConnector.java new file mode 100644 index 000000000000..3d7831798c3c --- /dev/null +++ b/plugins/xslt-debugger/src/org/intellij/plugins/xsltDebugger/DebuggerConnector.java @@ -0,0 +1,130 @@ +/* + * 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; + +import com.intellij.execution.process.ProcessHandler; +import com.intellij.execution.process.ProcessOutputTypes; +import com.intellij.openapi.diagnostic.Logger; +import com.intellij.openapi.project.Project; +import org.intellij.plugins.xsltDebugger.impl.XsltDebugProcess; +import org.intellij.plugins.xsltDebugger.rt.engine.Debugger; +import org.intellij.plugins.xsltDebugger.rt.engine.OutputEventQueue; +import org.intellij.plugins.xsltDebugger.rt.engine.remote.RemoteDebuggerClient; +import org.intellij.plugins.xsltDebugger.ui.StructureTabComponent; +import org.jetbrains.annotations.Nullable; + +import java.io.IOException; +import java.rmi.ConnectException; +import java.rmi.NotBoundException; + +/** + * Establishes the debugger-connection to the started XSLT-process, starts the debugger-session + * and attaches the debugger-UI. + */ +class DebuggerConnector implements Runnable { + + private final Project myProject; + private final ProcessHandler myProcess; + private final int myPort; + + public DebuggerConnector(Project project, ProcessHandler process, int port) { + myProject = project; + myProcess = process; + myPort = port; + } + + public void run() { + final Debugger client = connect(); + if (client == null) { + // client will be null if the process terminated prematurely for some reason. no need for an error message + if (!myProcess.isProcessTerminated()) { + myProcess.notifyTextAvailable("Failed to connect to debugged process. Terminating.\n", ProcessOutputTypes.SYSTEM); + myProcess.destroyProcess(); + } + return; + } + + final XsltDebuggerSession session = XsltDebuggerSession.create(myProject, myProcess, client); + + final XsltDebugProcess dbgp = XsltDebugProcess.getInstance(myProcess); + assert dbgp != null; + dbgp.init(client); + + session.addListener(new XsltDebuggerSession.Listener() { + @Override + public void debuggerSuspended() { + final OutputEventQueue queue = client.getEventQueue(); + StructureTabComponent.getInstance(myProcess).getEventModel().update(queue.getEvents()); + } + + @Override + public void debuggerResumed() { + } + + @Override + public void debuggerStopped() { + try { + final OutputEventQueue queue = client.getEventQueue(); + StructureTabComponent.getInstance(myProcess).getEventModel().finalUpdate(queue.getEvents()); + } catch (Exception e) { + // can fail when debugger is manually terminated + } + } + }); + + session.start(); + } + + @Nullable + private Debugger connect() { + Throwable lastException = null; + for (int i = 0; i < 10; i++) { + try { + final Debugger realClient = EDTGuard.create(new RemoteDebuggerClient(myPort), myProcess); + myProcess.notifyTextAvailable("Connected to XSLT debugger on port " + myPort + "\n", ProcessOutputTypes.SYSTEM); + return realClient; + } catch (ConnectException e) { + lastException = e; + try { + Thread.sleep(500); + } catch (InterruptedException e1) { + break; + } + } catch (NotBoundException e) { + lastException = e; + try { + Thread.sleep(200); + } catch (InterruptedException e1) { + break; + } + } catch (IOException e) { + lastException = e; + break; + } + } + + if (lastException != null) { + Logger.getInstance(getClass().getName()).info("Could not connect to debugger", lastException); + + if (lastException.getMessage() != null) { + myProcess.notifyTextAvailable("Connection error: " + lastException.getMessage() + "\n", ProcessOutputTypes.SYSTEM); + } + } + + return null; + } +} diff --git a/plugins/xslt-debugger/src/org/intellij/plugins/xsltDebugger/EDTGuard.java b/plugins/xslt-debugger/src/org/intellij/plugins/xsltDebugger/EDTGuard.java new file mode 100644 index 000000000000..8e68a6f648b7 --- /dev/null +++ b/plugins/xslt-debugger/src/org/intellij/plugins/xsltDebugger/EDTGuard.java @@ -0,0 +1,294 @@ +/* + * Copyright 2002-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; + +import com.intellij.execution.process.ProcessAdapter; +import com.intellij.execution.process.ProcessEvent; +import com.intellij.execution.process.ProcessHandler; +import com.intellij.openapi.Disposable; +import com.intellij.openapi.util.Disposer; +import com.intellij.openapi.util.Pair; +import com.intellij.util.Alarm; +import gnu.trove.THashMap; +import gnu.trove.TObjectHashingStrategy; +import org.intellij.plugins.xsltDebugger.rt.engine.Watchable; +import org.jetbrains.annotations.NotNull; +import org.jetbrains.annotations.Nullable; + +import javax.swing.*; +import java.io.Serializable; +import java.lang.reflect.InvocationHandler; +import java.lang.reflect.InvocationTargetException; +import java.lang.reflect.Method; +import java.lang.reflect.Proxy; +import java.util.*; +import java.util.concurrent.LinkedBlockingQueue; +import java.util.concurrent.TimeUnit; +import java.util.concurrent.atomic.AtomicBoolean; + +/** + * Utility class that tries to prevent hanging the whole IDE because some call on the EDT waits too long on the debugged + * VM, which can especially happen when the VM has been paused (either manually or by a breakpoint on Java code). + *

+ * This is just the second best solution though as it would be better to avoid any interaction with the debuggee on the + * EDT, but, at least right now, it seems to be more reliable against bad surprises. + */ +class EDTGuard implements InvocationHandler { + // maximum time to wait for a result on the EDT + private static final long MAX_TIMEOUT = 10 * 1000; + + @SuppressWarnings({ "unchecked" }) + private final Map myInstanceCache = new THashMap(TObjectHashingStrategy.IDENTITY); + + private final Object myTarget; + + private final Pair, LinkedBlockingQueue> myQueue; + private final AtomicBoolean myPausedRef; + + private EDTGuard(Object target, + Pair, LinkedBlockingQueue> queue, + AtomicBoolean ref) { + myTarget = target; + myQueue = queue; + myPausedRef = ref; + } + + @Nullable + public Object invoke(Object proxy, @NotNull Method method, Object[] args) throws Throwable { + if (SwingUtilities.isEventDispatchThread()) { + return invokeAsync(method, args); + } + + return invoke(method, args); + } + + @Nullable + private Object invokeAsync(Method method, Object[] args) throws Throwable { + final Call call = new Call(method, args); + + if (!myQueue.first.offer(call)) { + throw new VMPausedException(); + } + + Call.Result result; + final long start = System.currentTimeMillis(); + do { + do { + result = myQueue.second.poll(200, TimeUnit.MILLISECONDS); + + if (myPausedRef.get() || result == null && System.currentTimeMillis() - start > MAX_TIMEOUT) { + throw new VMPausedException(); + } + } + while (result == null); + } + while (!result.isFromCall(call)); + + return result.getValue(); + } + + @Nullable + private Object invoke(@NotNull Method method, Object[] args) throws Throwable { + try { + return convert(method.invoke(myTarget, args)); + } catch (InvocationTargetException e) { + final Throwable t = e.getTargetException(); + if (t instanceof RuntimeException) { + throw (RuntimeException)t; + } else if (t instanceof Error) { + throw (Error)t; + } else { + throw t; + } + } + } + + @Nullable + @SuppressWarnings({ "unchecked" }) + private Object convert(@Nullable Object o) { + if (o != null && !(o instanceof Serializable)) { + synchronized (myInstanceCache) { + Object instance = myInstanceCache.get(o); + if (instance == null) { + final ClassLoader loader = o.getClass().getClassLoader(); + final Class[] interfaces = o.getClass().getInterfaces(); + final EDTGuard guard = new EDTGuard(o, myQueue, myPausedRef); + + myInstanceCache.put(o, instance = Proxy.newProxyInstance(loader, interfaces, guard)); + } + return instance; + } + } else if (o instanceof List) { + final List list = (List)o; + for (int i = 0; i < list.size(); i++) { + final Object e = list.remove(i); + list.add(i, convert(e)); + } + } else if (o instanceof Set) { + final Set set = (Set)o; + final List s2 = new ArrayList(); + for (Iterator iterator = set.iterator(); iterator.hasNext(); ) { + Object o1 = iterator.next(); + final Object o2 = convert(o1); + if (o1 != o2) { + iterator.remove(); + s2.add(o2); + } + } + set.addAll(s2); + } + return o; + } + + @NotNull + public static T create(@NotNull final O target, final ProcessHandler process) { + final Pair, LinkedBlockingQueue> queue = + Pair.create(new LinkedBlockingQueue(10), new LinkedBlockingQueue()); + + final Thread thread = new Thread("Async Invocation Thread for " + process) { + @Override + public void run() { + try { + while (!Thread.currentThread().isInterrupted()) { + final Call call = queue.first.take(); + if (call != null) { + queue.second.offer(call.invoke()); + } + } + } catch (InterruptedException e) { + // break + } + } + }; + thread.start(); + + final AtomicBoolean ref = new AtomicBoolean(); + final Disposable d = new Disposable() { + boolean disposed; + + @Override + public void dispose() { + if (!disposed) { + disposed = true; + + ref.set(true); + thread.interrupt(); + } + } + }; + process.addProcessListener(new ProcessAdapter() { + @Override + public void processTerminated(ProcessEvent event) { + synchronized (d) { + Disposer.dispose(d); + } + } + + @Override + public void processWillTerminate(ProcessEvent event, boolean willBeDestroyed) { + if (!willBeDestroyed) { + synchronized (d) { + Disposer.dispose(d); + } + } + } + }); + + final Alarm alarm = new Alarm(Alarm.ThreadToUse.SHARED_THREAD, d); + final Alarm alarm2 = new Alarm(Alarm.ThreadToUse.OWN_THREAD, alarm); + + final Runnable watchdog = new Runnable() { + @Override + public void run() { + ref.set(true); + } + }; + + final Runnable ping = new Runnable() { + @Override + public void run() { + synchronized (d) { + if (alarm.isDisposed()) { + return; + } + + alarm2.addRequest(watchdog, 200); + try { + ref.set(!target.ping()); + } catch (Exception e) { + ref.set(true); + } finally { + alarm2.cancelRequest(watchdog); + } + } + } + }; + alarm.addRequest(ping, 500); + + final EDTGuard guard = new EDTGuard(target, queue, ref); + final ClassLoader classLoader = target.getClass().getClassLoader(); + final Class[] interfaces = target.getClass().getInterfaces(); + //noinspection unchecked + return (T)Proxy.newProxyInstance(classLoader, interfaces, guard); + } + + class Call { + private final Method myMethod; + private final Object[] myArguments; + + class Result { + private final Object myObject; + private final Throwable myThrowable; + + public Result(Object o) { + myObject = o; + myThrowable = null; + } + + public Result(Throwable o) { + myObject = null; + myThrowable = o; + } + + public boolean isFromCall(Call call) { + return call == Call.this; + } + + @Nullable + public Object getValue() throws Throwable { + if (myThrowable != null) { + throw myThrowable; + } + return myObject; + } + } + + public Call(Method method, Object[] arguments) { + myMethod = method; + myArguments = arguments; + } + + @NotNull + public Result invoke() { + try { + return new Result(EDTGuard.this.invoke(myMethod, myArguments)); + } catch (Throwable e) { + return new Result(e); + } + } + } +} diff --git a/plugins/xslt-debugger/src/org/intellij/plugins/xsltDebugger/VMPausedException.java b/plugins/xslt-debugger/src/org/intellij/plugins/xsltDebugger/VMPausedException.java new file mode 100644 index 000000000000..90c6ed935d3a --- /dev/null +++ b/plugins/xslt-debugger/src/org/intellij/plugins/xsltDebugger/VMPausedException.java @@ -0,0 +1,23 @@ +/* + * Copyright 2002-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; + +/** + * Thrown by the EDTGuard when some method cannot be completed due to the target VM being paused. + */ +public class VMPausedException extends RuntimeException { +} diff --git a/plugins/xslt-debugger/src/org/intellij/plugins/xsltDebugger/XsltBreakpointType.java b/plugins/xslt-debugger/src/org/intellij/plugins/xsltDebugger/XsltBreakpointType.java new file mode 100644 index 000000000000..dbfb1b09581e --- /dev/null +++ b/plugins/xslt-debugger/src/org/intellij/plugins/xsltDebugger/XsltBreakpointType.java @@ -0,0 +1,71 @@ +package org.intellij.plugins.xsltDebugger; + +import com.intellij.openapi.editor.Document; +import com.intellij.openapi.fileEditor.FileDocumentManager; +import com.intellij.openapi.fileTypes.FileType; +import com.intellij.openapi.fileTypes.StdFileTypes; +import com.intellij.openapi.project.Project; +import com.intellij.openapi.vfs.VirtualFile; +import com.intellij.psi.PsiDocumentManager; +import com.intellij.psi.PsiFile; +import com.intellij.xdebugger.breakpoints.XBreakpointProperties; +import com.intellij.xdebugger.breakpoints.XLineBreakpointType; +import com.intellij.xdebugger.evaluation.XDebuggerEditorsProvider; +import com.intellij.xdebugger.ui.DebuggerIcons; +import org.intellij.lang.xpath.xslt.XsltSupport; +import org.intellij.plugins.xsltDebugger.impl.XsltDebuggerEditorsProvider; +import org.jetbrains.annotations.NotNull; + +import javax.swing.*; + +/* +* Created by IntelliJ IDEA. +* User: sweinreuter +* Date: 03.03.11 +*/ +public final class XsltBreakpointType extends XLineBreakpointType { + + private final XsltDebuggerEditorsProvider myMyEditorsProvider = new XsltDebuggerEditorsProvider(); + + public XsltBreakpointType() { + super("xslt", "XSLT Breakpoints"); + } + + @Override + public boolean canPutAt(@NotNull VirtualFile file, int line, @NotNull Project project) { + final Document document = FileDocumentManager.getInstance().getDocument(file); + if (document == null) return false; + + final PsiFile psiFile = PsiDocumentManager.getInstance(project).getPsiFile(document); + if (psiFile == null) { + return false; + } + final FileType fileType = psiFile.getFileType(); + if (fileType != StdFileTypes.XML || !XsltSupport.isXsltFile(psiFile)) { + return false; + } + return true; + } + + @Override + public XDebuggerEditorsProvider getEditorsProvider() { + return myMyEditorsProvider; + } + + @NotNull + @Override + public Icon getEnabledIcon() { + return XsltSupport.createXsltIcon(DebuggerIcons.ENABLED_BREAKPOINT_ICON); + } + + @NotNull + @Override + public Icon getDisabledIcon() { + return XsltSupport.createXsltIcon(DebuggerIcons.DISABLED_BREAKPOINT_ICON); + } + + @Override + public XBreakpointProperties createBreakpointProperties(@NotNull VirtualFile file, int line) { + return null; + } +} diff --git a/plugins/xslt-debugger/src/org/intellij/plugins/xsltDebugger/XsltDebuggerExtension.java b/plugins/xslt-debugger/src/org/intellij/plugins/xsltDebugger/XsltDebuggerExtension.java new file mode 100644 index 000000000000..275ff940618d --- /dev/null +++ b/plugins/xslt-debugger/src/org/intellij/plugins/xsltDebugger/XsltDebuggerExtension.java @@ -0,0 +1,273 @@ +/* + * Copyright 2002-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; + +import com.intellij.compiler.impl.CompilerUtil; +import com.intellij.diagnostic.logging.AdditionalTabComponent; +import com.intellij.execution.CantRunException; +import com.intellij.execution.configurations.AdditionalTabComponentManager; +import com.intellij.execution.configurations.SimpleJavaParameters; +import com.intellij.execution.process.ProcessHandler; +import com.intellij.execution.process.ProcessListener; +import com.intellij.ide.plugins.IdeaPluginDescriptor; +import com.intellij.ide.plugins.PluginManager; +import com.intellij.openapi.application.PathManager; +import com.intellij.openapi.application.ex.ApplicationManagerEx; +import com.intellij.openapi.diagnostic.Logger; +import com.intellij.openapi.extensions.PluginId; +import com.intellij.openapi.project.Project; +import com.intellij.openapi.projectRoots.Sdk; +import com.intellij.openapi.util.IconLoader; +import com.intellij.openapi.util.Key; +import com.intellij.openapi.util.UserDataHolder; +import com.intellij.openapi.vfs.JarFileSystem; +import com.intellij.openapi.vfs.VirtualFile; +import com.intellij.psi.PsiManager; +import com.intellij.util.Icons; +import com.intellij.util.net.NetUtils; +import com.intellij.xdebugger.impl.ui.DebuggerSessionTabBase; +import org.intellij.lang.xpath.xslt.XsltSupport; +import org.intellij.lang.xpath.xslt.impl.XsltChecker; +import org.intellij.lang.xpath.xslt.run.XsltRunConfiguration; +import org.intellij.lang.xpath.xslt.run.XsltRunnerExtension; +import org.intellij.plugins.xsltDebugger.ui.OutputTabComponent; +import org.intellij.plugins.xsltDebugger.ui.StructureTabComponent; +import org.jetbrains.annotations.NonNls; +import org.jetbrains.annotations.Nullable; + +import java.io.File; +import java.io.IOException; +import java.util.List; +import java.util.jar.Attributes; +import java.util.jar.Manifest; + +/** + * Extension for XPathView that hooks into the execution of XSLT-scripts. Manages the creation of the XSLT-Debugger UI + * and ensures the debugged process is started with the required JVM properties. + */ +public class XsltDebuggerExtension extends XsltRunnerExtension { + private static final Logger LOG = Logger.getInstance(XsltDebuggerExtension.class.getName()); + + private static final Key PORT = Key.create("PORT"); + private static final Key MANIFEST = Key.create("MANIFEST"); + + @NonNls + private static final String SAXON_6_JAR = "saxon.jar"; + + @NonNls + private static final String SAXON_9_JAR = "saxon9he.jar"; + + protected boolean supports(XsltRunConfiguration config, boolean debugger) { + return (debugger || XsltDebuggerRunner.ACTIVE.get() == Boolean.TRUE) && + config.getOutputType() == XsltRunConfiguration.OutputType.CONSOLE; // ? + } + + public ProcessListener createProcessListener(Project project, UserDataHolder extensionData) { + final Integer port = extensionData.getUserData(PORT); + assert port != null; + return new DebugProcessListener(project, port); + } + + public boolean createTabs(Project project, + AdditionalTabComponentManager manager, + AdditionalTabComponent outputConsole, + ProcessHandler process) { + if (manager instanceof DebuggerSessionTabBase) { + final DebuggerSessionTabBase mgr = (DebuggerSessionTabBase)manager; + mgr.addAdditionalTabComponent(new OutputTabComponent(outputConsole), "XSLT-Output", IconLoader.getIcon("/debugger/console.png")); + mgr.addAdditionalTabComponent(StructureTabComponent.create(process, outputConsole), "XSLT-Structure", Icons.FLATTEN_PACKAGES_ICON); + } else { + manager.addAdditionalTabComponent(new OutputTabComponent(outputConsole), "XSLT-Output"); + manager.addAdditionalTabComponent(StructureTabComponent.create(process, outputConsole), "XSLT-Structure"); + } + return true; + } + + public void patchParameters(final SimpleJavaParameters parameters, XsltRunConfiguration configuration, UserDataHolder extensionData) + throws CantRunException { + final XsltRunConfiguration.OutputType outputType = configuration.getOutputType(); + + final Sdk jdk = configuration.getEffectiveJDK(); + assert jdk != null; + + final String ver = jdk.getVersionString(); + if (!(CompilerUtil.isOfVersion(ver, "1.5") || + CompilerUtil.isOfVersion(ver, "5.0") || + CompilerUtil.isOfVersion(ver, "1.6") || + CompilerUtil.isOfVersion(ver, "1.7"))) { + + throw new CantRunException("The XSLT Debugger can only be used with JDK 1.5+"); + } + + // TODO: fix and remove + if (outputType != XsltRunConfiguration.OutputType.CONSOLE) { + throw new CantRunException("XSLT Debugger requires Output Type == CONSOLE"); + } + + try { + final int port = NetUtils.findAvailableSocketPort(); + parameters.getVMParametersList().defineProperty("xslt.debugger.port", String.valueOf(port)); + extensionData.putUserData(PORT, port); + } catch (IOException e) { + LOG.info(e); + throw new CantRunException("Unable to find a free network port"); + } + + final char c = File.separatorChar; + + final PluginId pluginId = PluginManager.getPluginByClassName(getClass().getName()); + assert pluginId != null || System.getProperty("xslt-debugger.plugin.path") != null; + + final File pluginPath; + if (pluginId != null) { + final IdeaPluginDescriptor descriptor = PluginManager.getPlugin(pluginId); + assert descriptor != null; + pluginPath = descriptor.getPath(); + } else { + pluginPath = new File(System.getProperty("xslt-debugger.plugin.path")); + } + + File rtClasspath = new File(pluginPath, "lib" + c + "xslt-debugger-engine.jar"); + if (rtClasspath.exists()) { + parameters.getClassPath().addTail(rtClasspath.getAbsolutePath()); + } else { + if (!(rtClasspath = new File(pluginPath, "classes")).exists()) { + if (ApplicationManagerEx.getApplicationEx().isInternal() && new File(pluginPath, "org").exists()) { + rtClasspath = pluginPath; + } else { + throw new CantRunException("Runtime classes not found"); + } + } + + parameters.getClassPath().addTail(rtClasspath.getAbsolutePath()); + parameters.getClassPath().addTail(new File(rtClasspath, "rmi-stubs.jar").getAbsolutePath()); + } + + File trove4j = new File(PathManager.getLibPath() + c + "trove4j.jar"); + if (!trove4j.exists()) { + trove4j = new File(PathManager.getHomePath() + c + "community" + c + "lib" + c + "trove4j.jar"); + } + parameters.getClassPath().addTail(trove4j.getAbsolutePath()); + + final String type = parameters.getVMParametersList().getPropertyValue("xslt.transformer.type"); + if ("saxon".equalsIgnoreCase(type)) { + addSaxon(parameters, pluginPath, SAXON_6_JAR); + } else if ("saxon9".equalsIgnoreCase(type)) { + addSaxon(parameters, pluginPath, SAXON_9_JAR); + } else if ("xalan".equalsIgnoreCase(type)) { + final Boolean xalanPresent = isValidXalanPresent(parameters); + if (xalanPresent == null) { + addXalan(parameters, pluginPath); + } else if (!xalanPresent) { + throw new CantRunException("Unsupported Xalan version is present in classpath."); + } + } else if (type != null) { + throw new CantRunException("Unsupported Transformer type '" + type + "'"); + } else if (parameters.getClassPath().getPathsString().toLowerCase().contains("xalan")) { + if (isValidXalanPresent(parameters) == Boolean.TRUE) { + parameters.getVMParametersList().defineProperty("xslt.transformer.type", "xalan"); + } + } + if (!parameters.getVMParametersList().hasProperty("xslt.transformer.type")) { + // add saxon for backward-compatibility + final VirtualFile xsltFile = configuration.findXsltFile(); + final PsiManager psiManager = PsiManager.getInstance(configuration.getProject()); + if (xsltFile != null && XsltSupport.getXsltLanguageLevel(psiManager.findFile(xsltFile)) == XsltChecker.LanguageLevel.V2) + { + parameters.getVMParametersList().defineProperty("xslt.transformer.type", "saxon9"); + addSaxon(parameters, pluginPath, SAXON_9_JAR); + } else { + parameters.getVMParametersList().defineProperty("xslt.transformer.type", "saxon"); + addSaxon(parameters, pluginPath, SAXON_6_JAR); + } + } + + parameters.getVMParametersList().defineProperty("xslt.main", "org.intellij.plugins.xsltDebugger.rt.XSLTDebuggerMain"); + } + + @Nullable + private static Boolean isValidXalanPresent(SimpleJavaParameters parameters) { + final List files = parameters.getClassPath().getVirtualFiles(); + for (VirtualFile file : files) { + if (file.getName().matches(".*xalan.*\\.jar")) { + final VirtualFile root = JarFileSystem.getInstance().getJarRootForLocalFile(file); + final VirtualFile manifestFile = root != null ? root.findFileByRelativePath("META-INF/MANIFEST.MF") : null; + if (manifestFile != null) { + try { + Manifest manifest = manifestFile.getUserData(MANIFEST); + if (manifest == null) { + manifest = new Manifest(manifestFile.getInputStream()); + manifestFile.putUserData(MANIFEST, manifest); + } + Attributes attributes = manifest.getAttributes("org/apache/xalan/"); + if (attributes == null) { + attributes = manifest.getAttributes("org/apache/xalan"); + } + if (attributes == null) { + LOG.info("No manifest attributes for 'org/apache/xalan/' in " + manifestFile.getPresentableUrl()); + continue; + } + final String version = attributes.getValue("Implementation-Version"); + if (version != null) { + final String[] parts = version.split("\\."); + if (parts.length >= 2) { + if (Integer.parseInt(parts[0]) >= 2 && Integer.parseInt(parts[1]) >= 6) { + return true; + } + } + LOG.info("Unsupported Xalan version: " + version); + } else { + LOG.info("No Xalan version information in " + file.getPath()); + } + } catch (IOException e) { + LOG.warn("Unable to read manifest from " + file.getName(), e); + } + } else { + LOG.info("No manifest file in " + file.getPath()); + } + return false; + } + } + + return null; + } + + private static void addXalan(SimpleJavaParameters parameters, File pluginPath) { + final char c = File.separatorChar; + File xalan = new File(pluginPath, "lib" + c + "rt" + c + "xalan.jar"); + if (!xalan.exists()) { + if (!(xalan = new File(pluginPath, "lib" + c + "xalan.jar")).exists()) { + xalan = new File(pluginPath, "xalan.jar"); + assert xalan.exists(); + } + } + parameters.getClassPath().addTail(xalan.getAbsolutePath()); + parameters.getClassPath().addTail(new File(xalan.getParentFile(), "serializer.jar").getAbsolutePath()); + } + + private static void addSaxon(SimpleJavaParameters parameters, File pluginPath, final String saxonJar) { + final char c = File.separatorChar; + File saxon = new File(pluginPath, "lib" + c + "rt" + c + saxonJar); + if (!saxon.exists()) { + if (!(saxon = new File(pluginPath, "lib" + c + saxonJar)).exists()) { + saxon = new File(pluginPath, saxonJar); + assert saxon.exists(); + } + } + parameters.getClassPath().addTail(saxon.getAbsolutePath()); + } +} diff --git a/plugins/xslt-debugger/src/org/intellij/plugins/xsltDebugger/XsltDebuggerRunner.java b/plugins/xslt-debugger/src/org/intellij/plugins/xsltDebugger/XsltDebuggerRunner.java new file mode 100644 index 000000000000..f683c03a5acc --- /dev/null +++ b/plugins/xslt-debugger/src/org/intellij/plugins/xsltDebugger/XsltDebuggerRunner.java @@ -0,0 +1,84 @@ +package org.intellij.plugins.xsltDebugger; + +import com.intellij.execution.ExecutionException; +import com.intellij.execution.ExecutionResult; +import com.intellij.execution.Executor; +import com.intellij.execution.configurations.JavaParameters; +import com.intellij.execution.configurations.RunProfile; +import com.intellij.execution.configurations.RunProfileState; +import com.intellij.execution.configurations.RunnerSettings; +import com.intellij.execution.runners.ExecutionEnvironment; +import com.intellij.execution.runners.JavaPatchableProgramRunner; +import com.intellij.execution.ui.RunContentDescriptor; +import com.intellij.openapi.fileEditor.FileDocumentManager; +import com.intellij.openapi.project.Project; +import com.intellij.xdebugger.XDebugProcess; +import com.intellij.xdebugger.XDebugProcessStarter; +import com.intellij.xdebugger.XDebugSession; +import com.intellij.xdebugger.XDebuggerManager; +import org.intellij.lang.xpath.xslt.run.XsltRunConfiguration; +import org.intellij.plugins.xsltDebugger.impl.XsltDebugProcess; +import org.jetbrains.annotations.NonNls; +import org.jetbrains.annotations.NotNull; + +/* +* Created by IntelliJ IDEA. +* User: sweinreuter +* Date: 16.11.10 +*/ +public class XsltDebuggerRunner extends JavaPatchableProgramRunner { + static final ThreadLocal ACTIVE = new ThreadLocal(); + + @NonNls + private static final String ID = "XsltDebuggerRunner"; + + + @NotNull + @Override + public String getRunnerId() { + return ID; + } + + @Override + public boolean canRun(@NotNull String executorId, @NotNull RunProfile profile) { + return executorId.equals("Debug") && profile instanceof XsltRunConfiguration; + } + + @Override + protected RunContentDescriptor doExecute(Project project, + Executor executor, + RunProfileState state, + RunContentDescriptor contentToReuse, + ExecutionEnvironment env) throws ExecutionException { + FileDocumentManager.getInstance().saveAllDocuments(); + return createContentDescriptor(project, executor, state, contentToReuse, env); + } + + @Override + public void patch(JavaParameters javaParameters, RunnerSettings settings, boolean beforeExecution) throws ExecutionException { + javaParameters.setJdk(((XsltRunConfiguration)settings.getRunProfile()).getEffectiveJDK()); + } + + protected RunContentDescriptor createContentDescriptor(Project project, + final Executor executor, + final RunProfileState runProfileState, + RunContentDescriptor contentToReuse, + ExecutionEnvironment executionEnvironment) throws ExecutionException { + final XDebugSession debugSession = + XDebuggerManager.getInstance(project).startSession(this, executionEnvironment, contentToReuse, new XDebugProcessStarter() { + @NotNull + public XDebugProcess start(@NotNull final XDebugSession session) { + ACTIVE.set(Boolean.TRUE); + try { + final ExecutionResult result = runProfileState.execute(executor, XsltDebuggerRunner.this); + return new XsltDebugProcess(session, result); + } catch (ExecutionException e) { + throw new RuntimeException(e); + } finally { + ACTIVE.remove(); + } + } + }); + return debugSession.getRunContentDescriptor(); + } +} \ No newline at end of file diff --git a/plugins/xslt-debugger/src/org/intellij/plugins/xsltDebugger/XsltDebuggerSession.java b/plugins/xslt-debugger/src/org/intellij/plugins/xsltDebugger/XsltDebuggerSession.java new file mode 100644 index 000000000000..97ec8374b013 --- /dev/null +++ b/plugins/xslt-debugger/src/org/intellij/plugins/xsltDebugger/XsltDebuggerSession.java @@ -0,0 +1,259 @@ +/* + * 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; + +import com.intellij.execution.process.ProcessHandler; +import com.intellij.openapi.Disposable; +import com.intellij.openapi.editor.Editor; +import com.intellij.openapi.fileEditor.FileEditorManager; +import com.intellij.openapi.fileEditor.OpenFileDescriptor; +import com.intellij.openapi.project.Project; +import com.intellij.openapi.util.Disposer; +import com.intellij.openapi.util.Key; +import com.intellij.openapi.vfs.VfsUtil; +import com.intellij.openapi.vfs.VirtualFile; +import com.intellij.pom.Navigatable; +import com.intellij.psi.PsiFile; +import com.intellij.util.EventDispatcher; +import com.intellij.xdebugger.XSourcePosition; +import org.intellij.plugins.xsltDebugger.impl.XsltBreakpointHandler; +import org.intellij.plugins.xsltDebugger.impl.XsltDebugProcess; +import org.intellij.plugins.xsltDebugger.rt.engine.Breakpoint; +import org.intellij.plugins.xsltDebugger.rt.engine.BreakpointManager; +import org.intellij.plugins.xsltDebugger.rt.engine.Debugger; +import org.intellij.plugins.xsltDebugger.rt.engine.DebuggerStoppedException; +import org.jetbrains.annotations.NotNull; +import org.jetbrains.annotations.Nullable; + +import java.net.MalformedURLException; +import java.net.SocketException; +import java.net.URI; +import java.net.URISyntaxException; +import java.rmi.RemoteException; +import java.util.EventListener; + +/** + * This is the main place that interacts with the debugged XSLT processor. Waits until the processor + * hits a breakpoint, resumes execution, etc. + */ +public class XsltDebuggerSession implements Disposable { + private static final Key DEBUGGER_SESSION = Key.create("DEBUGGER_SESSION"); + + private final Project myProject; + private final ProcessHandler myProcess; + private final Debugger myClient; + private final EventDispatcher myEventDispatcher = EventDispatcher.create(Listener.class); + + private Breakpoint myTempBreakpoint; + + private volatile Debugger.State myState; + private boolean myClosed; + + private XsltDebuggerSession(Project project, ProcessHandler process, Debugger client) { + myProject = project; + myProcess = process; + myClient = client; + Disposer.register(XsltDebugProcess.getInstance(process), this); + } + + public void start() { + myClient.start(); + myState = Debugger.State.RUNNING; + + final BreakpointManager breakpointManager = myClient.getBreakpointManager(); + final Listener multicaster = myEventDispatcher.getMulticaster(); + try { + if (!myClient.waitForDebuggee()) { + multicaster.debuggerStopped(); + return; + } + myState = Debugger.State.SUSPENDED; + do { + if (myState == Debugger.State.SUSPENDED) { + if (myTempBreakpoint != null) { + breakpointManager.removeBreakpoint(myTempBreakpoint); + myTempBreakpoint = null; + } + multicaster.debuggerSuspended(); + } else if (myState == Debugger.State.RUNNING) { + multicaster.debuggerResumed(); + } else if (myState == Debugger.State.STOPPED) { + break; + } + } + while ((myState = myClient.waitForStateChange(myState)) != null); + + multicaster.debuggerStopped(); + } catch (DebuggerStoppedException e) { + multicaster.debuggerStopped(); + } catch (RuntimeException e) { + if (e.getCause() instanceof RemoteException) { + if (e.getCause().getCause() instanceof SocketException) { + multicaster.debuggerStopped(); + return; + } + } + throw e; + } finally { + myState = Debugger.State.STOPPED; + close(); + } + } + + public void addListener(Listener listener) { + myEventDispatcher.addListener(listener); + } + + public void removeListener(Listener listener) { + myEventDispatcher.removeListener(listener); + } + + public Debugger getClient() { + return myClient; + } + + public Debugger.State getCurrentState() { + return myState; + } + + public void pause() { + myClient.pause(); + } + + public void resume() { + myClient.resume(); + } + + public void stop() { + try { + myClient.stop(false); + } catch (DebuggerStoppedException ignore) { + } + } + + public void stepOver() { + myClient.step(); + } + + public void stepInto() { + myClient.stepInto(); + } + + public boolean canRunTo(final PsiFile file, final int offset) { + return XsltBreakpointHandler.getActualLineNumber(myProject, new MyXSourcePosition(file, offset)) != -1; + } + + public void runTo(final PsiFile file, final int offset) { + assert myTempBreakpoint == null; + + final int lineNumber = XsltBreakpointHandler.getActualLineNumber(myProject, new MyXSourcePosition(file, offset)); + final String uri = XsltBreakpointHandler.getFileURL(file.getVirtualFile()); + myTempBreakpoint = myClient.getBreakpointManager().setBreakpoint(uri, lineNumber); + + resume(); + } + + @Nullable + public static Editor openLocation(Project project, @NotNull String uri, int lineNumber) { + try { + final VirtualFile file = VfsUtil.findFileByURL(new URI(uri).toURL()); + final OpenFileDescriptor descriptor = new OpenFileDescriptor(project, file, lineNumber, 0); + descriptor.navigate(true); + + return FileEditorManager.getInstance(project).openTextEditor(descriptor, true); + } catch (MalformedURLException e) { + e.printStackTrace(); //To change body of catch statement use File | Settings | File Templates. + return null; + } catch (URISyntaxException e) { + e.printStackTrace(); //To change body of catch statement use File | Settings | File Templates. + return null; + } + } + + public synchronized void close() { + if (myClosed) return; + myClosed = true; + + try { + myClient.stop(true); + } catch (DebuggerStoppedException e) { + // OK + } finally { + myProcess.destroyProcess(); + } + } + + @Override + public void dispose() { + detach(myProcess); + } + + @NotNull + public static XsltDebuggerSession create(Project project, @NotNull ProcessHandler process, Debugger client) { + final XsltDebuggerSession session = new XsltDebuggerSession(project, process, client); + process.putUserData(DEBUGGER_SESSION, session); + return session; + } + + public static XsltDebuggerSession getInstance(@NotNull ProcessHandler process) { + return process.getUserData(DEBUGGER_SESSION); + } + + public static void detach(ProcessHandler processHandler) { + processHandler.putUserData(DEBUGGER_SESSION, null); + } + + public interface Listener extends EventListener { + void debuggerSuspended(); + + void debuggerResumed(); + + void debuggerStopped(); + } + + private static class MyXSourcePosition implements XSourcePosition { + private final PsiFile myFile; + private final int myOffset; + + public MyXSourcePosition(PsiFile file, int offset) { + myFile = file; + myOffset = offset; + } + + @Override + public int getLine() { + return -1; + } + + @Override + public int getOffset() { + return myOffset; + } + + @NotNull + @Override + public VirtualFile getFile() { + return myFile.getVirtualFile(); + } + + @NotNull + @Override + public Navigatable createNavigatable(@NotNull Project project) { + throw new UnsupportedOperationException(); + } + } +} diff --git a/plugins/xslt-debugger/src/org/intellij/plugins/xsltDebugger/impl/EvalContextProvider.java b/plugins/xslt-debugger/src/org/intellij/plugins/xsltDebugger/impl/EvalContextProvider.java new file mode 100644 index 000000000000..095a6745dd8a --- /dev/null +++ b/plugins/xslt-debugger/src/org/intellij/plugins/xsltDebugger/impl/EvalContextProvider.java @@ -0,0 +1,88 @@ +/* + * 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.impl; + +import com.intellij.psi.xml.XmlElement; +import com.intellij.util.ArrayUtil; +import org.intellij.lang.xpath.context.*; +import org.intellij.lang.xpath.psi.XPathElement; +import org.intellij.lang.xpath.xslt.context.XsltContextProvider; +import org.intellij.plugins.xsltDebugger.rt.engine.Debugger; +import org.jetbrains.annotations.NotNull; +import org.jetbrains.annotations.Nullable; + +import javax.xml.namespace.QName; +import java.util.ArrayList; +import java.util.List; +import java.util.Set; + +/** + * Created by IntelliJ IDEA. + * User: sweinreuter + * Date: 06.06.2007 + */ +public class EvalContextProvider extends ContextProvider { + private final List myVariables; + + public EvalContextProvider(List model) { + myVariables = model; + } + + @NotNull + public ContextType getContextType() { + return XsltContextProvider.TYPE; + } + + @Nullable + public XmlElement getContextElement() { + return null; + } + + @Override + protected boolean isValid() { + return true; + } + + @Nullable + public NamespaceContext getNamespaceContext() { + return null; + } + + public VariableContext getVariableContext() { + return new SimpleVariableContext() { + @NotNull + public String[] getVariablesInScope(XPathElement element) { + final int size = myVariables.size(); + final ArrayList vars = new ArrayList(size); + for (Debugger.Variable myVariable : myVariables) { + vars.add(myVariable.getName()); + } + return ArrayUtil.toStringArray(vars); + } + }; + } + + @Nullable + public Set getAttributes(boolean forValidation) { + return null; // TODO + } + + @Nullable + public Set getElements(boolean forValidation) { + return null; // TODO + } +} diff --git a/plugins/xslt-debugger/src/org/intellij/plugins/xsltDebugger/impl/XsltBreakpointHandler.java b/plugins/xslt-debugger/src/org/intellij/plugins/xsltDebugger/impl/XsltBreakpointHandler.java new file mode 100644 index 000000000000..7f6a02d84c6b --- /dev/null +++ b/plugins/xslt-debugger/src/org/intellij/plugins/xsltDebugger/impl/XsltBreakpointHandler.java @@ -0,0 +1,173 @@ +package org.intellij.plugins.xsltDebugger.impl; + +import com.intellij.lang.ASTNode; +import com.intellij.openapi.editor.Document; +import com.intellij.openapi.project.Project; +import com.intellij.openapi.ui.MessageType; +import com.intellij.openapi.vfs.VfsUtil; +import com.intellij.openapi.vfs.VirtualFile; +import com.intellij.psi.PsiDocumentManager; +import com.intellij.psi.PsiElement; +import com.intellij.psi.PsiFile; +import com.intellij.psi.PsiManager; +import com.intellij.psi.tree.IElementType; +import com.intellij.psi.util.PsiTreeUtil; +import com.intellij.psi.xml.*; +import com.intellij.xdebugger.XDebugSession; +import com.intellij.xdebugger.XSourcePosition; +import com.intellij.xdebugger.breakpoints.XBreakpointHandler; +import com.intellij.xdebugger.breakpoints.XBreakpointProperties; +import com.intellij.xdebugger.breakpoints.XLineBreakpoint; +import com.intellij.xdebugger.ui.DebuggerIcons; +import org.intellij.plugins.xsltDebugger.VMPausedException; +import org.intellij.plugins.xsltDebugger.XsltBreakpointType; +import org.intellij.plugins.xsltDebugger.rt.engine.Breakpoint; +import org.intellij.plugins.xsltDebugger.rt.engine.BreakpointManager; +import org.jetbrains.annotations.NotNull; +import org.jetbrains.annotations.Nullable; + +public class XsltBreakpointHandler extends XBreakpointHandler> { + private XsltDebugProcess myXsltDebugProcess; + + public XsltBreakpointHandler(XsltDebugProcess xsltDebugProcess) { + super(XsltBreakpointType.class); + myXsltDebugProcess = xsltDebugProcess; + } + + @Override + public void registerBreakpoint(@NotNull XLineBreakpoint breakpoint) { + final XSourcePosition sourcePosition = breakpoint.getSourcePosition(); + if (sourcePosition == null) { + // ??? + return; + } + final VirtualFile file = sourcePosition.getFile(); + final Project project = myXsltDebugProcess.getSession().getProject(); + final String fileURL = getFileURL(file); + final int lineNumber = getActualLineNumber(breakpoint, project); + + try { + final BreakpointManager manager = myXsltDebugProcess.getBreakpointManager(); + Breakpoint bp; + if ((bp = manager.getBreakpoint(fileURL, lineNumber)) != null) { + bp.setEnabled(true); + } else { + manager.setBreakpoint(fileURL, lineNumber); + } + } catch (VMPausedException e) { + final XDebugSession session = myXsltDebugProcess.getSession(); + session.reportMessage("Target VM is not responding. Breakpoint can not be set", MessageType.ERROR); + session.updateBreakpointPresentation(breakpoint, DebuggerIcons.INVALID_BREAKPOINT_ICON, + "Target VM is not responding. Breakpoint can not be set"); + } + } + + public static String getFileURL(VirtualFile file) { + return VfsUtil.virtualToIoFile(file).toURI().toASCIIString(); + } + + @Override + public void unregisterBreakpoint(@NotNull XLineBreakpoint breakpoint, final boolean temporary) { + final XSourcePosition sourcePosition = breakpoint.getSourcePosition(); + if (sourcePosition == null) { + // ??? + return; + } + + final VirtualFile file = sourcePosition.getFile(); + final Project project = myXsltDebugProcess.getSession().getProject(); + final String fileURL = getFileURL(file); + final int lineNumber = getActualLineNumber(breakpoint, project); + + try { + final BreakpointManager manager = myXsltDebugProcess.getBreakpointManager(); + if (temporary) { + final Breakpoint bp = manager.getBreakpoint(fileURL, lineNumber); + if (bp != null) { + bp.setEnabled(false); + } + } else { + manager.removeBreakpoint(fileURL, lineNumber); + } + } catch (VMPausedException e) { + myXsltDebugProcess.getSession().reportMessage("Target VM is not responding. Breakpoint can not be removed", MessageType.ERROR); + } + } + + public static int getActualLineNumber(XLineBreakpoint breakpoint, Project project) { + return getActualLineNumber(project, breakpoint.getSourcePosition()); + } + + public static int getActualLineNumber(Project project, @Nullable XSourcePosition position) { + if (position == null) { + return -1; + } + final PsiElement element = findContextElement(project, position); + if (element == null) { + return -1; + } + + if (element instanceof XmlToken) { + final IElementType tokenType = ((XmlToken)element).getTokenType(); + if (tokenType == XmlTokenType.XML_START_TAG_START || tokenType == XmlTokenType.XML_NAME) { + final PsiManager psiManager = PsiManager.getInstance(project); + final PsiDocumentManager documentManager = PsiDocumentManager.getInstance(project); + final PsiFile psiFile = psiManager.findFile(position.getFile()); + if (psiFile == null) { + return -1; + } + + final Document document = documentManager.getDocument(psiFile); + if (document == null) { + return -1; + } + + if (document.getLineNumber(element.getTextRange().getStartOffset()) == position.getLine()) { + final XmlTag tag = PsiTreeUtil.getParentOfType(element, XmlTag.class, false); + if (tag != null) { + final ASTNode node = tag.getNode(); + assert node != null; + // TODO: re-check if/when Xalan is supported + final ASTNode end = XmlChildRole.START_TAG_END_FINDER.findChild(node); + if (end != null) { + return document.getLineNumber(end.getTextRange().getEndOffset()) + 1; + } else { + final ASTNode end2 = XmlChildRole.EMPTY_TAG_END_FINDER.findChild(node); + if (end2 != null) { + return document.getLineNumber(end2.getTextRange().getEndOffset()) + 1; + } + } + } + } + } + } + return -1; + } + + @Nullable + public static PsiElement findContextElement(Project project, @Nullable XSourcePosition position) { + if (position == null) { + return null; + } + + final PsiFile file = PsiManager.getInstance(project).findFile(position.getFile()); + if (file == null) { + return null; + } + + int offset = -1; + final Document document = PsiDocumentManager.getInstance(project).getDocument(file); + if (document != null) { + offset = document.getLineStartOffset(position.getLine()); + } + if (offset < 0) { + offset = position.getOffset(); + } + + PsiElement contextElement = file.findElementAt(offset); + while (contextElement != null && !(contextElement instanceof XmlElement)) { + contextElement = PsiTreeUtil.nextLeaf(contextElement); + } + return contextElement; + } +} diff --git a/plugins/xslt-debugger/src/org/intellij/plugins/xsltDebugger/impl/XsltDebugProcess.java b/plugins/xslt-debugger/src/org/intellij/plugins/xsltDebugger/impl/XsltDebugProcess.java new file mode 100644 index 000000000000..5d0c16aa153d --- /dev/null +++ b/plugins/xslt-debugger/src/org/intellij/plugins/xsltDebugger/impl/XsltDebugProcess.java @@ -0,0 +1,189 @@ +package org.intellij.plugins.xsltDebugger.impl; + +import com.intellij.execution.ExecutionResult; +import com.intellij.execution.process.ProcessHandler; +import com.intellij.execution.ui.ExecutionConsole; +import com.intellij.openapi.Disposable; +import com.intellij.openapi.util.Disposer; +import com.intellij.openapi.util.Key; +import com.intellij.openapi.wm.StatusBar; +import com.intellij.psi.PsiFile; +import com.intellij.psi.PsiManager; +import com.intellij.xdebugger.XDebugProcess; +import com.intellij.xdebugger.XDebugSession; +import com.intellij.xdebugger.XSourcePosition; +import com.intellij.xdebugger.breakpoints.XBreakpointHandler; +import com.intellij.xdebugger.evaluation.XDebuggerEditorsProvider; +import com.intellij.xdebugger.frame.XExecutionStack; +import com.intellij.xdebugger.frame.XSuspendContext; +import org.intellij.plugins.xsltDebugger.XsltDebuggerSession; +import org.intellij.plugins.xsltDebugger.rt.engine.Breakpoint; +import org.intellij.plugins.xsltDebugger.rt.engine.BreakpointManager; +import org.intellij.plugins.xsltDebugger.rt.engine.Debugger; +import org.intellij.plugins.xsltDebugger.rt.engine.local.BreakpointManagerImpl; +import org.jetbrains.annotations.NotNull; +import org.jetbrains.annotations.Nullable; + +import java.util.List; + +public class XsltDebugProcess extends XDebugProcess implements Disposable { + private static final Key KEY = Key.create("PROCESS"); + + private final XsltDebuggerEditorsProvider myEditorsProvider; + private final ProcessHandler myProcessHandler; + private final ExecutionConsole myExecutionConsole; + + private BreakpointManager myBreakpointManager = new BreakpointManagerImpl(); + + private final XBreakpointHandler[] myXBreakpointHandlers = new XBreakpointHandler[]{ + new XsltBreakpointHandler(this) + }; + private XsltDebuggerSession myDebuggerSession; + + public XsltDebugProcess(XDebugSession session, ExecutionResult executionResult) { + super(session); + myProcessHandler = executionResult.getProcessHandler(); + myProcessHandler.putUserData(KEY, this); + myExecutionConsole = executionResult.getExecutionConsole(); + myEditorsProvider = new XsltDebuggerEditorsProvider(); + Disposer.register(myExecutionConsole, this); + } + + @Override + public XBreakpointHandler[] getBreakpointHandlers() { + return myXBreakpointHandlers; + } + + public BreakpointManager getBreakpointManager() { + return myBreakpointManager; + } + + public void init(Debugger client) { + myDebuggerSession = XsltDebuggerSession.getInstance(myProcessHandler); + + myDebuggerSession.addListener(new XsltDebuggerSession.Listener() { + @Override + public void debuggerSuspended() { + final XDebugSession session = XsltDebugProcess.this.getSession(); + final Debugger c = myDebuggerSession.getClient(); + session.positionReached(new MySuspendContext(myDebuggerSession, c.getCurrentFrame(), c.getSourceFrame())); + } + + @Override + public void debuggerResumed() { + } + + @Override + public void debuggerStopped() { + } + }); + + final BreakpointManager mgr = client.getBreakpointManager(); + if (myBreakpointManager != mgr) { + final List breakpoints = myBreakpointManager.getBreakpoints(); + for (Breakpoint breakpoint : breakpoints) { + final Breakpoint bp = mgr.setBreakpoint(breakpoint.getUri(), breakpoint.getLine()); + bp.setEnabled(breakpoint.isEnabled()); + bp.setLogMessage(breakpoint.getLogMessage()); + bp.setTraceMessage(breakpoint.getTraceMessage()); + bp.setCondition(breakpoint.getCondition()); + bp.setSuspend(breakpoint.isSuspend()); + } + myBreakpointManager = mgr; + } + } + + @Nullable + public static XsltDebugProcess getInstance(ProcessHandler handler) { + return handler.getUserData(KEY); + } + + @NotNull + @Override + public ExecutionConsole createConsole() { + return myExecutionConsole; + } + + @Override + protected ProcessHandler doGetProcessHandler() { + return myProcessHandler; + } + + @NotNull + @Override + public XDebuggerEditorsProvider getEditorsProvider() { + return myEditorsProvider; + } + + @Override + public void startStepOver() { + myDebuggerSession.stepOver(); + } + + @Override + public void startStepInto() { + myDebuggerSession.stepInto(); + } + + @Override + public void startStepOut() { + } + + @Override + public void stop() { + if (myDebuggerSession != null) { + myDebuggerSession.stop(); + } + } + + @Override + public void resume() { + myDebuggerSession.resume(); + } + + @Override + public void dispose() { + } + + @Override + public void runToPosition(@NotNull XSourcePosition position) { + final PsiFile psiFile = PsiManager.getInstance(getSession().getProject()).findFile(position.getFile()); + assert psiFile != null; + if (myDebuggerSession.canRunTo(psiFile, position.getOffset())) { + myDebuggerSession.runTo(psiFile, position.getOffset()); + } else { + StatusBar.Info.set("Not a valid position in file '" + psiFile.getName() + "'", psiFile.getProject()); + final Debugger c = myDebuggerSession.getClient(); + getSession().positionReached(new MySuspendContext(myDebuggerSession, c.getCurrentFrame(), c.getSourceFrame())); + } + } + + private static class MySuspendContext extends XSuspendContext { + private final XsltDebuggerSession myDebuggerSession; + private final Debugger.StyleFrame myStyleFrame; + private final Debugger.SourceFrame mySourceFrame; + + public MySuspendContext(XsltDebuggerSession debuggerSession, Debugger.StyleFrame styleFrame, Debugger.SourceFrame sourceFrame) { + myDebuggerSession = debuggerSession; + myStyleFrame = styleFrame; + mySourceFrame = sourceFrame; + } + + @Override + public XExecutionStack getActiveExecutionStack() { + return new XsltExecutionStack("XSLT Frames", myStyleFrame, myDebuggerSession); + } + + public XExecutionStack getSourceStack() { + return new XsltExecutionStack("Source Frames", mySourceFrame, myDebuggerSession); + } + + @Override + public XExecutionStack[] getExecutionStacks() { + return new XExecutionStack[]{ + getActiveExecutionStack(), + getSourceStack() + }; + } + } +} diff --git a/plugins/xslt-debugger/src/org/intellij/plugins/xsltDebugger/impl/XsltDebuggerEditorsProvider.java b/plugins/xslt-debugger/src/org/intellij/plugins/xsltDebugger/impl/XsltDebuggerEditorsProvider.java new file mode 100644 index 000000000000..9e4a1df73a10 --- /dev/null +++ b/plugins/xslt-debugger/src/org/intellij/plugins/xsltDebugger/impl/XsltDebuggerEditorsProvider.java @@ -0,0 +1,48 @@ +package org.intellij.plugins.xsltDebugger.impl; + +import com.intellij.openapi.editor.Document; +import com.intellij.openapi.fileTypes.FileType; +import com.intellij.openapi.project.Project; +import com.intellij.psi.PsiDocumentManager; +import com.intellij.psi.PsiElement; +import com.intellij.psi.PsiFile; +import com.intellij.psi.PsiFileFactory; +import com.intellij.util.LocalTimeCounter; +import com.intellij.xdebugger.XSourcePosition; +import com.intellij.xdebugger.evaluation.XDebuggerEditorsProvider; +import org.intellij.lang.xpath.XPathFileType; +import org.intellij.plugins.xsltDebugger.BreakpointContext; +import org.intellij.plugins.xsltDebugger.rt.engine.Debugger; +import org.jetbrains.annotations.NotNull; +import org.jetbrains.annotations.Nullable; + +public class XsltDebuggerEditorsProvider extends XDebuggerEditorsProvider { + @NotNull + @Override + public FileType getFileType() { + return XPathFileType.XPATH; + } + + @NotNull + @Override + public Document createDocument(@NotNull Project project, @NotNull String text, @Nullable XSourcePosition sourcePosition) { + final PsiFile psiFile = PsiFileFactory.getInstance(project) + .createFileFromText("XPathExpr.xpath", XPathFileType.XPATH, text, LocalTimeCounter.currentTime(), true); + + if (sourcePosition instanceof XsltSourcePosition && ((XsltSourcePosition)sourcePosition).getLocation() instanceof Debugger.StyleFrame) { + final Debugger.Locatable location = ((XsltSourcePosition)sourcePosition).getLocation(); + final EvalContextProvider context = new EvalContextProvider(((Debugger.StyleFrame)location).getVariables()); + context.attachTo(psiFile); + } else { + final PsiElement contextElement = XsltBreakpointHandler.findContextElement(project, sourcePosition); + if (contextElement != null) { + final BreakpointContext context = new BreakpointContext(contextElement); + context.attachTo(psiFile); + } + } + + final Document document = PsiDocumentManager.getInstance(project).getDocument(psiFile); + assert document != null; + return document; + } +} diff --git a/plugins/xslt-debugger/src/org/intellij/plugins/xsltDebugger/impl/XsltExecutionStack.java b/plugins/xslt-debugger/src/org/intellij/plugins/xsltDebugger/impl/XsltExecutionStack.java new file mode 100644 index 000000000000..e32a209bc0c1 --- /dev/null +++ b/plugins/xslt-debugger/src/org/intellij/plugins/xsltDebugger/impl/XsltExecutionStack.java @@ -0,0 +1,45 @@ +package org.intellij.plugins.xsltDebugger.impl; + +import com.intellij.xdebugger.frame.XExecutionStack; +import com.intellij.xdebugger.frame.XStackFrame; +import org.intellij.plugins.xsltDebugger.XsltDebuggerSession; +import org.intellij.plugins.xsltDebugger.rt.engine.Debugger; + +import java.util.ArrayList; +import java.util.Collections; +import java.util.List; + +public class XsltExecutionStack extends XExecutionStack { + private final XsltStackFrame myTopFrame; + private final XsltDebuggerSession myDebuggerSession; + + public XsltExecutionStack(String name, Debugger.Frame topFrame, XsltDebuggerSession debuggerSession) { + super(name); + myDebuggerSession = debuggerSession; + myTopFrame = new XsltStackFrame(topFrame, myDebuggerSession); + } + + @Override + public XStackFrame getTopFrame() { + return myTopFrame; + } + + @Override + public void computeStackFrames(int firstFrameIndex, XStackFrameContainer container) { + if (myDebuggerSession.getCurrentState() == Debugger.State.SUSPENDED) { + Debugger.Frame frame = myTopFrame.getFrame(); + final List frames = new ArrayList(); + while (frame != null) { + frame = frame.getPrevious(); + if (frame != null) { + frames.add(new XsltStackFrame(frame, myDebuggerSession)); + } + } + if (firstFrameIndex <= frames.size()) { + container.addStackFrames(frames.subList(firstFrameIndex - 1, frames.size()), true); + } else { + container.addStackFrames(Collections.emptyList(), true); + } + } + } +} diff --git a/plugins/xslt-debugger/src/org/intellij/plugins/xsltDebugger/impl/XsltSourcePosition.java b/plugins/xslt-debugger/src/org/intellij/plugins/xsltDebugger/impl/XsltSourcePosition.java new file mode 100644 index 000000000000..6d6be7a33a78 --- /dev/null +++ b/plugins/xslt-debugger/src/org/intellij/plugins/xsltDebugger/impl/XsltSourcePosition.java @@ -0,0 +1,65 @@ +package org.intellij.plugins.xsltDebugger.impl; + +import com.intellij.openapi.fileEditor.OpenFileDescriptor; +import com.intellij.openapi.project.Project; +import com.intellij.openapi.vfs.VfsUtil; +import com.intellij.openapi.vfs.VirtualFile; +import com.intellij.pom.Navigatable; +import com.intellij.xdebugger.XSourcePosition; +import org.intellij.plugins.xsltDebugger.rt.engine.Debugger; +import org.jetbrains.annotations.NotNull; +import org.jetbrains.annotations.Nullable; + +import java.net.URI; + +public class XsltSourcePosition implements XSourcePosition { + private final Debugger.Locatable myLocation; + private final VirtualFile myFile; + private final int myLine; + + XsltSourcePosition(Debugger.Locatable location, VirtualFile file, int line) { + myLocation = location; + myFile = file; + myLine = line; + } + + @Nullable + public static XSourcePosition create(Debugger.Locatable location) { + final VirtualFile file; + try { + file = VfsUtil.findFileByURL(new URI(location.getURI()).toURL()); + } catch (Exception e) { + // TODO log + return null; + } + + final int line = location.getLineNumber() - 1; + return line >= 0 ? new XsltSourcePosition(location, file, line) : null; + } + + @Override + public int getLine() { + return myLine; + } + + @Override + public int getOffset() { + return 0; + } + + @NotNull + @Override + public VirtualFile getFile() { + return myFile; + } + + @NotNull + @Override + public Navigatable createNavigatable(@NotNull Project project) { + return new OpenFileDescriptor(project, getFile(), getLine(), 0); + } + + public Debugger.Locatable getLocation() { + return myLocation; + } +} diff --git a/plugins/xslt-debugger/src/org/intellij/plugins/xsltDebugger/impl/XsltStackFrame.java b/plugins/xslt-debugger/src/org/intellij/plugins/xsltDebugger/impl/XsltStackFrame.java new file mode 100644 index 000000000000..5e13ae83d1ff --- /dev/null +++ b/plugins/xslt-debugger/src/org/intellij/plugins/xsltDebugger/impl/XsltStackFrame.java @@ -0,0 +1,235 @@ +package org.intellij.plugins.xsltDebugger.impl; + +import com.intellij.openapi.vfs.VfsUtil; +import com.intellij.openapi.vfs.VirtualFile; +import com.intellij.ui.SimpleColoredComponent; +import com.intellij.ui.SimpleTextAttributes; +import com.intellij.util.Icons; +import com.intellij.xdebugger.XSourcePosition; +import com.intellij.xdebugger.evaluation.XDebuggerEvaluator; +import com.intellij.xdebugger.frame.*; +import org.intellij.plugins.xsltDebugger.XsltDebuggerSession; +import org.intellij.plugins.xsltDebugger.rt.engine.Debugger; +import org.intellij.plugins.xsltDebugger.rt.engine.DebuggerStoppedException; +import org.intellij.plugins.xsltDebugger.rt.engine.Value; +import org.jetbrains.annotations.NotNull; +import org.jetbrains.annotations.Nullable; + +import javax.swing.*; +import java.net.URI; +import java.util.List; + +public class XsltStackFrame extends XStackFrame { + private final Debugger.Frame myFrame; + private final XsltDebuggerSession myDebuggerSession; + private XSourcePosition myPosition; + + public XsltStackFrame(Debugger.Frame frame, XsltDebuggerSession debuggerSession) { + myFrame = frame; + myDebuggerSession = debuggerSession; + myPosition = XsltSourcePosition.create(frame); + } + + @Override + public Object getEqualityObject() { + return super.getEqualityObject(); + } + + @Override + public XDebuggerEvaluator getEvaluator() { + return myFrame instanceof Debugger.StyleFrame ? new MyEvaluator((Debugger.StyleFrame)myFrame) : null; + } + + @Override + public XSourcePosition getSourcePosition() { + return myPosition; + } + + @Override + public void customizePresentation(SimpleColoredComponent component) { + if (myDebuggerSession.getCurrentState() == Debugger.State.SUSPENDED) { + try { + _customizePresentation(component); + } catch (DebuggerStoppedException ignore) { + } + } + } + + private void _customizePresentation(SimpleColoredComponent component) { + final Debugger.Frame frame = myFrame; + if (frame instanceof Debugger.StyleFrame) { + component.append(((Debugger.StyleFrame)frame).getInstruction(), SimpleTextAttributes.REGULAR_BOLD_ATTRIBUTES); + } else if (frame instanceof Debugger.SourceFrame) { + component.append(((Debugger.SourceFrame)frame).getXPath(), SimpleTextAttributes.REGULAR_BOLD_ATTRIBUTES); + } + component.append(" ", SimpleTextAttributes.REGULAR_ATTRIBUTES); + + try { + final VirtualFile file = VfsUtil.findFileByURL(new URI(frame.getURI()).toURL()); + if (file != null) { + component.append(file.getName(), SimpleTextAttributes.REGULAR_ATTRIBUTES); + if (frame.getLineNumber() > 0) { + component.append(":" + frame.getLineNumber(), SimpleTextAttributes.REGULAR_ATTRIBUTES); + } + + component.setToolTipText(file.getPresentableUrl()); + } else { + component.append(frame.getURI() + ":" + frame.getLineNumber(), SimpleTextAttributes.REGULAR_ATTRIBUTES); + } + } catch (Exception e) { + component.append(frame.getURI() + ":" + frame.getLineNumber(), SimpleTextAttributes.REGULAR_ATTRIBUTES); + } + } + + @Override + public void computeChildren(@NotNull XCompositeNode node) { + if (myFrame instanceof Debugger.StyleFrame) { + final List variables = ((Debugger.StyleFrame)myFrame).getVariables(); + final XValueChildrenList list = new XValueChildrenList(); + for (final Debugger.Variable variable : variables) { + list.add(variable.getName(), new MyValue(variable)); + } + node.addChildren(list, true); + } else { + super.computeChildren(node); + } + } + + public Debugger.Frame getFrame() { + return myFrame; + } + + private static class MyValue extends XValue { + private final Debugger.Variable myVariable; + + public MyValue(Debugger.Variable variable) { + myVariable = variable; + } + + @Override + public void computePresentation(@NotNull XValueNode node) { + final Debugger.Variable.Kind kind = myVariable.getKind(); + Icon icon = null; + if (myVariable.isGlobal()) { + if (kind == Debugger.Variable.Kind.VARIABLE) { + icon = Icons.FIELD_ICON; + } else { + icon = Icons.PROPERTY_ICON; + } + } else if (kind == Debugger.Variable.Kind.VARIABLE) { + icon = Icons.VARIABLE_ICON; + } else if (kind == Debugger.Variable.Kind.PARAMETER) { + icon = Icons.PARAMETER_ICON; + } + + final Value v = myVariable.getValue(); + if (v.getType() == Value.XPathType.STRING) { + node.setPresentation(icon, v.getType().getName(), "'" + String.valueOf(v.getValue()) + "'", false); + } else { + final boolean hasChildren = myVariable.getValue().getValue() instanceof Value.NodeSet; + node.setPresentation(icon, v.getType().getName(), String.valueOf(v.getValue()), hasChildren); + } + } + + @Override + public void computeChildren(@NotNull XCompositeNode node) { + if (myVariable.getValue().getValue() instanceof Value.NodeSet) { + final Value.NodeSet set = (Value.NodeSet)myVariable.getValue().getValue(); + final XValueChildrenList list = new XValueChildrenList(); + for (final Value.Node n : set.getNodes()) { + list.add(n.myXPath, new NodeValue(n)); + } + node.addChildren(list, false); + } + super.computeChildren(node); + } + + @Override + public String getEvaluationExpression() { + return "$" + myVariable.getName(); + } + + @Override + public void computeSourcePosition(@NotNull XNavigatable navigatable) { + navigatable.setSourcePosition(XsltSourcePosition.create(myVariable)); + } + + static String clipValue(String stringValue) { + return stringValue.length() < 100 ? stringValue : stringValue.substring(0, 100) + "..."; + } + + private static class NodeValue extends XValue { + private final Value.Node myNode; + + public NodeValue(Value.Node n) { + myNode = n; + } + + @Override + public void computeSourcePosition(@NotNull XNavigatable navigatable) { + navigatable.setSourcePosition(XsltSourcePosition.create(myNode)); + } + + @Override + public void computePresentation(@NotNull XValueNode node) { + node.setPresentation(null, "node", myNode.myStringValue, false); + } + } + } + + private static class MyEvaluator extends XDebuggerEvaluator { + private final Debugger.StyleFrame myFrame; + + public MyEvaluator(Debugger.StyleFrame frame) { + myFrame = frame; + } + + @Override + public boolean isCodeFragmentEvaluationSupported() { + return false; + } + + @Override + public void evaluate(@NotNull String expression, XEvaluationCallback callback, @Nullable XSourcePosition expressionPosition) { + try { + final Value eval = myFrame.eval(expression); + callback.evaluated(new MyValue(new ExpressionResult(eval))); + } catch (Debugger.EvaluationException e) { + callback.errorOccurred(e.getMessage()); + } + } + + private static class ExpressionResult implements Debugger.Variable { + private final Value myValue; + + public ExpressionResult(Value value) { + myValue = value; + } + + @SuppressWarnings({ "ConstantConditions" }) + public String getURI() { + return null; + } + + public int getLineNumber() { + return -1; + } + + public boolean isGlobal() { + return false; + } + + public Kind getKind() { + return Kind.EXPRESSION; + } + + public String getName() { + return "result"; + } + + public Value getValue() { + return myValue; + } + } + } +} diff --git a/plugins/xslt-debugger/src/org/intellij/plugins/xsltDebugger/ui/AbstractTabComponent.java b/plugins/xslt-debugger/src/org/intellij/plugins/xsltDebugger/ui/AbstractTabComponent.java new file mode 100644 index 000000000000..5e9182baa27e --- /dev/null +++ b/plugins/xslt-debugger/src/org/intellij/plugins/xsltDebugger/ui/AbstractTabComponent.java @@ -0,0 +1,47 @@ +package org.intellij.plugins.xsltDebugger.ui; + +import com.intellij.diagnostic.logging.AdditionalTabComponent; + +import javax.swing.*; + +/* +* Created by IntelliJ IDEA. +* User: sweinreuter +* Date: 08.03.11 +*/ +abstract class AbstractTabComponent extends AdditionalTabComponent { + private final String myTabTitle; + + public AbstractTabComponent(String tabTitle) { + myTabTitle = tabTitle; + } + + @Override + public String getTabTitle() { + return myTabTitle; + } + + @Override + public JComponent getSearchComponent() { + return null; + } + + @Override + public String getToolbarPlace() { + return null; + } + + @Override + public JComponent getToolbarContextComponent() { + return null; + } + + @Override + public boolean isContentBuiltIn() { + return false; + } + + @Override + public void dispose() { + } +} \ No newline at end of file diff --git a/plugins/xslt-debugger/src/org/intellij/plugins/xsltDebugger/ui/GeneratedStructureModel.java b/plugins/xslt-debugger/src/org/intellij/plugins/xsltDebugger/ui/GeneratedStructureModel.java new file mode 100644 index 000000000000..09fa9636e999 --- /dev/null +++ b/plugins/xslt-debugger/src/org/intellij/plugins/xsltDebugger/ui/GeneratedStructureModel.java @@ -0,0 +1,322 @@ +/* + * Copyright 2002-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.ui; + +import com.intellij.ide.DataManager; +import com.intellij.openapi.actionSystem.PlatformDataKeys; +import com.intellij.openapi.application.ApplicationManager; +import com.intellij.openapi.project.Project; +import com.intellij.pom.Navigatable; +import com.intellij.util.SmartList; +import com.intellij.util.containers.StringInterner; +import org.intellij.plugins.xsltDebugger.XsltDebuggerSession; +import org.intellij.plugins.xsltDebugger.rt.engine.OutputEventQueue; +import org.jetbrains.annotations.NonNls; +import org.jetbrains.annotations.Nullable; + +import javax.swing.*; +import javax.swing.tree.DefaultMutableTreeNode; +import javax.swing.tree.DefaultTreeModel; +import javax.swing.tree.TreeNode; +import java.lang.ref.WeakReference; +import java.util.ArrayList; +import java.util.Collections; +import java.util.LinkedList; +import java.util.List; + +/** + * Created by IntelliJ IDEA. + * User: sweinreuter + * Date: 09.06.2007 + */ +public class GeneratedStructureModel extends DefaultTreeModel { + @NonNls + private static final String PENDING = "..."; + + private static WeakReference ourSharedInterner; + + private final LinkedList myCurrentPath = new LinkedList(); + private final List myLastNodes = new LinkedList(); + + private final StringInterner myInterner = getInterner(); + + // we keep a shared string interner across all currently running xslt debugger instances. it should go away once + // all instances (and their toolwindow contents) are gone. This should minimize the memory usage of the generated + // structure tree. + private static StringInterner getInterner() { + StringInterner interner; + if (ourSharedInterner == null || (interner = ourSharedInterner.get()) == null) { + interner = new StringInterner(); + ourSharedInterner = new WeakReference(interner); + } + return interner; + } + + private boolean myFilterWhitespace; + private boolean myListenersDisabled; + + public GeneratedStructureModel() { + super(new MyRootNode()); + final DefaultMutableTreeNode root = (DefaultMutableTreeNode)getRoot(); + myCurrentPath.add(root); + root.add(new DefaultMutableTreeNode(PENDING)); + } + + public void update(final List eventQueue) { + if (!SwingUtilities.isEventDispatchThread()) { + ApplicationManager.getApplication().invokeLater(new Runnable() { + public void run() { + updateImpl(eventQueue); + } + }); + return; + } + updateImpl(eventQueue); + } + + @Override + public Object getChild(Object parent, int index) { + if (!myFilterWhitespace) { + return super.getChild(parent, index); + } + return getFilteredChildren((DefaultMutableTreeNode)parent, false).get(index); + } + + @Override + public int getChildCount(Object parent) { + if (!myFilterWhitespace) { + return super.getChildCount(parent); + } + return getFilteredChildren((DefaultMutableTreeNode)parent, false).size(); + } + + @Override + public boolean isLeaf(Object node) { + if (!myFilterWhitespace) { + return super.isLeaf(node); + } + return super.isLeaf(node) || getFilteredChildren((DefaultMutableTreeNode)node, true).size() == 0; + } + + private static List getFilteredChildren(DefaultMutableTreeNode node, boolean checkOnly) { + if (node.getChildCount() == 0) { + return Collections.emptyList(); + } + final List nodes = checkOnly ? + new SmartList() : + new ArrayList(node.getChildCount()); + + DefaultMutableTreeNode child = (DefaultMutableTreeNode)node.getFirstChild(); + while (child != null) { + if (child instanceof StructureNode) { + final OutputEventQueue.NodeEvent event = (OutputEventQueue.NodeEvent)child.getUserObject(); + if (event != null && event.getType() == OutputEventQueue.CHARACTERS) { + if (event.getValue().trim().length() == 0) { + child = child.getNextSibling(); + continue; + } + } + } + + nodes.add(child); + if (checkOnly) return nodes; + + child = child.getNextSibling(); + } + return nodes; + } + + private void updateImpl(List nodeEvents) { + if (nodeEvents.size() > 0) { + for (DefaultMutableTreeNode node : myLastNodes) { + if (node instanceof StructureNode) { + ((StructureNode)node).refresh(); + } + } + myLastNodes.clear(); + } + + for (OutputEventQueue.NodeEvent event : nodeEvents) { + event = intern(event); + + final DefaultMutableTreeNode node = myCurrentPath.getFirst(); + switch (event.getType()) { + case OutputEventQueue.START_DOCUMENT: + break; + case OutputEventQueue.START_ELEMENT: + final StructureNode child = new StructureNode(event); + myLastNodes.add(child); + + final int index = getChildCount(node) - 1; + node.insert(child, node.getChildCount() - 1); + child.add(new DefaultMutableTreeNode(PENDING)); + + myCurrentPath.addFirst(child); + + nodesWereInserted(node, new int[]{ index }); + break; + + case OutputEventQueue.END_ELEMENT: + if (node instanceof MyRootNode) { + // unknown xalan bug: start/end is sometimes unbalanced. should be fixed somewhere else... + continue; + } + case OutputEventQueue.END_DOCUMENT: + final DefaultMutableTreeNode c = myCurrentPath.removeFirst(); + final int childIndex = getChildCount(c) - 1; + final int realChildIndex = c.getChildCount() - 1; + if (realChildIndex >= 0) { + final DefaultMutableTreeNode childNode = (DefaultMutableTreeNode)c.getChildAt(realChildIndex); + assert childNode.getUserObject() == PENDING; + c.remove(realChildIndex); + nodesWereRemoved(c, new int[]{ childIndex }, new Object[]{ childNode }); + } + break; + + case OutputEventQueue.TRACE_POINT: + case OutputEventQueue.ATTRIBUTE: + case OutputEventQueue.CHARACTERS: + case OutputEventQueue.COMMENT: + case OutputEventQueue.PI: + final StructureNode ch = new StructureNode(event); + myLastNodes.add(ch); + + final int i = getChildCount(node) - 1; + node.insert(ch, node.getChildCount() - 1); + nodesWereInserted(node, new int[]{ i }); + } + } + } + + private OutputEventQueue.NodeEvent intern(OutputEventQueue.NodeEvent event) { + event.myURI = intern(event.myURI); + event.myQName = intern(event.myQName); + event.myValue = intern(event.myValue); + return event; + } + + @Nullable + private OutputEventQueue.NodeEvent.QName intern(OutputEventQueue.NodeEvent.QName name) { + if (name == null) return null; + name.myPrefix = intern(name.myPrefix); + name.myLocalName = intern(name.myLocalName); + name.myURI = intern(name.myURI); + return name; + } + + @Nullable + private String intern(String s) { + if (s != null) { + if (s.length() == 0) return s.intern(); + return myInterner.intern(s); + } else { + return null; + } + } + + public boolean isFilterWhitespace() { + return myFilterWhitespace; + } + + public void setFilterWhitespace(boolean b) { + final boolean old = myFilterWhitespace; + myFilterWhitespace = b; + if (b != old) { + nodeStructureChanged((TreeNode)getRoot()); + } + } + + public void finalUpdate(final List events) { + Runnable runnable = new Runnable() { + public void run() { + myListenersDisabled = true; + try { + updateImpl(events); + } finally { + myListenersDisabled = false; + nodeStructureChanged((TreeNode)getRoot()); + } + } + }; + ApplicationManager.getApplication().invokeLater(runnable); + } + + @Override + protected void fireTreeNodesChanged(Object source, Object[] path, int[] childIndices, Object[] children) { + if (myListenersDisabled) return; + super.fireTreeNodesChanged(source, path, childIndices, children); + } + + @Override + protected void fireTreeNodesInserted(Object source, Object[] path, int[] childIndices, Object[] children) { + if (myListenersDisabled) return; + super.fireTreeNodesInserted(source, path, childIndices, children); + } + + @Override + protected void fireTreeNodesRemoved(Object source, Object[] path, int[] childIndices, Object[] children) { + if (myListenersDisabled) return; + super.fireTreeNodesRemoved(source, path, childIndices, children); + } + + @Override + protected void fireTreeStructureChanged(Object source, Object[] path, int[] childIndices, Object[] children) { + if (myListenersDisabled) return; + super.fireTreeStructureChanged(source, path, childIndices, children); + } + + private static class MyRootNode extends DefaultMutableTreeNode { + public MyRootNode() { + super("ROOT"); + } + } + + public static class StructureNode extends DefaultMutableTreeNode implements Navigatable { + private boolean isNew = true; + + public StructureNode(OutputEventQueue.NodeEvent event) { + super(event); + } + + private void refresh() { + isNew = false; + } + + public boolean isNew() { + return isNew; + } + + @Override + public OutputEventQueue.NodeEvent getUserObject() { + return (OutputEventQueue.NodeEvent)super.getUserObject(); + } + + public void navigate(boolean requestFocus) { + final OutputEventQueue.NodeEvent event = getUserObject(); + final Project project = (Project)DataManager.getInstance().getDataContext().getData(PlatformDataKeys.PROJECT.getName()); + XsltDebuggerSession.openLocation(project, event.getURI(), event.getLineNumber() - 1); + } + + public boolean canNavigate() { + return getUserObject().getLineNumber() > 0; + } + + public boolean canNavigateToSource() { + return canNavigate(); + } + } +} diff --git a/plugins/xslt-debugger/src/org/intellij/plugins/xsltDebugger/ui/GeneratedStructureRenderer.java b/plugins/xslt-debugger/src/org/intellij/plugins/xsltDebugger/ui/GeneratedStructureRenderer.java new file mode 100644 index 000000000000..ae9f5722cf70 --- /dev/null +++ b/plugins/xslt-debugger/src/org/intellij/plugins/xsltDebugger/ui/GeneratedStructureRenderer.java @@ -0,0 +1,104 @@ +/* + * Copyright 2002-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.ui; + +import com.intellij.openapi.util.IconLoader; +import com.intellij.ui.ColoredTreeCellRenderer; +import com.intellij.ui.SimpleTextAttributes; +import com.intellij.util.Icons; +import com.intellij.xdebugger.ui.DebuggerIcons; +import org.intellij.plugins.xsltDebugger.rt.engine.OutputEventQueue; + +import javax.swing.*; +import javax.swing.tree.DefaultMutableTreeNode; + +/** + * Created by IntelliJ IDEA. + * User: sweinreuter + * Date: 09.06.2007 + */ +class GeneratedStructureRenderer extends ColoredTreeCellRenderer { + private static final Icon XML_COMMENT_ICON = IconLoader.getIcon("xmlComment.png"); + + public void customizeCellRenderer(JTree tree, Object value, boolean selected, boolean expanded, boolean leaf, int row, boolean hasFocus) { + final DefaultMutableTreeNode node = (DefaultMutableTreeNode)value; + final Object o = node.getUserObject(); + + if (o == null || "ROOT".equals(o)) { + // invisible + } else if (o instanceof String) { + // "..." node + append((String)o, SimpleTextAttributes.SYNTHETIC_ATTRIBUTES); + setToolTipText("Element is not finished yet"); + } else if (o instanceof OutputEventQueue.NodeEvent) { + final OutputEventQueue.NodeEvent event = (OutputEventQueue.NodeEvent)o; + final OutputEventQueue.NodeEvent.QName qname = event.getQName(); + switch (event.getType()) { + case OutputEventQueue.START_ELEMENT: + setIcon(Icons.XML_TAG_ICON); + append(qname.getQName(), SimpleTextAttributes.REGULAR_BOLD_ATTRIBUTES); + if (qname.myURI != null && qname.myURI.length() > 0) { + append(" {", SimpleTextAttributes.GRAYED_ATTRIBUTES); + append(qname.myURI, SimpleTextAttributes.GRAYED_ATTRIBUTES); + append("}", SimpleTextAttributes.GRAYED_ATTRIBUTES); + } + break; + case OutputEventQueue.ATTRIBUTE: + setIcon(Icons.ANNOTATION_TYPE_ICON); + append(qname.getQName(), SimpleTextAttributes.REGULAR_BOLD_ATTRIBUTES); + if (qname.myURI != null && qname.myURI.length() > 0) { + append(" {" + qname.myURI + "}", SimpleTextAttributes.GRAYED_ATTRIBUTES); + } + append(" = \"", SimpleTextAttributes.REGULAR_ATTRIBUTES); + append(event.getValue(), SimpleTextAttributes.REGULAR_ATTRIBUTES); + append("\"", SimpleTextAttributes.REGULAR_ATTRIBUTES); + break; + case OutputEventQueue.CHARACTERS: + append("#text ", SimpleTextAttributes.GRAYED_ATTRIBUTES); + append(clipValue(event.getValue()), SimpleTextAttributes.REGULAR_ATTRIBUTES); + break; + case OutputEventQueue.COMMENT: + setIcon(XML_COMMENT_ICON); + append("#comment ", SimpleTextAttributes.GRAYED_ATTRIBUTES); + append(event.getValue(), SimpleTextAttributes.REGULAR_ATTRIBUTES); + break; + case OutputEventQueue.PI: + append("#processing-instruction ", SimpleTextAttributes.GRAYED_ATTRIBUTES); + append(qname.myLocalName, SimpleTextAttributes.REGULAR_BOLD_ATTRIBUTES); + append(" " + event.getValue(), SimpleTextAttributes.REGULAR_ATTRIBUTES); + break; + case OutputEventQueue.TRACE_POINT: + setIcon(DebuggerIcons.ENABLED_BREAKPOINT_ICON); + append("Tracepoint at line " + event.getLineNumber(), SimpleTextAttributes.GRAY_ATTRIBUTES); + if (event.getValue() != null) { + append(" " + event.getValue(), SimpleTextAttributes.REGULAR_ATTRIBUTES); + } + break; + } + + if (node instanceof GeneratedStructureModel.StructureNode) { + if (((GeneratedStructureModel.StructureNode)node).isNew()) { + append(" *", SimpleTextAttributes.SYNTHETIC_ATTRIBUTES); + } + } + } + } + + static String clipValue(String stringValue) { + return stringValue.length() < 80 ? stringValue : stringValue.substring(0, 80) + "..."; + } +} diff --git a/plugins/xslt-debugger/src/org/intellij/plugins/xsltDebugger/ui/OutputTabComponent.java b/plugins/xslt-debugger/src/org/intellij/plugins/xsltDebugger/ui/OutputTabComponent.java new file mode 100644 index 000000000000..8e2485139c9e --- /dev/null +++ b/plugins/xslt-debugger/src/org/intellij/plugins/xsltDebugger/ui/OutputTabComponent.java @@ -0,0 +1,39 @@ +package org.intellij.plugins.xsltDebugger.ui; + +import com.intellij.diagnostic.logging.AdditionalTabComponent; +import com.intellij.openapi.actionSystem.ActionGroup; +import com.intellij.openapi.actionSystem.DefaultActionGroup; +import org.intellij.plugins.xsltDebugger.ui.actions.OpenOutputAction; +import org.jetbrains.annotations.NotNull; + +import javax.swing.*; + +public class OutputTabComponent extends AbstractTabComponent { + private final DefaultActionGroup myOutputActions; + private final AdditionalTabComponent myOutputConsole; + + public OutputTabComponent(AdditionalTabComponent outputConsole) { + super("Output"); + + myOutputConsole = outputConsole; + final DefaultActionGroup outputActions = new DefaultActionGroup(); + outputActions.add(new OpenOutputAction(outputConsole)); + myOutputActions = outputActions; + } + + @NotNull + @Override + public JComponent getComponent() { + return myOutputConsole; + } + + @Override + public JComponent getPreferredFocusableComponent() { + return myOutputConsole.getPreferredFocusableComponent(); + } + + @Override + public ActionGroup getToolbarActions() { + return myOutputActions; + } +} diff --git a/plugins/xslt-debugger/src/org/intellij/plugins/xsltDebugger/ui/SmartStructureTracker.java b/plugins/xslt-debugger/src/org/intellij/plugins/xsltDebugger/ui/SmartStructureTracker.java new file mode 100644 index 000000000000..4673d55c559e --- /dev/null +++ b/plugins/xslt-debugger/src/org/intellij/plugins/xsltDebugger/ui/SmartStructureTracker.java @@ -0,0 +1,96 @@ +/* + * Copyright 2002-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.ui; + +import com.intellij.openapi.Disposable; +import com.intellij.openapi.application.ApplicationManager; +import com.intellij.util.Alarm; +import com.intellij.util.ui.tree.TreeModelAdapter; +import com.intellij.util.ui.tree.TreeUtil; + +import javax.swing.*; +import javax.swing.event.TreeModelEvent; +import javax.swing.tree.DefaultMutableTreeNode; +import javax.swing.tree.TreeNode; +import javax.swing.tree.TreePath; + +/** + * Created by IntelliJ IDEA. + * User: sweinreuter + * Date: 09.06.2007 + */ +public class SmartStructureTracker extends TreeModelAdapter { + private final JTree myEventTree; + private final Alarm myAlarm; + + public SmartStructureTracker(JTree eventTree, Disposable disposable) { + myEventTree = eventTree; + myAlarm = new Alarm(Alarm.ThreadToUse.SWING_THREAD, disposable); + } + + @Override + public void treeNodesInserted(TreeModelEvent e) { + final TreePath path = e.getTreePath(); + final Object child = e.getChildren()[0]; + if (path != null && child != null) { + myAlarm.cancelAllRequests(); + final Runnable runnable = new Runnable() { + public void run() { + myEventTree.expandPath(path); + TreeUtil.showRowCentered(myEventTree, myEventTree.getRowForPath(TreeUtil.getPathFromRoot((TreeNode)child)), false); + } + }; + myAlarm.addRequest(runnable, 300); + } + } + + @Override + public void treeNodesRemoved(TreeModelEvent e) { + final TreePath p = e.getTreePath(); + if (p != null) { + if (p.getPathCount() > 1) { + final Runnable runnable = new Runnable() { + public void run() { + DefaultMutableTreeNode last = (DefaultMutableTreeNode)p.getLastPathComponent(); + if (last.getChildCount() > 0) { + DefaultMutableTreeNode next = (DefaultMutableTreeNode)last.getFirstChild(); + while (next != null) { + boolean collapse = true; +// final int count = next.getChildCount(); +// if (count > 0) { +// for (int i = 0; i < count; i++) { +// final DefaultMutableTreeNode child = (DefaultMutableTreeNode)next.getChildAt(i); +// if (child instanceof GeneratedStructureModel.StructureNode) { +// if (((GeneratedStructureModel.StructureNode)child).isNew()) { +// collapse = false; +// } +// } +// } +// } + if (collapse) { + myEventTree.collapsePath(TreeUtil.getPathFromRoot(next)); + } + next = next.getNextSibling(); + } + } + } + }; + ApplicationManager.getApplication().invokeLater(runnable); + } + } + } +} diff --git a/plugins/xslt-debugger/src/org/intellij/plugins/xsltDebugger/ui/StructureTabComponent.java b/plugins/xslt-debugger/src/org/intellij/plugins/xsltDebugger/ui/StructureTabComponent.java new file mode 100644 index 000000000000..0d871b45626c --- /dev/null +++ b/plugins/xslt-debugger/src/org/intellij/plugins/xsltDebugger/ui/StructureTabComponent.java @@ -0,0 +1,68 @@ +package org.intellij.plugins.xsltDebugger.ui; + +import com.intellij.execution.process.ProcessHandler; +import com.intellij.ide.CommonActionsManager; +import com.intellij.openapi.Disposable; +import com.intellij.openapi.actionSystem.ActionGroup; +import com.intellij.openapi.actionSystem.DefaultActionGroup; +import com.intellij.openapi.util.Key; +import com.intellij.ui.treeStructure.Tree; +import org.intellij.plugins.xsltDebugger.ui.actions.HideWhitespaceAction; +import org.jetbrains.annotations.NotNull; + +import javax.swing.*; + +public class StructureTabComponent extends AbstractTabComponent { + private static final Key KEY = Key.create("STRUCTURE"); + + private final DefaultActionGroup myToolbarActions; + private final Tree myStructureTree; + private final GeneratedStructureModel myEventModel; + + private StructureTabComponent(Disposable disposable) { + super("Structure"); + + myEventModel = new GeneratedStructureModel(); + myStructureTree = new StructureTree(myEventModel); + myEventModel.addTreeModelListener(new SmartStructureTracker(myStructureTree, disposable)); + + final DefaultActionGroup structureActions = new DefaultActionGroup(); + final StructureTreeExpander expander = new StructureTreeExpander(myStructureTree); + final CommonActionsManager actionsManager = CommonActionsManager.getInstance(); + structureActions.add(new HideWhitespaceAction(myStructureTree, myEventModel)); + structureActions.add(actionsManager.createExpandAllAction(expander, myStructureTree)); + structureActions.add(actionsManager.createCollapseAllAction(expander, myStructureTree)); + + myToolbarActions = structureActions; + } + + public GeneratedStructureModel getEventModel() { + return myEventModel; + } + + public static StructureTabComponent create(ProcessHandler process, Disposable disposable) { + final StructureTabComponent component = new StructureTabComponent(disposable); + process.putUserData(KEY, component); + return component; + } + + public static StructureTabComponent getInstance(ProcessHandler process) { + return process.getUserData(KEY); + } + + @NotNull + @Override + public JComponent getComponent() { + return myStructureTree; + } + + @Override + public JComponent getPreferredFocusableComponent() { + return getComponent(); + } + + @Override + public ActionGroup getToolbarActions() { + return myToolbarActions; + } +} \ No newline at end of file diff --git a/plugins/xslt-debugger/src/org/intellij/plugins/xsltDebugger/ui/StructureTree.java b/plugins/xslt-debugger/src/org/intellij/plugins/xsltDebugger/ui/StructureTree.java new file mode 100644 index 000000000000..79773bfaccb4 --- /dev/null +++ b/plugins/xslt-debugger/src/org/intellij/plugins/xsltDebugger/ui/StructureTree.java @@ -0,0 +1,65 @@ +/* + * Copyright 2002-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.ui; + +import com.intellij.openapi.actionSystem.*; +import com.intellij.pom.Navigatable; +import com.intellij.ui.PopupHandler; +import com.intellij.ui.treeStructure.Tree; +import org.intellij.plugins.xsltDebugger.ui.actions.CopyValueAction; +import org.intellij.plugins.xsltDebugger.ui.actions.NavigateAction; + +import javax.swing.tree.DefaultMutableTreeNode; +import javax.swing.tree.TreePath; + +/** + * Created by IntelliJ IDEA. + * User: sweinreuter + * Date: 17.06.2007 + */ +public class StructureTree extends Tree implements TypeSafeDataProvider { + public StructureTree(GeneratedStructureModel model) { + super(model); + + setCellRenderer(new GeneratedStructureRenderer()); + setRootVisible(false); + setShowsRootHandles(true); + + final DefaultActionGroup structureContextActions = new DefaultActionGroup("StructureContext", true); + structureContextActions.add(NavigateAction.getInstance()); + structureContextActions.add(new CopyValueAction(this)); + PopupHandler.installFollowingSelectionTreePopup(this, structureContextActions, "XSLT.Debugger.GeneratedStructure", ActionManager.getInstance()); + } + + public void calcData(DataKey key, DataSink sink) { + if (key.equals(PlatformDataKeys.NAVIGATABLE)) { + final TreePath selection = getSelectionPath(); + if (selection != null) { + final Object o = selection.getLastPathComponent(); + if (o instanceof Navigatable) { + sink.put(PlatformDataKeys.NAVIGATABLE, (Navigatable)o); + } + } + } else if (key.equals(CopyValueAction.SELECTED_NODE)) { + final TreePath selection = getSelectionPath(); + if (selection != null) { + final Object o = selection.getLastPathComponent(); + sink.put(CopyValueAction.SELECTED_NODE, (DefaultMutableTreeNode)o); + } + } + } +} diff --git a/plugins/xslt-debugger/src/org/intellij/plugins/xsltDebugger/ui/StructureTreeExpander.java b/plugins/xslt-debugger/src/org/intellij/plugins/xsltDebugger/ui/StructureTreeExpander.java new file mode 100644 index 000000000000..7e94e6c706a4 --- /dev/null +++ b/plugins/xslt-debugger/src/org/intellij/plugins/xsltDebugger/ui/StructureTreeExpander.java @@ -0,0 +1,51 @@ +/* + * Copyright 2002-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.ui; + +import com.intellij.ide.TreeExpander; +import com.intellij.util.ui.tree.TreeUtil; + +import javax.swing.*; + +/** + * Created by IntelliJ IDEA. + * User: sweinreuter + * Date: 28.11.2007 + */ +public class StructureTreeExpander implements TreeExpander { + private final JTree myTree; + + public StructureTreeExpander(JTree tree) { + myTree = tree; + } + + public void expandAll() { + TreeUtil.expandAll(myTree); + } + + public boolean canExpand() { + return true; + } + + public void collapseAll() { + TreeUtil.collapseAll(myTree, 1); + } + + public boolean canCollapse() { + return true; + } +} diff --git a/plugins/xslt-debugger/src/org/intellij/plugins/xsltDebugger/ui/actions/CopyValueAction.java b/plugins/xslt-debugger/src/org/intellij/plugins/xsltDebugger/ui/actions/CopyValueAction.java new file mode 100644 index 000000000000..6bc6a4044524 --- /dev/null +++ b/plugins/xslt-debugger/src/org/intellij/plugins/xsltDebugger/ui/actions/CopyValueAction.java @@ -0,0 +1,69 @@ +/* + * Copyright 2002-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.ui.actions; + +import com.intellij.openapi.actionSystem.ActionManager; +import com.intellij.openapi.actionSystem.AnAction; +import com.intellij.openapi.actionSystem.AnActionEvent; +import com.intellij.openapi.actionSystem.DataKey; +import com.intellij.openapi.ide.CopyPasteManager; +import org.intellij.plugins.xsltDebugger.rt.engine.OutputEventQueue; +import org.intellij.plugins.xsltDebugger.ui.GeneratedStructureModel; + +import javax.swing.*; +import javax.swing.tree.DefaultMutableTreeNode; +import java.awt.datatransfer.StringSelection; + +@SuppressWarnings({ "ComponentNotRegistered" }) +public class CopyValueAction extends AnAction { + public static final DataKey SELECTED_NODE = DataKey.create("SELECTED_NODE"); + + public CopyValueAction(JComponent component) { + final AnAction action = ActionManager.getInstance().getAction("$Copy"); + if (action != null) { + copyFrom(action); + registerCustomShortcutSet(getShortcutSet(), component); + } + } + + @Override + public void update(AnActionEvent e) { + e.getPresentation().setEnabled(isEnabled(e)); + } + + public void actionPerformed(AnActionEvent e) { + final DefaultMutableTreeNode node = e.getData(SELECTED_NODE); + if (node instanceof GeneratedStructureModel.StructureNode) { + final GeneratedStructureModel.StructureNode structureNode = (GeneratedStructureModel.StructureNode)node; + final OutputEventQueue.NodeEvent event = structureNode.getUserObject(); + setClipboardData(event.getValue()); + } + } + + private static void setClipboardData(String value) { + CopyPasteManager.getInstance().setContents(new StringSelection(value)); + } + + protected static boolean isEnabled(AnActionEvent e) { + final DefaultMutableTreeNode node = e.getData(SELECTED_NODE); + if (node instanceof GeneratedStructureModel.StructureNode) { + final GeneratedStructureModel.StructureNode structureNode = (GeneratedStructureModel.StructureNode)node; + final OutputEventQueue.NodeEvent event = structureNode.getUserObject(); + return event != null && event.getValue() != null; + } + return false; + } +} diff --git a/plugins/xslt-debugger/src/org/intellij/plugins/xsltDebugger/ui/actions/HideWhitespaceAction.java b/plugins/xslt-debugger/src/org/intellij/plugins/xsltDebugger/ui/actions/HideWhitespaceAction.java new file mode 100644 index 000000000000..10899b5d92fa --- /dev/null +++ b/plugins/xslt-debugger/src/org/intellij/plugins/xsltDebugger/ui/actions/HideWhitespaceAction.java @@ -0,0 +1,47 @@ +/* + * Copyright 2002-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.ui.actions; + +import com.intellij.ide.util.treeView.TreeState; +import com.intellij.openapi.actionSystem.AnActionEvent; +import com.intellij.openapi.actionSystem.ToggleAction; +import com.intellij.openapi.util.IconLoader; +import com.intellij.ui.treeStructure.Tree; +import org.intellij.plugins.xsltDebugger.ui.GeneratedStructureModel; + +@SuppressWarnings({ "ComponentNotRegistered" }) +public class HideWhitespaceAction extends ToggleAction { + private final Tree myStructureTree; + private final GeneratedStructureModel myEventModel; + + public HideWhitespaceAction(Tree structureTree, GeneratedStructureModel eventModel) { + super("Hide Whitespace Nodes"); + myStructureTree = structureTree; + myEventModel = eventModel; + + getTemplatePresentation().setIcon(IconLoader.getIcon("filterWhitespace.png")); + } + + public boolean isSelected(AnActionEvent e) { + return myEventModel.isFilterWhitespace(); + } + + public void setSelected(AnActionEvent e, boolean state) { + final TreeState treeState = TreeState.createOn(myStructureTree); + myEventModel.setFilterWhitespace(state); + treeState.applyTo(myStructureTree); + } +} \ No newline at end of file diff --git a/plugins/xslt-debugger/src/org/intellij/plugins/xsltDebugger/ui/actions/NavigateAction.java b/plugins/xslt-debugger/src/org/intellij/plugins/xsltDebugger/ui/actions/NavigateAction.java new file mode 100644 index 000000000000..f30df107a3ab --- /dev/null +++ b/plugins/xslt-debugger/src/org/intellij/plugins/xsltDebugger/ui/actions/NavigateAction.java @@ -0,0 +1,29 @@ +/* + * Copyright 2002-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.ui.actions; + +import com.intellij.openapi.actionSystem.ActionManager; +import com.intellij.openapi.actionSystem.AnAction; +import com.intellij.openapi.actionSystem.IdeActions; + +public class NavigateAction { + private NavigateAction() { + } + + public static AnAction getInstance() { + return ActionManager.getInstance().getAction(IdeActions.ACTION_EDIT_SOURCE); + } +} \ No newline at end of file diff --git a/plugins/xslt-debugger/src/org/intellij/plugins/xsltDebugger/ui/actions/OpenOutputAction.java b/plugins/xslt-debugger/src/org/intellij/plugins/xsltDebugger/ui/actions/OpenOutputAction.java new file mode 100644 index 000000000000..f4ad22123c9a --- /dev/null +++ b/plugins/xslt-debugger/src/org/intellij/plugins/xsltDebugger/ui/actions/OpenOutputAction.java @@ -0,0 +1,65 @@ +/* + * Copyright 2002-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.ui.actions; + +import com.intellij.diagnostic.logging.AdditionalTabComponent; +import com.intellij.ide.DataManager; +import com.intellij.openapi.actionSystem.AnAction; +import com.intellij.openapi.actionSystem.AnActionEvent; +import com.intellij.openapi.actionSystem.PlatformDataKeys; +import com.intellij.openapi.editor.Editor; +import com.intellij.openapi.fileEditor.FileEditorManager; +import com.intellij.openapi.util.IconLoader; +import com.intellij.openapi.vcs.vfs.VcsFileSystem; +import com.intellij.openapi.vcs.vfs.VcsVirtualFile; + +import java.io.UnsupportedEncodingException; +import java.nio.charset.Charset; + +@SuppressWarnings({ "ComponentNotRegistered" }) +public class OpenOutputAction extends AnAction { + private final AdditionalTabComponent myConsole; + + public OpenOutputAction(AdditionalTabComponent console) { + super("Open in Editor"); + myConsole = console; + getTemplatePresentation().setIcon(IconLoader.getIcon("/actions/export.png")); + } + + public void actionPerformed(AnActionEvent e) { + final Editor editor = PlatformDataKeys.EDITOR.getData(DataManager.getInstance().getDataContext(myConsole.getComponent())); + if (editor != null) { + try { + final byte[] content = editor.getDocument().getText().getBytes("UTF-8"); + final String extension = "xml"; // TODO: get from output type + final VcsVirtualFile file = new VcsVirtualFile("XSLT Output." + extension, content, null, VcsFileSystem.getInstance()) { + @Override + public Charset getCharset() { + return Charset.forName("UTF-8"); + } + }; + FileEditorManager.getInstance(PlatformDataKeys.PROJECT.getData(e.getDataContext())).openFile(file, true); + } catch (UnsupportedEncodingException e1) { + throw new AssertionError(e); + } + } + } + + public void update(AnActionEvent e) { + final Editor editor = PlatformDataKeys.EDITOR.getData(DataManager.getInstance().getDataContext(myConsole.getComponent())); + e.getPresentation().setEnabled(editor != null && editor.getDocument().getTextLength() > 0); + } +} \ No newline at end of file diff --git a/plugins/xslt-debugger/src/org/intellij/plugins/xsltDebugger/ui/actions/filterWhitespace.png b/plugins/xslt-debugger/src/org/intellij/plugins/xsltDebugger/ui/actions/filterWhitespace.png new file mode 100644 index 000000000000..6efd2770e9e9 Binary files /dev/null and b/plugins/xslt-debugger/src/org/intellij/plugins/xsltDebugger/ui/actions/filterWhitespace.png differ diff --git a/plugins/xslt-debugger/src/org/intellij/plugins/xsltDebugger/ui/xmlComment.png b/plugins/xslt-debugger/src/org/intellij/plugins/xsltDebugger/ui/xmlComment.png new file mode 100644 index 000000000000..48636008c85b Binary files /dev/null and b/plugins/xslt-debugger/src/org/intellij/plugins/xsltDebugger/ui/xmlComment.png differ diff --git a/plugins/xslt-debugger/xslt-debugger.iml b/plugins/xslt-debugger/xslt-debugger.iml new file mode 100644 index 000000000000..4f5ffabb0fc6 --- /dev/null +++ b/plugins/xslt-debugger/xslt-debugger.iml @@ -0,0 +1,28 @@ + + + + + + + + + + + + + + + + + + + + + + + + + + + +