/** * Copyright (c) 2005-2011 by Appcelerator, Inc. All Rights Reserved. * Licensed under the terms of the Eclipse Public License (EPL). * Please see the license.txt included with this distribution for details. * Any modifications to this file must keep this entire header intact. */ /* * Author: atotic * Created on Apr 22, 2004 */ package org.python.pydev.debug.model; import java.io.ByteArrayInputStream; import java.io.File; import java.io.IOException; import java.io.UnsupportedEncodingException; import java.net.URLDecoder; import java.util.ArrayList; import java.util.List; import javax.xml.parsers.ParserConfigurationException; import javax.xml.parsers.SAXParser; import javax.xml.parsers.SAXParserFactory; import org.eclipse.core.runtime.CoreException; import org.eclipse.core.runtime.IPath; import org.eclipse.core.runtime.IStatus; import org.eclipse.core.runtime.Path; import org.eclipse.debug.core.model.IStackFrame; import org.python.pydev.debug.core.PydevDebugPlugin; import org.python.pydev.debug.newconsole.EvaluateDebugConsoleExpression; import org.xml.sax.Attributes; import org.xml.sax.SAXException; import org.xml.sax.helpers.DefaultHandler; import com.aptana.shared_core.io.FileUtils; /** * Translate XML protocol responses into Py structures. * * Things get more complex than I'd like when complex Py structures get built. */ public class XMLUtils { static SAXParserFactory parserFactory = SAXParserFactory.newInstance(); static SAXParser getSAXParser() throws CoreException { SAXParser parser = null; try { synchronized (parserFactory) { parser = parserFactory.newSAXParser(); } } catch (ParserConfigurationException e) { throw new CoreException(PydevDebugPlugin.makeStatus(IStatus.ERROR, "Unexpected XML SAX error", e)); } catch (SAXException e) { throw new CoreException(PydevDebugPlugin.makeStatus(IStatus.ERROR, "Unexpected XML SAX error", e)); } return parser; } private static String decode(String value) { if (value != null) { try { return URLDecoder.decode(value, "UTF-8"); } catch (UnsupportedEncodingException e) { throw new RuntimeException(e); } } return null; } /** * SAX parser for thread info * <xml><thread name="name" id="id"/><xml> */ static class XMLToThreadInfo extends DefaultHandler { public AbstractDebugTarget target; public List<PyThread> threads = new ArrayList<PyThread>(); public XMLToThreadInfo(AbstractDebugTarget target) { this.target = target; } public void startElement(String uri, String localName, String qName, Attributes attributes) throws SAXException { if (qName.equals("thread")) { String name = attributes.getValue("name"); String id = attributes.getValue("id"); name = decode(name); threads.add(new PyThread(target, name, id)); } } } /** * Creates IThread[] from the XML response */ static public PyThread[] ThreadsFromXML(AbstractDebugTarget target, String payload) throws CoreException { try { SAXParser parser = getSAXParser(); XMLToThreadInfo info = new XMLToThreadInfo(target); parser.parse(new ByteArrayInputStream(payload.getBytes()), info); return (PyThread[]) info.threads.toArray(new PyThread[0]); } catch (CoreException e) { throw e; } catch (SAXException e) { throw new CoreException(PydevDebugPlugin.makeStatus(IStatus.ERROR, "Unexpected XML error", e)); } catch (IOException e) { throw new CoreException(PydevDebugPlugin.makeStatus(IStatus.ERROR, "Unexpected XML error", e)); } } /** * Creates a variable from XML attributes * <var name="self" type="ObjectType" value="<DeepThread>"/> */ static PyVariable createVariable(AbstractDebugTarget target, IVariableLocator locator, Attributes attributes) { PyVariable var; String name = attributes.getValue("name"); String type = attributes.getValue("type"); String value = attributes.getValue("value"); try { if (value != null) { value = URLDecoder.decode(value, "UTF-8"); } } catch (Exception e) { throw new RuntimeException(e); } String isContainer = attributes.getValue("isContainer"); if ("True".equals(isContainer)) { var = new PyVariableCollection(target, name, type, value, locator); } else { var = new PyVariable(target, name, type, value, locator); } return var; } /** * XMLToStack SAX traverse */ static class XMLToStackInfo extends DefaultHandler { public PyThread thread; public String stop_reason; public List<IStackFrame> stack = new ArrayList<IStackFrame>(); public List<PyVariable> locals; public AbstractDebugTarget target; PyStackFrame currentFrame; public XMLToStackInfo(AbstractDebugTarget target) { this.target = target; } private void startThread(Attributes attributes) throws SAXException { String target_id = attributes.getValue("id"); thread = target.findThreadByID(target_id); if (thread == null) { throw new SAXException("Thread not found (" + target_id + ")"); // can happen when debugger has been destroyed } stop_reason = attributes.getValue("stop_reason"); } private void startFrame(Attributes attributes) { String name = attributes.getValue("name"); String id = attributes.getValue("id"); String file = attributes.getValue("file"); try { if (file != null) { file = URLDecoder.decode(file, "UTF-8"); File tempFile = new File(file); if (tempFile.exists()) { file = FileUtils.getFileAbsolutePath(tempFile); } } } catch (Exception e) { throw new RuntimeException(e); } String line = attributes.getValue("line"); IPath filePath = new Path(file); // Try to recycle old stack objects currentFrame = thread.findStackFrameByID(id); if (currentFrame == null) { currentFrame = new PyStackFrame(thread, id, name, filePath, Integer.parseInt(line), target); } else { currentFrame.setName(name); currentFrame.setPath(filePath); currentFrame.setLine(Integer.parseInt(line)); } stack.add(currentFrame); } /** * Assign stack frames to thread. * Assign global variables to thread * Assign local variables to stack frame */ public void startElement(String uri, String localName, String qName, Attributes attributes) throws SAXException { /* <xml> <thread id="id"/> <frame id="id" name="functionName " file="file" line="line"> @deprecated: variables are no longer returned in this request (they are gotten later in asynchronously to speed up the debugger). <var scope="local" name="self" type="ObjectType" value="<DeepThread>"/> </frame>* */ if (qName.equals("thread")) { startThread(attributes); } else if (qName.equals("frame")) { startFrame(attributes); } } public void endElement(String uri, String localName, String qName) throws SAXException { } } /** * @param payload * @return an array of [thread_id, stop_reason, IStackFrame[]] */ public static Object[] XMLToStack(AbstractDebugTarget target, String payload) throws CoreException { IStackFrame[] stack; Object[] retVal = new Object[3]; try { SAXParser parser = getSAXParser(); XMLToStackInfo info = new XMLToStackInfo(target); parser.parse(new ByteArrayInputStream(payload.getBytes()), info); stack = info.stack.toArray(new IStackFrame[0]); retVal[0] = info.thread; retVal[1] = info.stop_reason; retVal[2] = stack; } catch (CoreException e) { throw e; } catch (SAXException e) { throw new CoreException(PydevDebugPlugin.makeStatus(IStatus.ERROR, "Unexpected XML error reading:" + payload, e)); } catch (IOException e) { throw new CoreException(PydevDebugPlugin.makeStatus(IStatus.ERROR, "Unexpected IO error reading xml:" + payload, e)); } return retVal; } /** * Processes CMD_GET_VARIABLE return * */ static class XMLToVariableInfo extends DefaultHandler { private AbstractDebugTarget target; private IVariableLocator locator; public List<PyVariable> vars; public XMLToVariableInfo(AbstractDebugTarget target, IVariableLocator locator) { this.target = target; this.locator = locator; vars = new ArrayList<PyVariable>(); } public void startElement(String uri, String localName, String qName, Attributes attributes) throws SAXException { // <var name="self" type="ObjectType" value="<DeepThread>"/> // create a local variable, and add it to locals if (qName.equals("var")) vars.add(createVariable(target, locator, attributes)); } } public static PyVariable[] XMLToVariables(AbstractDebugTarget target, IVariableLocator locator, String payload) throws CoreException { try { SAXParser parser = getSAXParser(); XMLToVariableInfo info = new XMLToVariableInfo(target, locator); parser.parse(new ByteArrayInputStream(payload.getBytes()), info); PyVariable[] vars = new PyVariable[info.vars.size()]; for (int i = 0; i < info.vars.size(); i++) vars[i] = (PyVariable) info.vars.get(i); return vars; } catch (CoreException e) { throw e; } catch (SAXException e) { throw new CoreException(PydevDebugPlugin.makeStatus(IStatus.ERROR, "Unexpected XML error", e)); } catch (IOException e) { throw new CoreException(PydevDebugPlugin.makeStatus(IStatus.ERROR, "Unexpected XML error", e)); } } /** * Processes CMD_GET_COMPLETIONS return * */ static class XMLToCompletionsInfo extends DefaultHandler { public List<Object[]> completions; public XMLToCompletionsInfo() { completions = new ArrayList<Object[]>(); } public void startElement(String uri, String localName, String qName, Attributes attributes) throws SAXException { // <comp p0="%s" p1="%s" p2="%s" p3="%s"/> if (qName.equals("comp")) { Object[] comp = new Object[] { decode(attributes.getValue("p0")), decode(attributes.getValue("p1")), decode(attributes.getValue("p2")), decode(attributes.getValue("p3")), }; completions.add(comp); } } } public static List<Object[]> convertXMLcompletionsFromConsole(String payload) throws CoreException { try { SAXParser parser = getSAXParser(); XMLToCompletionsInfo info = new XMLToCompletionsInfo(); parser.parse(new ByteArrayInputStream(payload.getBytes()), info); return info.completions; } catch (CoreException e) { throw e; } catch (SAXException e) { throw new CoreException(PydevDebugPlugin.makeStatus(IStatus.ERROR, "Unexpected XML error", e)); } catch (IOException e) { throw new CoreException(PydevDebugPlugin.makeStatus(IStatus.ERROR, "Unexpected XML error", e)); } } /** * Creates an object of * EvaluateDebugConsoleExpression.PydevDebugConsoleMessage. Parse the XML in * the below mentioned format * <xml> * <output message = console_output_message></output> * <error message = console_error_message></error> * <more>true/false</more> * </xml> * * @author hussain.bohra */ static class DebugConsoleMessageInfo extends DefaultHandler { private EvaluateDebugConsoleExpression.PydevDebugConsoleMessage debugConsoleMessage; private String attrValue; @Override public void characters(char[] ch, int start, int length) throws SAXException { attrValue = new String(ch, start, length); } @Override public void startElement(String uri, String localName, String qName, Attributes attributes) { boolean isError = true; if (qName.equalsIgnoreCase("MORE")) { return; } if (qName.equalsIgnoreCase("OUTPUT")) { isError = false; } for (int i = 0; i < attributes.getLength(); i++) { if (attributes.getQName(i).equalsIgnoreCase("MESSAGE")) { String outputMessage = attributes.getValue(i); debugConsoleMessage.appendMessage(outputMessage, isError); } } } @Override public void endElement(String uri, String localName, String qName) throws SAXException { if (qName.equalsIgnoreCase("MORE")) { if (attrValue.equals("True")) { debugConsoleMessage.setMore(true); } else { debugConsoleMessage.setMore(false); } } } public DebugConsoleMessageInfo() { debugConsoleMessage = new EvaluateDebugConsoleExpression.PydevDebugConsoleMessage(); } } /** * Get an instance of a SAXParser and create a new DebugConsoleMessageInfo object. * * Call the parser passing it a DebugConsoleMessageInfo Object * * @param payload * @return * @throws CoreException */ public static EvaluateDebugConsoleExpression.PydevDebugConsoleMessage getConsoleMessage(String payload) throws CoreException { EvaluateDebugConsoleExpression.PydevDebugConsoleMessage debugConsoleMessage = new EvaluateDebugConsoleExpression.PydevDebugConsoleMessage(); try { SAXParser parser = getSAXParser(); DebugConsoleMessageInfo info = new DebugConsoleMessageInfo(); parser.parse(new ByteArrayInputStream(payload.getBytes()), info); debugConsoleMessage = info.debugConsoleMessage; } catch (SAXException e) { throw new CoreException(PydevDebugPlugin.makeStatus(IStatus.ERROR, "Unexpected XML error", e)); } catch (IOException e) { throw new CoreException(PydevDebugPlugin.makeStatus(IStatus.ERROR, "Unexpected XML error", e)); } return debugConsoleMessage; } }