Initial implementation of the new Debug Plugin
[phpeclipse.git] / net.sourceforge.phpeclipse.xdebug.core / src / net / sourceforge / phpeclipse / xdebug / core / DebugConnection.java
diff --git a/net.sourceforge.phpeclipse.xdebug.core/src/net/sourceforge/phpeclipse/xdebug/core/DebugConnection.java b/net.sourceforge.phpeclipse.xdebug.core/src/net/sourceforge/phpeclipse/xdebug/core/DebugConnection.java
new file mode 100644 (file)
index 0000000..64c8550
--- /dev/null
@@ -0,0 +1,408 @@
+package net.sourceforge.phpeclipse.xdebug.core;
+
+import java.io.ByteArrayInputStream;
+import java.io.DataInputStream;
+import java.io.EOFException;
+import java.io.IOException;
+import java.io.OutputStreamWriter;
+import java.net.ServerSocket;
+import java.net.Socket;
+import java.net.UnknownHostException;
+import java.util.HashMap;
+import java.util.LinkedList;
+import java.util.Map;
+
+import javax.xml.parsers.DocumentBuilder;
+import javax.xml.parsers.DocumentBuilderFactory;
+import javax.xml.parsers.ParserConfigurationException;
+
+
+import net.sourceforge.phpeclipse.xdebug.php.model.XDebugLineBreakpoint;
+import net.sourceforge.phpeclipse.xdebug.php.model.XDebugTarget;
+import net.sourceforge.phpeclipse.xdebug.php.model.XDebugThread;
+
+import org.eclipse.core.runtime.CoreException;
+import org.eclipse.core.runtime.IProgressMonitor;
+import org.eclipse.core.runtime.IStatus;
+import org.eclipse.core.runtime.Status;
+import org.eclipse.core.runtime.jobs.Job;
+import org.eclipse.debug.core.DebugEvent;
+import org.eclipse.debug.core.DebugException;
+import org.eclipse.debug.core.model.IBreakpoint;
+import org.eclipse.debug.core.model.IThread;
+import org.w3c.dom.Document;
+import org.w3c.dom.NamedNodeMap;
+import org.w3c.dom.Node;
+import org.xml.sax.SAXException;
+
+public class DebugConnection {
+       
+       private ServerSocket fDebugServerSocket;
+       private DebugResponse lastResponse;
+       private Socket fDebugSocket;
+       private OutputStreamWriter fDebugWriter;
+       private DataInputStream fDebugReader;
+       private ResponseListenerJob fResponseListener;
+       
+       // Settings for Debug Process
+       private int fTransactionID=0;
+       private String fileuri = "";
+       private String appID="";
+       private String lastCommand = "";
+       private XDebugTarget fDebugTarget;
+       private HashMap ResponseList;
+
+       public class DebugResponse {
+
+               private Node parentNode;
+               private int fTransactionID=-1;
+               private String fCommand="";
+               private String fStatus;
+               private String fReason;
+               private String fName;
+               private boolean  fError;
+
+               public synchronized void setParentNode (String xmlInput){
+                       DocumentBuilderFactory factory = DocumentBuilderFactory.newInstance();
+                       DocumentBuilder builder=null;
+                       Document doc=null;
+                       try {
+                               builder = factory.newDocumentBuilder();
+                       } catch (ParserConfigurationException e) {
+                               // TODO Auto-generated catch block
+                               e.printStackTrace();
+                       }
+                       ByteArrayInputStream InputXMLStream = new ByteArrayInputStream(xmlInput.getBytes());
+                       
+                       try {
+                               doc = builder.parse(InputXMLStream);
+                       } catch (SAXException e) {
+                               // TODO Auto-generated catch block
+                               e.printStackTrace();
+                       } catch (IOException e) {
+                               // TODO Auto-generated catch block
+                               e.printStackTrace();
+                       }
+                       parentNode=doc.getFirstChild();
+                       fName=parentNode.getNodeName();
+                       String idStr = getAttributeValue("transaction_id");
+                       if (idStr!="")
+                               fTransactionID = Integer.parseInt(idStr);
+                       fCommand = getAttributeValue("command");
+                       fStatus = getAttributeValue("status");
+                       fReason = getAttributeValue("reason");
+//                     notifyAll();
+               }
+               
+               public String getAttributeValue (String AttributeName) {
+                       String strValue = "";
+                       if (parentNode.hasAttributes()) {
+                               NamedNodeMap listAttribute = parentNode.getAttributes();
+                               Node attribute = listAttribute.getNamedItem(AttributeName);
+                               if (attribute !=null)
+                                       strValue = attribute.getNodeValue();
+                       }
+                       return strValue;
+               }
+               
+               public synchronized Node getParentNode(){
+                       return parentNode;
+               }
+               
+               public synchronized String getCommand() {
+                       return fCommand;
+               }
+               public synchronized String getName() {
+                       return fName;
+               }
+               
+               
+               DebugResponse () {
+                       fTransactionID = -1;
+                       fCommand = "";
+                       fStatus = "";
+                       fReason = "";                   
+                       fName= "";
+               }
+               
+               DebugResponse (String XMLInput) {
+                       setParentNode(XMLInput);
+               }
+
+               public synchronized String getReason() {
+                       return fReason;
+               }
+
+               public synchronized String getStatus() {
+                       return fStatus;
+               }
+
+               public synchronized int getTransactionID() {
+                       return fTransactionID;
+               }
+               
+               private synchronized DebugResponse waitforTransactionID(int id) {
+                       while (fTransactionID!= id) {
+                               try {
+                                       wait();
+                               } catch (InterruptedException e) {
+                               }
+                       }
+//                     XDebugCorePlugin.log(IStatus.INFO,"got TransID: "+id);
+
+                       return this;
+               }
+
+               public boolean  isError() {
+                       return fError;
+               }
+
+               public void setError(boolean error) {
+                       fError = error;
+               }
+               
+               protected synchronized void notifyWait() {
+                       notifyAll();
+               }
+       }
+       
+       
+       /**
+        * Listens to events from the XDebug and fires corresponding 
+        * debug events.
+        */
+       class ResponseListenerJob extends Job {
+               
+               public ResponseListenerJob() {
+                       super("XDebug Event Dispatch");
+                       setSystem(true);
+                       
+               }
+               
+               
+               
+               
+               private void checkResponse(DebugResponse response) {
+                       Node node=response.getParentNode();
+                       if (node.hasChildNodes()) {
+                               Node child=node.getFirstChild();
+                               if (child.getNodeName().equals("error")) {
+                                       int code = Integer.parseInt(PHPDebugUtils.getAttributeValue(child, "code"));
+                                       String text=(child.getFirstChild()).getNodeValue();
+                                       XDebugCorePlugin.log(IStatus.ERROR,lastCommand+" ERROR "+code+": "+text);
+                                       lastResponse.setError(true);
+                                       return;
+                               }
+                       }
+                       lastResponse.setError(false);
+                       if (response.getStatus().equals("stopped"))
+                               terminated();
+                       else if (response.getStatus().equals("break") && response.getReason().equals("ok")){ 
+                               if (response.getCommand().equals("run")) {  // breakpoint hit
+                                       int id=-1;
+                                       try {
+                                               id=sendRequest("stack_get");
+                                       } catch (DebugException e) {
+                                               // TODO Auto-generated catch block
+                                               e.printStackTrace();
+                                       }
+                                       String InputXML = readData();
+                                       if (InputXML != null) {
+                                               XDebugCorePlugin.log(IStatus.INFO, InputXML);
+                                               lastResponse.setParentNode(InputXML);
+                                               fDebugTarget.breakpointHit(lastResponse.getParentNode());
+                                       }
+                                       
+                               } else if (response.getCommand().equals("step_into")) { // step_into
+                                       fDebugTarget.suspended(DebugEvent.STEP_END);
+//                                     XDebugCorePlugin.log(IStatus.INFO,response.getCommand()+" STEP_END sent");
+                               } else if (response.getCommand().equals("step_over")) { // step_over
+                                       fDebugTarget.suspended(DebugEvent.STEP_END);
+//                                     XDebugCorePlugin.log(IStatus.INFO,response.getCommand()+" STEP_END sent");
+                               } else if (response.getCommand().equals("step_out")) { // step_over
+                                       fDebugTarget.suspended(DebugEvent.STEP_END);
+//                                     XDebugCorePlugin.log(IStatus.INFO,response.getCommand()+" STEP_END sent");
+                               } 
+                       } else if (response.getCommand().equals("breakpoint_set")) { // step_over
+                               String idStr=response.getAttributeValue("id");
+                               if (idStr!="") {
+                                       int targetID=response.getTransactionID();
+                                       BreakpointResponseData ResponseData= new BreakpointResponseData(response.getTransactionID(),Integer.parseInt(idStr));
+
+                                       fDebugTarget.fireDebugResponseEvent(ResponseData);
+                                       IBreakpoint[] breakpoints=XDebugCorePlugin.getBreakpoints();
+                                       for(int i=0;i< breakpoints.length;i++) {
+                                               XDebugLineBreakpoint breakpoint=(XDebugLineBreakpoint)breakpoints[i];
+                                               try {
+                                                       if (breakpoint.getID()==targetID)
+                                                               breakpoint.setID(Integer.parseInt(idStr));
+                                               } catch (NumberFormatException e) {
+                                                       // TODO Auto-generated catch block
+                                                       e.printStackTrace();
+                                               } catch (CoreException e) {
+                                                       // TODO Auto-generated catch block
+                                                       e.printStackTrace();
+                                               }
+                                       }
+                               }
+                       }
+       
+               }
+               
+               /* (non-Javadoc)
+                * @see org.eclipse.core.runtime.jobs.Job#run(org.eclipse.core.runtime.IProgressMonitor)
+                */
+               protected IStatus run(IProgressMonitor monitor) {
+                       String InputXML = "";
+                       while (!fDebugTarget.isTerminated() && (InputXML != null)) {
+                               InputXML = readData();
+                               if (InputXML != null) {
+                                       XDebugCorePlugin.log(IStatus.INFO, InputXML);
+                                       lastResponse.setParentNode(InputXML);
+                                       if (lastResponse.getName() == "init") {
+                                               Node myNode=lastResponse.getParentNode();
+                                               appID = PHPDebugUtils.getAttributeValue(myNode, "appid");
+                                               fileuri = PHPDebugUtils.getAttributeValue(myNode, "fileuri");
+//                                             fDebugTarget.started();
+                                               fDebugTarget.fireCreationEvent();
+                                       } else if (lastResponse.getName() == "response") {
+                                               checkResponse(lastResponse);
+                                       }
+                                       ResponseList.put(new Integer(lastResponse.getTransactionID()),lastResponse);
+                                       lastResponse.notifyWait();
+                               }
+                       }
+                       return Status.OK_STATUS;
+               }
+               
+       }
+
+       
+       public DebugConnection (XDebugTarget debugTarget,int debugPort) {
+               fDebugTarget=debugTarget;
+               lastResponse=new DebugResponse();
+               ResponseList= new HashMap();
+               
+               try {
+                       fDebugServerSocket = new ServerSocket(debugPort);
+                       fDebugSocket = fDebugServerSocket.accept();
+                       fDebugWriter = new OutputStreamWriter(fDebugSocket.getOutputStream(), "UTF8");
+                       fDebugReader = new DataInputStream(fDebugSocket.getInputStream());                      
+//                     fDebugReader = new BufferedReader(new InputStreamReader(fDebugSocket.getInputStream()));
+                       
+               } catch (UnknownHostException e) {
+//                     abort("Unable to connect to PHP Debuger", e);
+               } catch (IOException e) {
+//                     abort("Unable to connect to PHP Debuger", e);
+               }
+               fResponseListener = new ResponseListenerJob();
+               fResponseListener.schedule();
+
+       }
+       
+       private void terminated() {
+               try {
+                       fDebugReader.close();           
+                       fDebugWriter.close();
+                       fDebugSocket.close();
+                       fDebugServerSocket.close();
+               } catch (IOException e) {
+                       // TODO Auto-generated catch block
+                       e.printStackTrace();
+               }
+               fDebugTarget.terminated();
+
+       }
+       
+       private String readData()
+       {
+        byte byteBuffer[]=null,b;
+               int count=0;
+               
+               try {
+                       while ( (b =fDebugReader.readByte())!=0)
+                       {
+                               count=count*10+b-'0';
+//                             count=count*10+Integer.parseInt(b);
+                       }
+//                     System.out.println(count);
+                       byteBuffer = new byte[count];
+                       int readCount=0;
+                       int attempts=0;
+                       while ((count >0) && (attempts <5)) {
+                               int rc=fDebugReader.read(byteBuffer,readCount,count);
+                               count-=rc;
+                               readCount+=rc;
+                               attempts++;
+                       }
+                       if((b= fDebugReader.readByte())!=0) // reads the NULL Byte at the end;
+                               System.out.println("NULL-Byte missing!!"); 
+               } catch (IOException e) {
+                       // TODO Auto-generated catch block
+                       if (e instanceof EOFException==false)
+                               e.printStackTrace();
+                       return null;
+               }
+               return new String(byteBuffer);
+       }
+
+       
+       /**
+        * Sends a request to the Debugengine and waits for an OK.
+        * 
+        * @param command debug command
+        * @throws DebugException if the request fails
+        */
+       
+       
+       public int sendRequest(String command) throws DebugException {  
+               return sendRequest(command,"");
+       }
+       
+       /**
+        * Sends a request to the Debugengine and waits for an OK.
+        * 
+        * @param command debug command
+        * @arguments arguments for the command
+        * @throws DebugException if the request fails
+        */
+       
+       public synchronized int sendRequest(String command, String arguments) throws DebugException {
+               
+//             System.out.println(command+" -i "+transactionID+" "+arguments);
+               XDebugCorePlugin.log(IStatus.INFO,command+" -i "+fTransactionID+" "+arguments);
+               synchronized (fDebugSocket) {
+                       try {
+                               fDebugWriter.write(command);
+                               fDebugWriter.write(" -i " + fTransactionID);
+                               if (arguments!="")
+                                       fDebugWriter.write(" " + arguments);
+                               fDebugWriter.write(0);
+                               fDebugWriter.flush();
+                       } catch (IOException e) {
+                               e.printStackTrace();
+               }
+               }
+               return fTransactionID++;
+       }
+       
+       public DebugResponse waitforTransID(int id) {
+               if (ResponseList.containsKey(new Integer(id)))
+               {
+//                     return (DebugResponse)ResponseList.get(new Integer(id));
+                       return (DebugResponse)ResponseList.remove(new Integer(id));
+               }
+               else
+                       return lastResponse.waitforTransactionID(id);
+       }
+       
+       public DebugResponse getResponse(int id) {
+               if (ResponseList.containsKey(new Integer(id)))
+                       return (DebugResponse)ResponseList.get(new Integer(id));
+               else
+                       return waitforTransID(id);
+       }
+
+
+
+}