/******************************************************************************* * Copyright (c) 2006 IBM Corporation and others. * All rights reserved. This program and the accompanying materials * are made available under the terms of the Eclipse Public License v1.0 * which accompanies this distribution, and is available at * http://www.eclipse.org/legal/epl-v10.html * * Contributors: * IBM Corporation - initial implementation *******************************************************************************/ package org.eclipse.php.internal.debug.core.xdebug.dbgp.protocol; import java.io.ByteArrayInputStream; import java.io.IOException; import java.io.InputStream; import javax.xml.parsers.DocumentBuilder; import javax.xml.parsers.DocumentBuilderFactory; import javax.xml.parsers.ParserConfigurationException; import org.eclipse.php.internal.debug.core.xdebug.dbgp.DBGpLogger; import org.w3c.dom.Document; import org.w3c.dom.NamedNodeMap; import org.w3c.dom.Node; import org.w3c.dom.NodeList; import org.xml.sax.SAXException; /** * Handle Init and Response DBGp Responses Init and Engine Child only Response, * status attributes and Error code child only status entries occur when a * program suspends or a status request is made * */ public class DBGpResponse { /* * Example Init <initfileuri= * "file:///C%3A%5Cudata-eclipse%5Cphpide025%5CtestXdebug%5CPhpCode%5Ctestcase3.php" * language="PHP" protocol_version="1.0" appid="2116" * idekey="ECLIPSE_XDEBUG11623014568921"> <engine version="2.0.0RC2-dev"> * <![CDATA[Xdebug]]> </engine> <author> <![CDATA[Derick Rethans]]> * </author> <url> <![CDATA[http://xdebug.org]]> </url> <copyright> * <![CDATA[Copyright (c) 2002-2006 by Derick Rethans]]> </copyright> * </init> */ /* * Example Responses <response command="property_get" transaction_id="95" * status="break" reason="ok"> <error code="300"> <message> <![CDATA[can not * get property]]> </message> </error> </response> * * <response command="stop" transaction_id="32" status="stopped" * reason="ok"></response> * * <response command="breakpoint_set" transaction_id="1" * id="49240001"></response> <response command="feature_set" * transaction_id="19" feature="max_depth" success="1"></response> */ /* * Example stream * * <stream type="stdout"encoding="base64"><![CDATA[PGh0bWw+ * DQo8aGVhZD4NCjx0aXRsZT5HdWVzc2luZyBHYW1lPC90aXRsZT4NCjwvaGVhZD4NCjxib2R5Pg0KPGZvcm0gYWN0aW9uPScnIG1ldGhvZD0ncG9zdCc * +PGlucHV0IHR5cGU9J2hpZGRlbicgdmFsdWU9JzUnIG5hbWU9J3R4dE51bWJlcic+ * PGlucHV0IHR5cGU9J2hpZGRlbicgdmFsdWU9JzAnIG5hbWU9J3R4dFRyaWVzJz5QbGVhc2UgR3Vlc3MgQSBOdW1iZXIgKDEgLSA1MCk8aW5wdXQgdHlwZT0ndGV4dCcgbmFtZT0ndHh0R3Vlc3MnIHNpemU9JzEwJz48aW5wdXQgdHlwZT0nc3VibWl0JyB2YWx1ZT0nU3VibWl0JyBuYW1lPSdTdWJtaXQnPjwvZm9ybT48YnIgLz4gPCEtLSA1LS0hPjwvYm9keT4NCjwvaHRtbD4 * =]]></stream> */ /* * status codes {"", "starting", "stopping", "stopped", "running", "break"}; */ public static final String STATUS_STARTING = "starting"; //$NON-NLS-1$ public static final String STATUS_STOPPING = "stopping"; //$NON-NLS-1$ public static final String STATUS_STOPPED = "stopped"; //$NON-NLS-1$ public static final String STATUS_RUNNING = "running"; //$NON-NLS-1$ public static final String STATUS_BREAK = "break"; //$NON-NLS-1$ /* * reason codes {"ok", "error", "aborted", "exception"}; */ public static final String REASON_OK = "ok"; //$NON-NLS-1$ public static final String REASON_ERROR = "error"; //$NON-NLS-1$ public static final String REASON_ABORTED = "aborted"; //$NON-NLS-1$ public static final String REASON_EXCEPTION = "exception"; //$NON-NLS-1$ private DocumentBuilder db; private Document doc; private Node parent; // type public static final int PARSE_FAILURE = 0; public static final int INIT = 1; public static final int RESPONSE = 2; public static final int STREAM = 3; public static final int PROXY_INIT = 4; public static final int PROXY_ERROR = 5; public static final int UNKNOWN_TYPE = 99; int type; // init attributes private String idekey; private String session; private String threadId; private String engineVersion = ""; //$NON-NLS-1$ private EngineTypes engineType = EngineTypes.other; private String fileUri; // Response attributes private String id; private String command; // Status Response attributes private String status; private String reason; // error responses /* * Error codes, eg * * { 0, "no error" }, { 1, "parse error in command" }, { 2, * "duplicate arguments in command" }, { 3, "invalid or missing options" }, * { 4, "unimplemented command" }, { 5, "command is not available" }, { 100, * "can not open file" }, { 101, "stream redirect failed" }, { 200, * "breakpoint could not be set" }, { 201, * "breakpoint type is not supported" }, { 202, "invalid breakpoint line" }, * { 203, "no code on breakpoint line" }, { 204, "invalid breakpoint state" * }, { 205, "no such breakpoint" }, { 206, "error evaluating code" }, { * 207, "invalid expression" }, { 300, "can not get property" }, { 301, * "stack depth invalid" }, { 302, "context invalid" }, { 900, * "encoding not supported" }, { 998, * "an internal exception in the debugger" }, { 999, "unknown error" }, */ public static final int ERROR_OK = 0; public static final int ERROR_CANT_PERFORM_EVAL = 206; public static final int ERROR_CANT_GET_PROPERTY = 300; // own internal error codes public static final int ERROR_UNKNOWN_ERROR_CODE = 10000; public static final int ERROR_UNKNOWN_TYPE = 10001; public static final int ERROR_PARSE_FAILURE = 10002; public static final int ERROR_INVALID_RESPONSE = 10003; int errorCode; String errorMessage; // stream data private String streamType; private String streamData; private byte[] rawXML; public DBGpResponse() { DocumentBuilderFactory dbFact = DocumentBuilderFactory.newInstance(); try { db = dbFact.newDocumentBuilder(); } catch (ParserConfigurationException e) { DBGpLogger.logException(null, this, e); } } public void parseResponse(byte[] xmlResponse) { rawXML = xmlResponse; if (db != null && xmlResponse != null) { ByteArrayInputStream bais = new ByteArrayInputStream(xmlResponse); parseResponse(bais); } else { type = PARSE_FAILURE; errorCode = ERROR_PARSE_FAILURE; } } private void parseResponse(InputStream is) { id = null; command = null; type = UNKNOWN_TYPE; errorCode = ERROR_UNKNOWN_TYPE; try { doc = db.parse(is); parent = doc.getFirstChild(); String nodeName = parent.getNodeName(); if (nodeName.equals("response")) { //$NON-NLS-1$ parseResponseType(); } else if (nodeName.equals("init")) { //$NON-NLS-1$ parseInitType(); } else if (nodeName.equals("stream")) { //$NON-NLS-1$ parseStreamType(); } else if (nodeName.equals("proxyinit")) { //$NON-NLS-1$ parseProxyInitType(); } else if (nodeName.equals("proxyerror")) { //$NON-NLS-1$ parseProxyErrorType(); } } catch (SAXException e) { DBGpLogger.logException(null, this, e); type = PARSE_FAILURE; errorCode = ERROR_PARSE_FAILURE; } catch (IOException e) { DBGpLogger.logException(null, this, e); type = PARSE_FAILURE; errorCode = ERROR_PARSE_FAILURE; } // System.out.println("wait for me"); } private void parseStreamType() { type = STREAM; streamType = getTopAttribute("type"); //$NON-NLS-1$ Node Child = parent.getFirstChild(); if (Child != null) { streamData = Child.getNodeValue(); } if (streamType.length() != 0) { errorCode = ERROR_OK; } else { errorCode = ERROR_INVALID_RESPONSE; } } private void parseProxyInitType() { type = PROXY_INIT; idekey = getTopAttribute("idekey"); //$NON-NLS-1$ // caller can retrieve address, port getErrorInformation(false); } private void parseProxyErrorType() { type = PROXY_ERROR; getErrorInformation(false); } private void parseInitType() { // get the init information type = INIT; idekey = getTopAttribute("idekey"); //$NON-NLS-1$ threadId = getTopAttribute("thread"); //$NON-NLS-1$ session = getTopAttribute("session"); //$NON-NLS-1$ if (session.trim().length() == 0) { session = null; } fileUri = getTopAttribute("fileuri"); //$NON-NLS-1$ // engine may not be the first child so you will need to search // for it. NodeList nodes = parent.getChildNodes(); for (int i = 0; i < nodes.getLength(); i++) { Node node = nodes.item(i); if (node.getNodeName().equals("engine")) { //$NON-NLS-1$ engineVersion = getAttribute(node, "version"); //$NON-NLS-1$ NodeList moreNodes = node.getChildNodes(); if (moreNodes != null && moreNodes.getLength() > 0) { String engineTypeStr = moreNodes.item(0).getNodeValue(); if (engineTypeStr != null) { try { engineType = EngineTypes.valueOf(engineTypeStr); } catch (IllegalArgumentException e) { engineType = EngineTypes.other; } } } i = nodes.getLength(); } } if (idekey.length() != 0 && fileUri.length() != 0) { errorCode = ERROR_OK; } else { errorCode = ERROR_INVALID_RESPONSE; } } private void parseResponseType() { type = RESPONSE; id = getTopAttribute("transaction_id"); //$NON-NLS-1$ command = getTopAttribute("command"); //$NON-NLS-1$ status = getTopAttribute("status"); //$NON-NLS-1$ reason = getTopAttribute("reason"); //$NON-NLS-1$ getErrorInformation(true); } private void getErrorInformation(boolean checkID) { // get the error information Node errNode = parent.getFirstChild(); if (errNode != null && errNode.getNodeName().equals("error")) { //$NON-NLS-1$ String errVal = getAttribute(errNode, "code"); //$NON-NLS-1$ try { errorCode = Integer.parseInt(errVal); } catch (NumberFormatException nfe) { errorCode = ERROR_UNKNOWN_ERROR_CODE; } Node msgNode = errNode.getFirstChild(); if (msgNode != null) { Node dataNode = msgNode.getFirstChild(); if (dataNode != null) { errorMessage = dataNode.getNodeValue(); } } } else { errorCode = ERROR_OK; if (checkID && (id == null || id.length() == 0)) { errorCode = ERROR_INVALID_RESPONSE; } } } public Node getParentNode() { return doc.getFirstChild(); } public String getTopAttribute(String attrName) { return getAttribute(parent, attrName); } public static String getAttribute(Node node, String attrName) { String attrValue = ""; //$NON-NLS-1$ if (node != null && node.hasAttributes()) { NamedNodeMap attrs = node.getAttributes(); Node attribute = attrs.getNamedItem(attrName); if (attribute != null) attrValue = attribute.getNodeValue(); } return attrValue; } public String getCommand() { return command; } public String getId() { return id; } public String getReason() { return reason; } public String getStatus() { return status; } public int getType() { return type; } public String getFileUri() { return fileUri; } public String getIdekey() { return idekey; } public String getSession() { return session; } public String getThreadId() { return threadId; } public int getErrorCode() { return errorCode; } public String getErrorMessage() { return errorMessage; } public String getEngineVersion() { return engineVersion; } /** * this will either be null or base64 encoded. It needs to be decoded. * * @return encoded stream data or null. */ public String getStreamData() { return streamData; } public String getStreamType() { return streamType; } public byte[] getRawXML() { return rawXML; } public EngineTypes getEngineType() { return engineType; } }