Initial pass of a log file reader
diff --git a/bundles/org.eclipse.core.runtime/src/org/eclipse/core/internal/runtime/PlatformLogReader.java b/bundles/org.eclipse.core.runtime/src/org/eclipse/core/internal/runtime/PlatformLogReader.java
new file mode 100644
index 0000000..41a15bf
--- /dev/null
+++ b/bundles/org.eclipse.core.runtime/src/org/eclipse/core/internal/runtime/PlatformLogReader.java
@@ -0,0 +1,213 @@
+package org.eclipse.core.internal.runtime;

+

+import java.io.*;

+import java.util.ArrayList;

+

+import java.util.StringTokenizer;

+import javax.xml.parsers.*;

+import org.eclipse.core.runtime.*;

+import org.w3c.dom.*;

+import org.xml.sax.SAXException;

+

+/**

+ * Reads a structured log from disk and reconstructs status and exception objects.

+ * General strategy: log entries that are malformed in any way are skipped, and an extra

+ * status is returned mentioned that there were problems.

+ */

+class PlatformLogReader {

+	/**

+	 * Temporary main class for testing...

+	 */

+	public static void main(String[] args) {

+		if (args.length != 1)

+			return;

+		String filename = args[0];

+		IStatus[] statii = new PlatformLogReader().readLogFile(filename);

+		System.out.println(statii.length + " status objects read from log");

+	}

+	/**

+	 * Reads the given log file and returns the contained status objects. 

+	 * If the log file could not be read, a status object indicating this fact

+	 * is returned.

+	 */

+public IStatus[] readLogFile(String path) {

+	Exception err = null;

+	try {

+		DocumentBuilder parser = DocumentBuilderFactory.newInstance().newDocumentBuilder();

+		Document document = parser.parse(new File(path));

+		return (IStatus[])read(document.getFirstChild());

+	} catch (IOException e) {

+		err = e;

+	} catch (SAXException e) {

+		err = e;

+	} catch (ParserConfigurationException e) {

+		err = e;

+	}

+	return new IStatus[] {new Status(IStatus.WARNING, Platform.PI_RUNTIME, 1, "Unable to parse error log", err)};

+}

+/**

+ * Returns a severity given its string representation.  

+ * Converse of PlatformLogReader#encodeSeverity.

+ */

+protected int decodeSeverity(String severity) {

+	if (severity == null)

+		return -1;

+	if (severity.equals("ERROR"))

+		return IStatus.ERROR;

+	if (severity.equals("INFO"))

+		return IStatus.INFO;

+	if (severity.equals("WARNING"))

+		return IStatus.WARNING;

+	if (severity.equals("OK"))

+		return IStatus.OK;

+	try {

+		return Integer.parseInt(severity);

+	} catch (NumberFormatException e) {

+		return -1;

+	}

+}

+/**

+ * Given a stack trace without carriage returns, returns a pretty-printed stack.

+ */

+protected String formatStack(String stack) {

+	StringWriter sWriter = new StringWriter();

+	PrintWriter writer = new PrintWriter(sWriter);

+	StringTokenizer tokenizer = new StringTokenizer(stack);

+	//first entry has no indentation

+	if (tokenizer.hasMoreTokens())

+		writer.print(tokenizer.nextToken());

+	while (tokenizer.hasMoreTokens()) {

+		String next = tokenizer.nextToken();

+		if (next != null && next.length() > 0) {

+			if (next.equals("at")) {

+				writer.println();

+				writer.print('\t');

+				writer.print(next);

+			} else {

+				writer.print(' ');

+				writer.print(next);

+			}

+		}

+	}

+	writer.flush();

+	writer.close();

+	return sWriter.toString();

+}

+protected String getString(Node target, String attributeName) {

+	NamedNodeMap map = target.getAttributes();

+	Node item = map.getNamedItem(attributeName);

+	return item == null ? null : item.getNodeValue();

+}

+protected IStatus[] readLog(Node node) {

+	NodeList children = node.getChildNodes();

+	int childCount = children.getLength();

+	boolean parseProblems = false;

+	ArrayList statii = new ArrayList(childCount);

+	for (int i = 0; i < childCount; i++) {

+		try {

+			Object status = read(children.item(i));

+			if (status != null)

+				statii.add(status);

+		} catch (RuntimeException e) {

+			parseProblems = true;

+		}

+	}

+	if (parseProblems) {

+		statii.add(new Status(IStatus.WARNING, Platform.PI_RUNTIME, 1, "Some log file entries could not be read", null));

+	}

+	return (IStatus[]) statii.toArray(new IStatus[statii.size()]);

+}

+protected Object read(Node node) {

+	if (node == null)

+		return null;

+	switch (node.getNodeType()) {

+		case Node.ELEMENT_NODE :

+			String name = node.getNodeName();

+			if (name.equals(PlatformLogListener.ELEMENT_LOG)) {

+				return readLog(node);

+			} else if (name.equals(PlatformLogListener.ELEMENT_LOG_ENTRY)) {

+				return readLogEntry(node);

+			} else if (name.equals(PlatformLogListener.ELEMENT_STATUS)) {

+				return readStatus(node);

+			} else if (name.equals(PlatformLogListener.ELEMENT_EXCEPTION)) {

+				return readException(node);

+			}

+			break;

+		//ignore text nodes for now, we don't have any in the log format

+		case Node.TEXT_NODE:

+	}

+	return null;

+}

+protected IStatus readLogEntry(Node node) {

+	NodeList children = node.getChildNodes();

+	int len = children.getLength();

+	for (int i = 0; i < len; i++) {

+		Object o = read(children.item(i));

+		if (o instanceof IStatus) {

+			return (IStatus)o;

+		}

+	}

+	return null;

+}

+protected IStatus readStatus(Node node) {

+	int severity = decodeSeverity(getString(node, PlatformLogListener.ATTRIBUTE_SEVERITY));

+	String pluginID = getString(node, PlatformLogListener.ATTRIBUTE_PLUGIN_ID);

+	String s = getString(node, PlatformLogListener.ATTRIBUTE_CODE);

+	int code = s == null ? -1 : Integer.parseInt(s);

+	String message = getString(node, PlatformLogListener.ATTRIBUTE_MESSAGE);

+	if (severity == -1 || pluginID == null || code == -1 || message == null)

+		throw new IllegalStateException();

+	//status children are either child statii or an exception

+	Throwable exception = null;

+	ArrayList children = new ArrayList();

+	NodeList childNodes = node.getChildNodes();

+	int childCount = childNodes.getLength();

+	for (int i = 0; i < childCount; i++) {

+		Object o = read(childNodes.item(i));

+		if (o instanceof IStatus) {

+			children.add(o);

+		} else if (o instanceof Throwable) {

+			exception = (Throwable)o;

+			exception.printStackTrace();

+		}

+	}

+	if (children.size() > 0) {

+		IStatus[] childStatii = (IStatus[]) children.toArray(new IStatus[children.size()]);

+		return new MultiStatus(pluginID, code, childStatii, message, exception);

+	} else {

+		return new Status(severity, pluginID, code, message, exception);

+	}

+

+}

+

+protected Throwable readException(Node node) {

+	String message = getString(node, PlatformLogListener.ATTRIBUTE_MESSAGE);

+	String stack = getString(node, PlatformLogListener.ATTRIBUTE_TRACE);

+	return new FakeException(message, formatStack(stack));

+}

+	

+	/**

+	 * A reconsituted exception that only contains a stack trace and a message.

+	 */

+	class FakeException extends Throwable {

+		private String message;

+		private String stackTrace;

+		FakeException(String msg, String stack) {

+			this.message = msg;

+			this.stackTrace = stack;

+		}

+		public String getMessage() {

+			return message;

+		}

+		public void printStackTrace() {

+			printStackTrace(System.out);

+		}

+		public void printStackTrace(PrintWriter writer) {

+			writer.println(stackTrace);

+		}

+		public void printStackTrace(PrintStream stream) {

+			stream.println(stackTrace);

+		}		

+	}

+}

+