| /* |
| * 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<Debugger.StyleFrame, StyleElement> 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<Debugger.Variable> getVariables() { |
| assert isValid(); |
| |
| final ArrayList<Debugger.Variable> variables = new ArrayList<Debugger.Variable>(); |
| 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<Debugger.Variable> 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<Node> list = new ArrayList<Node>(); |
| 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) { |
| debug(e); |
| 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 if (v != null) { |
| return new MyValue(v.evaluateAsString(context), v.getDataType()); |
| } else { |
| return new MyValue("", "<uninitialized>"); |
| } |
| } |
| } |
| |
| 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(); |
| } |
| } |
| } |