/* * #%~ * org.overture.ide.debug * %% * Copyright (C) 2008 - 2014 Overture * %% * This program is free software: you can redistribute it and/or modify * it under the terms of the GNU General Public License as * published by the Free Software Foundation, either version 3 of the * License, or (at your option) any later version. * * 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 * GNU General Public License for more details. * * You should have received a copy of the GNU General Public * License along with this program. If not, see * <http://www.gnu.org/licenses/gpl-3.0.html>. * #~% */ package org.overture.ide.debug.core.dbgp.internal.utils; import java.net.URI; import java.net.URISyntaxException; import java.util.List; import java.util.Vector; import org.overture.ide.debug.core.IDebugConstants; import org.overture.ide.debug.core.VdmDebugPlugin; import org.overture.ide.debug.core.dbgp.IDbgpProperty; import org.overture.ide.debug.core.dbgp.IDbgpSessionInfo; import org.overture.ide.debug.core.dbgp.IDbgpStatus; import org.overture.ide.debug.core.dbgp.IDbgpStatusInterpreterThreadState; import org.overture.ide.debug.core.dbgp.breakpoints.IDbgpBreakpoint; import org.overture.ide.debug.core.dbgp.exceptions.DbgpException; import org.overture.ide.debug.core.dbgp.exceptions.DbgpProtocolException; import org.overture.ide.debug.core.dbgp.internal.DbgpFeature; import org.overture.ide.debug.core.dbgp.internal.DbgpProperty; import org.overture.ide.debug.core.dbgp.internal.DbgpSessionInfo; import org.overture.ide.debug.core.dbgp.internal.DbgpStackLevel; import org.overture.ide.debug.core.dbgp.internal.DbgpStatus; import org.overture.ide.debug.core.dbgp.internal.DbgpStatusInterpreterThreadState; import org.overture.ide.debug.core.dbgp.internal.breakpoints.DbgpCallBreakpoint; import org.overture.ide.debug.core.dbgp.internal.breakpoints.DbgpConditionalBreakpoint; import org.overture.ide.debug.core.dbgp.internal.breakpoints.DbgpExceptionBreakpoint; import org.overture.ide.debug.core.dbgp.internal.breakpoints.DbgpLineBreakpoint; import org.overture.ide.debug.core.dbgp.internal.breakpoints.DbgpReturnBreakpoint; import org.overture.ide.debug.core.dbgp.internal.breakpoints.DbgpWatchBreakpoint; import org.w3c.dom.Element; import org.w3c.dom.Node; import org.w3c.dom.NodeList; //import org.eclipse.dltk.compiler.util.Util; //import org.eclipse.dltk.core.DLTKCore; import org.eclipse.osgi.util.NLS; public class DbgpXmlEntityParser extends DbgpXmlParser { private static final IDbgpProperty[] NO_CHILDREN = new IDbgpProperty[0]; private static final String ENCODING_NONE = "none"; //$NON-NLS-1$ private static final String ENCODING_BASE64 = "base64"; //$NON-NLS-1$ public static final String TAG_PROPERTY = "property"; //$NON-NLS-1$ protected DbgpXmlEntityParser() { } private static final String ATTR_LEVEL = "level"; //$NON-NLS-1$ private static final String ATTR_CMDBEGIN = "cmdbegin"; //$NON-NLS-1$ private static final String ATTR_CMDEND = "cmdend"; //$NON-NLS-1$ private static final String ATTR_LINENO = "lineno"; //$NON-NLS-1$ private static final String ATTR_FILENAME = "filename"; //$NON-NLS-1$ private static final String ATTR_WHERE = "where"; //$NON-NLS-1$ public static DbgpStackLevel parseStackLevel(Element element) throws DbgpException { int level = Integer.parseInt(element.getAttribute(ATTR_LEVEL)); String cmdBegin = element.getAttribute(ATTR_CMDBEGIN); String cmdEnd = element.getAttribute(ATTR_CMDEND); int beginLine = -1; int beginColumn = -1; int endLine = -1; int endColumn = -1; if (cmdBegin.length() != 0 && cmdEnd.length() != 0) { beginLine = parseLine(cmdBegin); beginColumn = parseColumn(cmdBegin); endLine = parseLine(cmdEnd); endColumn = parseColumn(cmdEnd); } int lineNumber = Integer.parseInt(element.getAttribute(ATTR_LINENO)); /** * TODO Check ATTR_TYPE who knows when. According to the http://xdebug.org/docs-dbgp.php#stack-get * <code>Valid values are "file" or "eval"</code>, but Tcl debugger also sends "source" and "console". */ final URI fileUri = parseURI(element.getAttribute(ATTR_FILENAME)); final String where = element.getAttribute(ATTR_WHERE); return new DbgpStackLevel(fileUri, where, level, lineNumber, beginLine, beginColumn, endLine, endColumn); } private static final String FILE_SCHEME_PREFIX = IDebugConstants.FILE_SCHEME + ":///"; //$NON-NLS-1$ private static URI parseURI(String fileName) { /* * ActiveState python debugger on windows sends URI as "file:///C|/path/to/file.py" we need to convert it. */ if (fileName.startsWith(FILE_SCHEME_PREFIX)) { final int pos = FILE_SCHEME_PREFIX.length(); if (fileName.length() > pos + 3) { if (Character.isLetter(fileName.charAt(pos)) && fileName.charAt(pos + 1) == '|' && fileName.charAt(pos + 2) == '/') { fileName = fileName.substring(0, pos + 1) + ':' + fileName.substring(pos + 2); } } } try { return URI.create(fileName); } catch (IllegalArgumentException e) { if (VdmDebugPlugin.DEBUG) { e.printStackTrace(); } } try { return new URI(IDebugConstants.UNKNOWN_SCHEME, Util.EMPTY_STRING, Util.EMPTY_STRING, fileName); } catch (URISyntaxException e) { if (VdmDebugPlugin.DEBUG) { e.printStackTrace(); } } try { return new URI(IDebugConstants.UNKNOWN_SCHEME, Util.EMPTY_STRING, Util.EMPTY_STRING, "unknown");//$NON-NLS-1$ } catch (URISyntaxException e) { throw new IllegalArgumentException(e.getMessage()); } } private static final String ATTR_FEATURE_NAME = "feature_name"; //$NON-NLS-1$ private static final String ATTR_SUPPORTED = "supported"; //$NON-NLS-1$ public static DbgpFeature parseFeature(Element element) throws DbgpProtocolException { String name = element.getAttribute(ATTR_FEATURE_NAME); boolean supported = makeBoolean(element.getAttribute(ATTR_SUPPORTED)); String value = parseContent(element); return new DbgpFeature(supported, name, value); } private static final String ATTR_NAME = "name"; //$NON-NLS-1$ private static final String ATTR_FULLNAME = "fullname"; //$NON-NLS-1$ private static final String ATTR_TYPE = "type"; //$NON-NLS-1$ private static final String ATTR_CHILDREN = "children"; //$NON-NLS-1$ private static final String ATTR_NUMCHILDREN = "numchildren"; //$NON-NLS-1$ private static final String ATTR_CONSTANT = "constant"; //$NON-NLS-1$ private static final String ATTR_KEY = "key"; //$NON-NLS-1$ private static final String ATTR_PAGE = "page"; //$NON-NLS-1$ private static final String ATTR_PAGE_SIZE = "pagesize"; //$NON-NLS-1$ private static final String ATTR_ADDRESS = "address"; //$NON-NLS-1$ public static IDbgpProperty parseProperty(Element property) { /* * attributes: name, fullname, type, children, numchildren, constant, encoding, size, key, address */ // may exist as an attribute of the property or as child element final String name = getFromChildOrAttr(property, ATTR_NAME); final String fullName = getFromChildOrAttr(property, ATTR_FULLNAME); final String type = property.getAttribute(ATTR_TYPE); // hasChildren boolean hasChildren = false; if (property.hasAttribute(ATTR_CHILDREN)) { hasChildren = makeBoolean(property.getAttribute(ATTR_CHILDREN)); } // Children count int childrenCount = -1; if (property.hasAttribute(ATTR_NUMCHILDREN)) { childrenCount = Integer.parseInt(property.getAttribute(ATTR_NUMCHILDREN)); } // Page int page = 0; if (property.hasAttribute(ATTR_PAGE)) { page = Integer.parseInt(property.getAttribute(ATTR_PAGE)); } // Page Size int pagesize = -1; if (property.hasAttribute(ATTR_PAGE_SIZE)) { pagesize = Integer.parseInt(property.getAttribute(ATTR_PAGE_SIZE)); } // Constant boolean constant = false; if (property.hasAttribute(ATTR_CONSTANT)) { constant = makeBoolean(property.getAttribute(ATTR_CONSTANT)); } // Key String key = null; if (property.hasAttribute(ATTR_KEY)) { key = property.getAttribute(ATTR_KEY); } // memory address String address = null; if (property.hasAttribute(ATTR_ADDRESS)) { address = property.getAttribute(ATTR_ADDRESS); } // Value String value = ""; //$NON-NLS-1$ NodeList list = property.getElementsByTagName("value"); //$NON-NLS-1$ if (list.getLength() == 0) { value = getEncodedValue(property); } else { value = getEncodedValue((Element) list.item(0)); } // Children IDbgpProperty[] availableChildren = NO_CHILDREN; if (hasChildren) { // final NodeList children = property // .getElementsByTagName(TAG_PROPERTY); List<Element> children = getChildElements(property); final int length = children.size();// children.getLength(); if (length > 0) { availableChildren = new IDbgpProperty[length]; for (int i = 0; i < length; ++i) { Element child = (Element) children.get(i); availableChildren[i] = parseProperty(child); } } } if (childrenCount < 0) { childrenCount = availableChildren.length; } return new DbgpProperty(name, fullName, type, value, childrenCount, hasChildren, constant, key, address, availableChildren, page, pagesize); } private static final String ATTR_REASON = "reason"; //$NON-NLS-1$ private static final String ATTR_STATUS = "status"; //$NON-NLS-1$ private static final String ELEM_INTERNAL = "internal"; private static final String ATTR_THREAD_ID = "threadId"; private static final String ATTR_THREAD_NAME = "threadName"; private static final String ATTR_THREAD_STATE = "threadState"; public static IDbgpStatus parseStatus(Element element) throws DbgpProtocolException { String status = element.getAttribute(ATTR_STATUS); String reason = element.getAttribute(ATTR_REASON); IDbgpStatusInterpreterThreadState interpreterThreadState = null; NodeList internalElements = element.getElementsByTagName(ELEM_INTERNAL); if (internalElements.getLength() > 0 && internalElements.item(0).getNodeType() == Node.ELEMENT_NODE) { Element internalElement = (Element) internalElements.item(0); int threadId = Integer.parseInt(internalElement.getAttribute(ATTR_THREAD_ID)); String name = internalElement.getAttribute(ATTR_THREAD_NAME); String tState = internalElement.getAttribute(ATTR_THREAD_STATE); interpreterThreadState = DbgpStatusInterpreterThreadState.parse(threadId, name, tState); } return DbgpStatus.parse(status, reason, interpreterThreadState); } private static final String LINE_BREAKPOINT = "line"; //$NON-NLS-1$ private static final String CALL_BREAKPOINT = "call"; //$NON-NLS-1$ private static final String RETURN_BREAKPOINT = "return"; //$NON-NLS-1$ private static final String EXCEPTION_BREAKPOINT = "exception"; //$NON-NLS-1$ private static final String CONDITIONAL_BREAKPOINT = "conditional"; //$NON-NLS-1$ private static final String WATCH_BREAKPOINT = "watch"; //$NON-NLS-1$ private static final String ATTR_ID = "id"; //$NON-NLS-1$ private static final String ATTR_STATE = "state"; //$NON-NLS-1$ private static final String ATTR_HIT_COUNT = "hit_count"; //$NON-NLS-1$ private static final String ATTR_HIT_VALUE = "hit_value"; //$NON-NLS-1$ private static final String ATTR_HIT_CONDITION = "hit_condition"; //$NON-NLS-1$ private static final String ATTR_LINE = "line"; //$NON-NLS-1$ private static final String ATTR_FUNCTION = "function"; //$NON-NLS-1$ private static final String ATTR_EXCEPTION = "exception"; //$NON-NLS-1$ private static final String ATTR_EXPRESSION = "expression"; //$NON-NLS-1$ private static List<Element> getChildElements(Element node) { List<Element> elements = new Vector<Element>(); NodeList childs = node.getChildNodes(); for (int j = 0; j < childs.getLength(); j++) { // Node cNode = childs.item(j); if (childs.item(j).getNodeName().equals(TAG_PROPERTY) && childs.item(j).getNodeType() == Node.ELEMENT_NODE) { elements.add((Element) childs.item(j)); } } return elements; } public static IDbgpBreakpoint parseBreakpoint(Element element) { // ActiveState Tcl // ActiveState Python // <response xmlns="urn:debugger_protocol_v1" command="breakpoint_get" // transaction_id="1"> // <breakpoint id="1" // type="line" // filename="c:\distrib\dbgp\test\test1.py" // lineno="8" // state="enabled" // temporary="0"> // </breakpoint> // </response> String type = element.getAttribute(ATTR_TYPE); String id = element.getAttribute(ATTR_ID); boolean enabled = element.getAttribute(ATTR_STATE).equals("enabled"); //$NON-NLS-1$ // not all dbgp implementations have these int hitCount = getIntAttribute(element, ATTR_HIT_COUNT, 0); int hitValue = getIntAttribute(element, ATTR_HIT_VALUE, 0); String hitCondition = getStringAttribute(element, ATTR_HIT_CONDITION); if (type.equals(LINE_BREAKPOINT)) { String fileName = element.getAttribute(ATTR_FILENAME); // ActiveState's dbgp implementation is slightly inconsistent String lineno = element.getAttribute(ATTR_LINENO); if ("".equals(lineno)) { //$NON-NLS-1$ lineno = element.getAttribute(ATTR_LINE); } int lineNumber = Integer.parseInt(lineno); return new DbgpLineBreakpoint(id, enabled, hitValue, hitCount, hitCondition, fileName, lineNumber); } else if (type.equals(CALL_BREAKPOINT)) { String function = element.getAttribute(ATTR_FUNCTION); return new DbgpCallBreakpoint(id, enabled, hitValue, hitCount, hitCondition, function); } else if (type.equals(RETURN_BREAKPOINT)) { String function = element.getAttribute(ATTR_FUNCTION); return new DbgpReturnBreakpoint(id, enabled, hitValue, hitCount, hitCondition, function); } else if (type.equals(EXCEPTION_BREAKPOINT)) { String exception = element.getAttribute(ATTR_EXCEPTION); return new DbgpExceptionBreakpoint(id, enabled, hitValue, hitCount, hitCondition, exception); } else if (type.equals(CONDITIONAL_BREAKPOINT)) { String expression = element.getAttribute(ATTR_EXPRESSION); return new DbgpConditionalBreakpoint(id, enabled, hitValue, hitCount, hitCondition, expression); } else if (type.equals(WATCH_BREAKPOINT)) { String expression = element.getAttribute(ATTR_EXPRESSION); return new DbgpWatchBreakpoint(id, enabled, hitValue, hitCount, hitCondition, expression); } return null; } private static final String ATTR_APPID = "appid"; //$NON-NLS-1$ private static final String ATTR_IDEKEY = "idekey"; //$NON-NLS-1$ private static final String ATTR_SESSION = "session"; //$NON-NLS-1$ private static final String ATTR_THREAD = "thread"; //$NON-NLS-1$ private static final String ATTR_PARENT = "parent"; //$NON-NLS-1$ private static final String ATTR_LANGUAGE = "language"; //$NON-NLS-1$ public static IDbgpSessionInfo parseSession(Element element) { String appId = element.getAttribute(ATTR_APPID); String ideKey = element.getAttribute(ATTR_IDEKEY); String session = element.getAttribute(ATTR_SESSION); String threadId = element.getAttribute(ATTR_THREAD); String parentId = element.getAttribute(ATTR_PARENT); String language = element.getAttribute(ATTR_LANGUAGE); DbgpException error = DbgpXmlParser.checkError(element); return new DbgpSessionInfo(appId, ideKey, session, threadId, parentId, language, null, error); } protected static String getFromChildOrAttr(Element property, String name) { NodeList list = property.getElementsByTagName(name); if (list.getLength() == 0) { return property.getAttribute(name); } /* * this may or may not need to be base64 decoded - need to see output from an ActiveState's python debugging * session to determine. gotta love protocol changes that have made their way back into the published spec */ return getEncodedValue((Element) list.item(0)); } private static final String ATTR_ENCODING = "encoding"; //$NON-NLS-1$ protected static String getEncodedValue(Element element) { String encoding = ENCODING_NONE; if (element.hasAttribute(ATTR_ENCODING)) { encoding = element.getAttribute(ATTR_ENCODING); } if (ENCODING_NONE.equals(encoding)) { return parseContent(element); } if (ENCODING_BASE64.equals(encoding)) { return parseBase64Content(element); } throw new AssertionError(NLS.bind("invalidEncoding", encoding)); } }