package com.aptana.ruby.internal.debug.core.parsing;
import java.io.IOException;
import java.net.SocketException;
import java.util.HashMap;
import java.util.Iterator;
import java.util.Map;
import org.xmlpull.v1.XmlPullParser;
import org.xmlpull.v1.XmlPullParserException;
import com.aptana.ruby.debug.core.RubyDebugCorePlugin;
@SuppressWarnings("nls")
public class MultiReaderStrategy extends AbstractReadStrategy
{
private Map<XmlStreamReader, Thread> threads;
private XmlStreamReader currentReader;
private boolean isConnected;
public MultiReaderStrategy(XmlPullParser xpp)
{
super(xpp);
isConnected = true;
threads = new HashMap<XmlStreamReader, Thread>();
new Thread("xml reader")
{
public void run()
{
try
{
readLoop();
}
catch (SocketException e)
{
RubyDebugCorePlugin.debug("read loop stopped because socket has been closed.");
}
catch (Exception e)
{
RubyDebugCorePlugin.debug("read loop stopped due to error : ", e);
e.printStackTrace();
}
finally
{
isConnected = false;
try
{
Thread.sleep(1000); // Avoid Commodfication Exceptions
}
catch (InterruptedException e)
{
}
releaseAllReaders();
}
}
}.start();
}
protected void readLoop() throws XmlPullParserException, IOException, XmlStreamReaderException
{
RubyDebugCorePlugin.debug("Starting xml read loop.");
int eventType = xpp.getEventType();
do
{
if (eventType == XmlPullParser.START_TAG)
{
this.dispatchStartTag();
}
else if (eventType == XmlPullParser.END_TAG && currentReader != null)
{
if (currentReader.processEndElement(xpp))
{
this.removeReader(currentReader);
currentReader = null;
}
}
else if (eventType == XmlPullParser.TEXT)
{
if (currentReader != null)
{
currentReader.processContent(xpp.getText());
}
}
eventType = xpp.next();
}
while (eventType != XmlPullParser.END_DOCUMENT);
RubyDebugCorePlugin.debug("Read loop stopped because end of stream was reached.");
}
protected void dispatchStartTag() throws XmlPullParserException, IOException, XmlStreamReaderException
{
RubyDebugCorePlugin.debug("Dispatching start tag " + xpp.getName());
if (currentReader != null)
{
// processing sub-elements, eg. <variable> from <variables>
if (currentReader.processStartElement(xpp))
{
return;
}
// this is an error, the currentReader must be able to process sub-elements
RubyDebugCorePlugin.debug("Current Reader can not process tag " + xpp.getName());
currentReader = null;
}
int missed = 0;
RubyDebugCorePlugin.debug("Searching reader for start tag " + xpp.getName());
do
{
findReaderForTag();
if (currentReader == null)
{
missed += 1;
RubyDebugCorePlugin.debug("Missed Start Tag : " + xpp.getName());
try
{
Thread.sleep(200);
}
catch (InterruptedException e)
{
}
}
}
while (currentReader == null && missed < 10);
}
private synchronized void findReaderForTag() throws XmlStreamReaderException
{
// System.out.println("There are no threads:" + threads.size()) ;
for (XmlStreamReader streamReader : threads.keySet())
{
if (streamReader.processStartElement(xpp))
{
currentReader = streamReader;
break;
}
}
}
protected synchronized void releaseAllReaders()
{
for (Iterator<Map.Entry<XmlStreamReader, Thread>> iter = threads.entrySet().iterator(); iter.hasNext();)
{
Thread thread = iter.next().getValue();
thread.interrupt();
iter.remove();
}
}
protected synchronized void removeReader(XmlStreamReader streamReader)
{
threads.get(streamReader).interrupt();
threads.remove(streamReader);
}
protected synchronized void addReader(XmlStreamReader streamReader)
{
threads.put(streamReader, Thread.currentThread());
}
public void readElement(XmlStreamReader streamReader) throws IOException
{
readElement(streamReader, Long.MAX_VALUE);
}
public void readElement(XmlStreamReader streamReader, long maxWaitTime) throws IOException
{
if (!isConnected)
{
throw new IOException("Read loop has finished");
}
this.addReader(streamReader);
try
{
RubyDebugCorePlugin.debug("Thread is waiting for input: " + Thread.currentThread());
Thread.sleep(maxWaitTime);
streamReader.setWaitTimeExpired(true);
}
catch (InterruptedException e)
{
RubyDebugCorePlugin.debug("Thread has finished processing : " + Thread.currentThread());
}
}
public boolean isConnected()
{
return isConnected;
}
}