changeset 5737:f2fc4d13975a

Merge.
author Lukas Stadler <lukas.stadler@jku.at>
date Wed, 27 Jun 2012 17:35:32 +0200
parents f96e7b39e9fe (current diff) 915952ed97c0 (diff)
children 50dbe728da9e
files
diffstat 43 files changed, 2840 insertions(+), 48 deletions(-) [+]
line wrap: on
line diff
--- /dev/null	Thu Jan 01 00:00:00 1970 +0000
+++ b/visualizer/LogViewer/build.xml	Wed Jun 27 17:35:32 2012 +0200
@@ -0,0 +1,8 @@
+<?xml version="1.0" encoding="UTF-8"?>
+<!-- You may freely edit this file. See harness/README in the NetBeans platform -->
+<!-- for some information on what you could do (e.g. targets to override). -->
+<!-- If you delete this file and reopen the project it will be recreated. -->
+<project name="at.ssw.visualizer.logviewer" default="netbeans" basedir=".">
+    <description>Builds, tests, and runs the project at.ssw.visualizer.logviewer.</description>
+    <import file="nbproject/build-impl.xml"/>
+</project>
--- /dev/null	Thu Jan 01 00:00:00 1970 +0000
+++ b/visualizer/LogViewer/manifest.mf	Wed Jun 27 17:35:32 2012 +0200
@@ -0,0 +1,6 @@
+Manifest-Version: 1.0
+OpenIDE-Module: at.ssw.visualizer.logviewer
+OpenIDE-Module-Layer: at/ssw/visualizer/logviewer/layer.xml
+OpenIDE-Module-Localizing-Bundle: at/ssw/visualizer/logviewer/Bundle.properties
+OpenIDE-Module-Specification-Version: 1.0
+
--- /dev/null	Thu Jan 01 00:00:00 1970 +0000
+++ b/visualizer/LogViewer/nbproject/build-impl.xml	Wed Jun 27 17:35:32 2012 +0200
@@ -0,0 +1,45 @@
+<?xml version="1.0" encoding="UTF-8"?>
+<!--
+*** GENERATED FROM project.xml - DO NOT EDIT  ***
+***         EDIT ../build.xml INSTEAD         ***
+-->
+<project name="at.ssw.visualizer.logviewer-impl" basedir="..">
+    <fail message="Please build using Ant 1.7.1 or higher.">
+        <condition>
+            <not>
+                <antversion atleast="1.7.1"/>
+            </not>
+        </condition>
+    </fail>
+    <property file="nbproject/private/suite-private.properties"/>
+    <property file="nbproject/suite.properties"/>
+    <fail unless="suite.dir">You must set 'suite.dir' to point to your containing module suite</fail>
+    <property file="${suite.dir}/nbproject/private/platform-private.properties"/>
+    <property file="${suite.dir}/nbproject/platform.properties"/>
+    <macrodef name="property" uri="http://www.netbeans.org/ns/nb-module-project/2">
+        <attribute name="name"/>
+        <attribute name="value"/>
+        <sequential>
+            <property name="@{name}" value="${@{value}}"/>
+        </sequential>
+    </macrodef>
+    <macrodef name="evalprops" uri="http://www.netbeans.org/ns/nb-module-project/2">
+        <attribute name="property"/>
+        <attribute name="value"/>
+        <sequential>
+            <property name="@{property}" value="@{value}"/>
+        </sequential>
+    </macrodef>
+    <property file="${user.properties.file}"/>
+    <nbmproject2:property name="harness.dir" value="nbplatform.${nbplatform.active}.harness.dir" xmlns:nbmproject2="http://www.netbeans.org/ns/nb-module-project/2"/>
+    <nbmproject2:property name="nbplatform.active.dir" value="nbplatform.${nbplatform.active}.netbeans.dest.dir" xmlns:nbmproject2="http://www.netbeans.org/ns/nb-module-project/2"/>
+    <nbmproject2:evalprops property="cluster.path.evaluated" value="${cluster.path}" xmlns:nbmproject2="http://www.netbeans.org/ns/nb-module-project/2"/>
+    <fail message="Path to 'platform' cluster missing in $${cluster.path} property or using corrupt Netbeans Platform (missing harness).">
+        <condition>
+            <not>
+                <contains string="${cluster.path.evaluated}" substring="platform"/>
+            </not>
+        </condition>
+    </fail>
+    <import file="${harness.dir}/build.xml"/>
+</project>
--- /dev/null	Thu Jan 01 00:00:00 1970 +0000
+++ b/visualizer/LogViewer/nbproject/genfiles.properties	Wed Jun 27 17:35:32 2012 +0200
@@ -0,0 +1,8 @@
+build.xml.data.CRC32=979ec129
+build.xml.script.CRC32=1c914106
+build.xml.stylesheet.CRC32=a56c6a5b@2.47.2
+# This file is used by a NetBeans-based IDE to track changes in generated files such as build-impl.xml.
+# Do not edit this file. You may delete it but then the IDE will never regenerate such files for you.
+nbproject/build-impl.xml.data.CRC32=979ec129
+nbproject/build-impl.xml.script.CRC32=712ab216
+nbproject/build-impl.xml.stylesheet.CRC32=238281d1@2.47.2
--- /dev/null	Thu Jan 01 00:00:00 1970 +0000
+++ b/visualizer/LogViewer/nbproject/project.properties	Wed Jun 27 17:35:32 2012 +0200
@@ -0,0 +1,2 @@
+javac.source=1.7
+javac.compilerargs=-Xlint -Xlint:-serial
--- /dev/null	Thu Jan 01 00:00:00 1970 +0000
+++ b/visualizer/LogViewer/nbproject/project.xml	Wed Jun 27 17:35:32 2012 +0200
@@ -0,0 +1,146 @@
+<?xml version="1.0" encoding="UTF-8"?>
+<project xmlns="http://www.netbeans.org/ns/project/1">
+    <type>org.netbeans.modules.apisupport.project</type>
+    <configuration>
+        <data xmlns="http://www.netbeans.org/ns/nb-module-project/3">
+            <code-name-base>at.ssw.visualizer.logviewer</code-name-base>
+            <suite-component/>
+            <module-dependencies>
+                <dependency>
+                    <code-name-base>com.oracle.graal.visualizer.editor</code-name-base>
+                    <build-prerequisite/>
+                    <compile-dependency/>
+                    <run-dependency>
+                        <specification-version>1.0</specification-version>
+                    </run-dependency>
+                </dependency>
+                <dependency>
+                    <code-name-base>com.oracle.graal.visualizer.sharedactions</code-name-base>
+                    <build-prerequisite/>
+                    <compile-dependency/>
+                    <run-dependency>
+                        <specification-version>1.0</specification-version>
+                    </run-dependency>
+                </dependency>
+                <dependency>
+                    <code-name-base>com.sun.hotspot.igv.data</code-name-base>
+                    <build-prerequisite/>
+                    <compile-dependency/>
+                    <run-dependency>
+                        <specification-version>1.0</specification-version>
+                    </run-dependency>
+                </dependency>
+                <dependency>
+                    <code-name-base>org.eclipse.draw2d</code-name-base>
+                    <build-prerequisite/>
+                    <compile-dependency/>
+                    <run-dependency>
+                        <specification-version>1.0</specification-version>
+                    </run-dependency>
+                </dependency>
+                <dependency>
+                    <code-name-base>org.jdesktop.layout</code-name-base>
+                    <build-prerequisite/>
+                    <compile-dependency/>
+                    <run-dependency>
+                        <release-version>1</release-version>
+                        <specification-version>1.19.1</specification-version>
+                    </run-dependency>
+                </dependency>
+                <dependency>
+                    <code-name-base>org.netbeans.api.visual</code-name-base>
+                    <build-prerequisite/>
+                    <compile-dependency/>
+                    <run-dependency>
+                        <specification-version>2.30.1</specification-version>
+                    </run-dependency>
+                </dependency>
+                <dependency>
+                    <code-name-base>org.netbeans.modules.options.api</code-name-base>
+                    <build-prerequisite/>
+                    <compile-dependency/>
+                    <run-dependency>
+                        <release-version>1</release-version>
+                        <specification-version>1.24.1</specification-version>
+                    </run-dependency>
+                </dependency>
+                <dependency>
+                    <code-name-base>org.netbeans.modules.projectapi</code-name-base>
+                    <build-prerequisite/>
+                    <compile-dependency/>
+                    <run-dependency>
+                        <release-version>1</release-version>
+                        <specification-version>1.40.1</specification-version>
+                    </run-dependency>
+                </dependency>
+                <dependency>
+                    <code-name-base>org.openide.actions</code-name-base>
+                    <build-prerequisite/>
+                    <compile-dependency/>
+                    <run-dependency>
+                        <specification-version>6.24.1</specification-version>
+                    </run-dependency>
+                </dependency>
+                <dependency>
+                    <code-name-base>org.openide.awt</code-name-base>
+                    <build-prerequisite/>
+                    <compile-dependency/>
+                    <run-dependency>
+                        <specification-version>7.39.1</specification-version>
+                    </run-dependency>
+                </dependency>
+                <dependency>
+                    <code-name-base>org.openide.dialogs</code-name-base>
+                    <build-prerequisite/>
+                    <compile-dependency/>
+                    <run-dependency>
+                        <specification-version>7.23.1</specification-version>
+                    </run-dependency>
+                </dependency>
+                <dependency>
+                    <code-name-base>org.openide.loaders</code-name-base>
+                    <build-prerequisite/>
+                    <compile-dependency/>
+                    <run-dependency>
+                        <specification-version>7.32.1</specification-version>
+                    </run-dependency>
+                </dependency>
+                <dependency>
+                    <code-name-base>org.openide.nodes</code-name-base>
+                    <build-prerequisite/>
+                    <compile-dependency/>
+                    <run-dependency>
+                        <specification-version>7.25.1</specification-version>
+                    </run-dependency>
+                </dependency>
+                <dependency>
+                    <code-name-base>org.openide.util</code-name-base>
+                    <build-prerequisite/>
+                    <compile-dependency/>
+                    <run-dependency>
+                        <specification-version>8.19.1</specification-version>
+                    </run-dependency>
+                </dependency>
+                <dependency>
+                    <code-name-base>org.openide.util.lookup</code-name-base>
+                    <build-prerequisite/>
+                    <compile-dependency/>
+                    <run-dependency>
+                        <specification-version>8.11.1</specification-version>
+                    </run-dependency>
+                </dependency>
+                <dependency>
+                    <code-name-base>org.openide.windows</code-name-base>
+                    <build-prerequisite/>
+                    <compile-dependency/>
+                    <run-dependency>
+                        <specification-version>6.48.2</specification-version>
+                    </run-dependency>
+                </dependency>
+            </module-dependencies>
+            <public-packages>
+                <package>at.ssw.visualizer.logviewer</package>
+            </public-packages>
+        </data>
+    </configuration>
+</project>
--- /dev/null	Thu Jan 01 00:00:00 1970 +0000
+++ b/visualizer/LogViewer/nbproject/suite.properties	Wed Jun 27 17:35:32 2012 +0200
@@ -0,0 +1,1 @@
+suite.dir=${basedir}/..
--- /dev/null	Thu Jan 01 00:00:00 1970 +0000
+++ b/visualizer/LogViewer/src/META-INF/services/com.oracle.graal.visualizer.editor.CompilationViewerFactory	Wed Jun 27 17:35:32 2012 +0200
@@ -0,0 +1,1 @@
+at.ssw.visualizer.logviewer.LogCompilationViewerFactory
\ No newline at end of file
--- /dev/null	Thu Jan 01 00:00:00 1970 +0000
+++ b/visualizer/LogViewer/src/at/ssw/visualizer/logviewer/Bundle.properties	Wed Jun 27 17:35:32 2012 +0200
@@ -0,0 +1,1 @@
+OpenIDE-Module-Name=LogViewer
--- /dev/null	Thu Jan 01 00:00:00 1970 +0000
+++ b/visualizer/LogViewer/src/at/ssw/visualizer/logviewer/LogCompilationViewer.java	Wed Jun 27 17:35:32 2012 +0200
@@ -0,0 +1,51 @@
+/*
+ * Copyright (c) 2012, Oracle and/or its affiliates. All rights reserved.
+ * DO NOT ALTER OR REMOVE COPYRIGHT NOTICES OR THIS FILE HEADER.
+ *
+ * This code is free software; you can redistribute it and/or modify it
+ * under the terms of the GNU General Public License version 2 only, as
+ * published by the Free Software Foundation.
+ *
+ * This code 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
+ * version 2 for more details (a copy is included in the LICENSE file that
+ * accompanied this code).
+ *
+ * You should have received a copy of the GNU General Public License version
+ * 2 along with this work; if not, write to the Free Software Foundation,
+ * Inc., 51 Franklin St, Fifth Floor, Boston, MA 02110-1301 USA.
+ *
+ * Please contact Oracle, 500 Oracle Parkway, Redwood Shores, CA 94065 USA
+ * or visit www.oracle.com if you need additional information or have any
+ * questions.
+ *
+ */
+
+package at.ssw.visualizer.logviewer;
+
+import at.ssw.visualizer.logviewer.scene.LogScene;
+import com.oracle.graal.visualizer.editor.CompilationViewer;
+import com.sun.hotspot.igv.data.InputGraph;
+import java.awt.Component;
+import org.openide.util.Lookup;
+import org.openide.util.lookup.Lookups;
+
+class LogCompilationViewer implements CompilationViewer {
+
+    private LogScene scene;
+
+    public LogCompilationViewer() {
+        this.scene = new LogScene();
+    }
+
+    @Override
+    public Lookup getLookup() {
+        return Lookups.fixed();
+    }
+
+    @Override
+    public Component getComponent() {
+        return scene;
+    }
+}
--- /dev/null	Thu Jan 01 00:00:00 1970 +0000
+++ b/visualizer/LogViewer/src/at/ssw/visualizer/logviewer/LogCompilationViewerFactory.java	Wed Jun 27 17:35:32 2012 +0200
@@ -0,0 +1,24 @@
+/*
+ * To change this template, choose Tools | Templates
+ * and open the template in the editor.
+ */
+
+package at.ssw.visualizer.logviewer;
+
+import com.oracle.graal.visualizer.editor.CompilationViewer;
+import com.oracle.graal.visualizer.editor.SplitCompilationViewerFactory;
+import com.sun.hotspot.igv.data.InputGraph;
+
+public class LogCompilationViewerFactory extends SplitCompilationViewerFactory {
+
+    @Override
+    public String getName() {
+        return "Log";
+    }
+
+    @Override
+    protected CompilationViewer createViewer(InputGraph graph) {
+        return new LogCompilationViewer();
+    }
+
+}
--- /dev/null	Thu Jan 01 00:00:00 1970 +0000
+++ b/visualizer/LogViewer/src/at/ssw/visualizer/logviewer/layer.xml	Wed Jun 27 17:35:32 2012 +0200
@@ -0,0 +1,13 @@
+<?xml version="1.0" encoding="UTF-8"?>
+<!DOCTYPE filesystem PUBLIC "-//NetBeans//DTD Filesystem 1.2//EN" "http://www.netbeans.org/dtds/filesystem-1_2.dtd">
+<filesystem>
+    <folder name="CompilationViewer">
+        <folder name="Log">
+            <folder name="Actions">
+                <file name="at-ssw-visualizer-logviewer-scene-actions-ImportLogAction.shadow">
+                    <attr name="originalFile" stringvalue="Actions/File/at-ssw-visualizer-logviewer-scene-actions-ImportLogActions.instance"/>
+                </file>
+            </folder>
+        </folder>
+    </folder>
+</filesystem>
--- /dev/null	Thu Jan 01 00:00:00 1970 +0000
+++ b/visualizer/LogViewer/src/at/ssw/visualizer/logviewer/model/LogLine.java	Wed Jun 27 17:35:32 2012 +0200
@@ -0,0 +1,71 @@
+package at.ssw.visualizer.logviewer.model;
+
+/**
+ *
+ * @author Alexander Stipsits
+ */
+public class LogLine {
+
+    private final int lineNum;
+	private final CharSequence text;
+	private final Method method;
+	private final Scope scope;
+	private final Node node;
+	
+	LogLine(int lineNum, CharSequence text, Method method, Scope scope, Node node) {
+		this.lineNum = lineNum;
+        this.text = text;
+		this.method = method;
+		this.scope = scope;
+		this.node = node;
+	}
+    
+    /**
+     * Returns the number of the line in the log file
+     * @return file line number
+     */
+    public int getLineNumber() {
+        return lineNum;
+    }
+	
+	/**
+	 * Returns the text of the log line
+	 * @return log line text
+	 */
+	public String getText() {
+		return text.toString();
+	}
+	
+	/**
+	 * Returns the parent method
+	 * @return the parent method
+	 */
+	public Method getMethod() {
+		return method;
+	}
+	
+	/**
+	 * Returns the parent scope
+	 * @return the parent scope
+	 */
+	public Scope getScope() {
+		return scope;
+	}
+	
+	/**
+	 * Returns the parent node
+	 * @return the parent node (or <code>null</code> if the log is not in a node's context)
+	 */
+	public Node getNode() {
+		return node;
+	}
+	
+	/**
+	 * The string representation of a log line is the text of it.
+	 */
+	@Override
+	public String toString() {
+		return getText();
+	}
+	
+}
--- /dev/null	Thu Jan 01 00:00:00 1970 +0000
+++ b/visualizer/LogViewer/src/at/ssw/visualizer/logviewer/model/LogModel.java	Wed Jun 27 17:35:32 2012 +0200
@@ -0,0 +1,107 @@
+package at.ssw.visualizer.logviewer.model;
+
+import java.util.ArrayList;
+import java.util.Collections;
+import java.util.List;
+import java.util.NoSuchElementException;
+
+/**
+ *
+ * @author Alexander Stipsits
+ */
+public class LogModel {
+
+	private List<Method> methods = new ArrayList<>();
+	private List<LogLine> logs = new ArrayList<>();
+	
+	void add(Method method) {
+		methods.add(method);
+	}
+	
+	/**
+	 * Returns a list of all methods within the log model.
+	 * @return list of methods
+	 */
+	public List<Method> getMethods() {
+		return Collections.unmodifiableList(methods);
+	}
+	
+	void add(LogLine log) {
+		logs.add(log);
+	}
+	
+	/**
+	 * Returns a list of all logs
+	 * @return list of log lines
+	 */
+	public List<LogLine> getLogs() {
+		return Collections.unmodifiableList(logs);
+	}
+	
+	/**
+	 * Returns a list of logs which are before and after a specific log line (and the line itself).
+	 * @param log reference log line
+	 * @param pre number of lines before the reference line
+	 * @param post number of lines after the reference line
+	 * @return list of log lines (size: pre + post + 1)
+	 */
+	public List<LogLine> range(LogLine log, int pre, int post) {
+		if(log == null) throw new NoSuchElementException();
+		
+		List<LogLine> logs = getLogs();
+		int index = logs.indexOf(log);
+		
+		if(index == -1) throw new NoSuchElementException();
+		
+		int fromIndex = index - pre;
+		int toIndex = index + post + 1;
+		
+		return getLogs().subList(fromIndex > 0 ? fromIndex : 0, toIndex < logs.size() ? toIndex : logs.size());
+	}
+	
+	/**
+	 * Returns a string with log l which are before and after a specific log line (and the line itself).
+	 * @param log reference log line
+	 * @param pre number of lines before the reference line
+	 * @param post number of lines after the reference line
+	 * @return list of log lines (size: pre + post + 1)
+	 */
+	public String rangeAsString(LogLine log, int pre, int post) {
+		List<LogLine> list = range(log, pre, post);
+		
+		String ls = System.getProperty("line.separator");
+		
+		boolean first = true;
+		StringBuilder buf = new StringBuilder();
+		for(LogLine line : list) {
+			if(!first) buf.append(ls);
+			else first = false;
+			
+			buf.append(line);
+		}
+		
+		return buf.toString();
+	}
+	
+	public LogLine getLogLine(int lineNum) {
+		int index = lineNum;
+		if(index >= logs.size()) index = logs.size()-1;
+		
+		LogLine logLine = logs.get(lineNum);
+		while(logLine.getLineNumber() > lineNum) {
+			logLine = logs.get(--index);
+		}
+		while(logLine.getLineNumber() < lineNum) {
+			logLine = logs.get(++index);
+		}
+		
+		return logLine;
+	}
+	
+	public List<LogLine> range(int lineNum, int pre, int post) {
+		LogLine logLine = getLogLine(lineNum);
+		return range(logLine, pre, post);
+	}
+	
+	
+}
--- /dev/null	Thu Jan 01 00:00:00 1970 +0000
+++ b/visualizer/LogViewer/src/at/ssw/visualizer/logviewer/model/LogParser.java	Wed Jun 27 17:35:32 2012 +0200
@@ -0,0 +1,210 @@
+package at.ssw.visualizer.logviewer.model;
+
+import at.ssw.visualizer.logviewer.model.io.FileLine;
+import at.ssw.visualizer.logviewer.model.io.ProgressMonitor;
+import at.ssw.visualizer.logviewer.model.io.SeekableFile;
+import at.ssw.visualizer.logviewer.model.io.SeekableFileReader;
+import java.io.BufferedReader;
+import java.io.File;
+import java.io.IOException;
+import java.util.ArrayList;
+import java.util.Collections;
+import java.util.List;
+import java.util.regex.Matcher;
+import java.util.regex.Pattern;
+
+/**
+ *
+ * @author Alexander Stipsits
+ */
+public class LogParser {
+	
+	private static final Pattern METHOD_PATTERN =
+		Pattern.compile("Finished target method HotSpotMethod<(.+?)>, isStub (true|false)");
+	private static final Pattern SCOPE_PATTERN =
+		Pattern.compile("scope: (.+?)");
+	private static final Pattern NODE_PATTERN =
+		Pattern.compile(".*?([0-9]+)\\|([0-9a-zA-Z.]+).*?");
+	
+	private final List<ParseErrorListener> errorListeners = new ArrayList<>();
+	private final List<ParseError> errors = new ArrayList<>();
+	
+	/**
+	 * Parses a log file without any feedback. When the parsing process is finished, the method returns a LogModel-object.
+	 * @param file log file
+	 * @return model-object
+	 * @throws IOException
+	 * @throws LogParserException
+	 */
+	public LogModel parse(File file) throws IOException {
+		return parse(file, null);
+	}
+	
+	/**
+	 * Parses a log file and uses a ProgressMonitor to give the caller a progress feedback. It returns a LogModel-object.
+	 * @param file log file
+	 * @param monitor monitor for progress monitoring (uses default gap)
+	 * @return model-object
+	 * @throws IOException
+	 * @throws LogParserException
+	 */
+	public LogModel parse(File file, ProgressMonitor monitor) throws IOException {
+		return parse(file, monitor, SeekableFileReader.DEFAULT_GAP);
+	} 
+	
+	/**
+	 * Parses a log file and uses a ProgressMonitor to give the caller a progress feedback. It returns a LogModel-object.
+	 * @param file log file
+	 * @param monitor monitor for progress monitoring (uses custom gap)
+	 * @param gap custom gap (every x bytes read from the file, the monitor will be informed)
+	 * @return model-object
+	 * @throws IOException
+	 * @throws LogParserException
+	 */
+	public LogModel parse(File file, ProgressMonitor monitor, int gap) throws IOException {
+		errors.clear();
+		SeekableFileReader reader = new SeekableFileReader(file, monitor, gap);
+		SeekableFile seekableFile = reader.getSeekableFile();
+		BufferedReader r = new BufferedReader(reader);
+		
+		LogModel model = new LogModel();
+		Method currentMethod = new Method(model);
+		Scope currentScope = null;
+		
+		Matcher matcher;
+		boolean methodNameSet = false;
+		
+		int lineNum = -1;
+		String line;
+		while((line = r.readLine()) != null) {
+			lineNum++;
+			
+			methodNameSet = false;
+			
+			matcher = SCOPE_PATTERN.matcher(line);
+			if(matcher.matches()) {
+				String name = matcher.group(1);
+				currentScope = new Scope(name, currentMethod);
+				currentMethod.add(currentScope);
+			}
+            
+            matcher = METHOD_PATTERN.matcher(line);
+            boolean methodPatternMatches = matcher.matches();
+            
+            if(methodPatternMatches){
+                currentScope = null;
+				String name = matcher.group(1);
+				boolean isStub = Boolean.parseBoolean(matcher.group(2));
+				currentMethod.init(name, isStub);
+				model.add(currentMethod);
+				methodNameSet = true;
+            }
+			
+			Node currentNode = null;
+			matcher = NODE_PATTERN.matcher(line);
+			if(matcher.matches()) {
+                if(currentScope == null){
+                	raiseError(lineNum, line, "scope is missing");
+                	continue;
+                }
+				int number = Integer.parseInt(matcher.group(1));
+				currentNode = currentScope.getNode(number);
+				if(currentNode == null) {
+					String name = matcher.group(2);
+					currentNode = new Node(number, name, currentScope);
+					currentScope.add(currentNode);
+				}
+			}
+			
+			FileLine fileLine = seekableFile.get(lineNum);
+			LogLine log = new LogLine(lineNum, fileLine, currentMethod, currentScope, currentNode);
+			if(currentNode != null) currentNode.add(log);
+			if(currentScope != null) currentScope.add(log);
+			currentMethod.add(log);
+			model.add(log);
+            
+            if(methodPatternMatches) {
+				currentMethod = new Method(model);
+			}
+		}
+		
+		if(!methodNameSet) raiseError(lineNum, line, "unexpected end of stream");
+		
+		return model;
+	}
+	
+	private void raiseError(int lineNum, String line, String message) {
+		ParseError error = new ParseError(lineNum, line, message);
+		errors.add(error);
+		for(ParseErrorListener listener : errorListeners) {
+			listener.errorOccurred(error);
+		}
+	}
+	
+	/**
+	 * Adds a ParseErrorListener to the listeners
+	 * @param listener
+	 */
+	public void addParseErrorListener(ParseErrorListener listener) {
+		errorListeners.add(listener);
+	}
+	
+	public void removeParseErrorListener(ParseErrorListener listener) {
+		errorListeners.remove(listener);
+	}
+	
+	/**
+	 * Checks if errors occurred during the parsing process
+	 * @return <code>true</code> if there are errors stored, <code>false</code> otherwise
+	 */
+	public boolean hasErrors() {
+		return !errors.isEmpty();
+	}
+	
+	/**
+	 * Returns a list of all errors.
+	 * @return error list
+	 */
+	public List<ParseError> getErrors() {
+		return Collections.unmodifiableList(errors);
+	}
+	
+	public static class ParseError {
+		
+		private final int lineNum;
+		private final String line;
+		private final String message;
+		
+		private ParseError(int lineNum, String line, String message) {
+			this.lineNum = lineNum;
+			this.message = message;
+			this.line = line;
+		}
+		
+		public int getLineNumber() {
+			return lineNum;
+		}
+		
+		public String getLine() {
+			return line;
+		}
+		
+		public String getMessage() {
+			return message;
+		}
+		
+	}
+	
+	public static interface ParseErrorListener {
+		
+		/**
+		 * Called when a new error occurs.<br><br>
+		 * <b>Attention</b>: This method runs on the same thread as the parsing process.
+		 * If time-consuming tasks should be executed, please consider multithreading.
+		 * @param error
+		 */
+		public void errorOccurred(ParseError error);
+		
+	}
+	
+}
--- /dev/null	Thu Jan 01 00:00:00 1970 +0000
+++ b/visualizer/LogViewer/src/at/ssw/visualizer/logviewer/model/Method.java	Wed Jun 27 17:35:32 2012 +0200
@@ -0,0 +1,86 @@
+package at.ssw.visualizer.logviewer.model;
+
+import java.util.ArrayList;
+import java.util.Collections;
+import java.util.List;
+
+/**
+ *
+ * @author Alexander Stipsits
+ */
+public class Method {
+
+	private String name;
+	private boolean isStub;
+	private final LogModel model;
+	private List<Scope> scopes = new ArrayList<>();
+	private List<LogLine> logs = new ArrayList<>();
+	
+	Method(LogModel model) {
+		name = null;
+		isStub = false;
+		this.model = model;
+	}
+	
+	void init(String name, boolean isStub) {
+		this.name = name;
+		this.isStub = isStub;
+	}
+	
+	/**
+	 * Returns the name of the method
+	 * @return the method name
+	 */
+	public String getName() {
+		return name;
+	}
+	
+	/**
+	 * Returns <code>true</code> if this is a stub method.
+	 * @return <code>true</code> if is stub, <code>false</code> otherwise
+	 */
+	public boolean isStub() {
+		return isStub;
+	}
+	
+	/**
+	 * Returns the model which holds this method.
+	 * @return model-object
+	 */
+	public LogModel getModel() {
+		return model;
+	}
+	
+	void add(Scope scope) {
+		scopes.add(scope);
+	}
+	
+	/**
+	 * Returns a list of all scopes inside a target method.
+	 * @return list of scopes
+	 */
+	public List<Scope> getScopes() {
+		return scopes;
+	}
+	
+	void add(LogLine log) {
+		logs.add(log);
+	}
+	
+	/**
+	 * Returns a list of all logs in the context of the method.
+	 * @return list of log lines
+	 */
+	public List<LogLine> getLogs() {
+		return Collections.unmodifiableList(logs);
+	}
+	
+	/**
+	 * The string representation of a method is its name.
+	 */
+	@Override
+	public String toString() {
+		return getName();
+	}
+	
+}
--- /dev/null	Thu Jan 01 00:00:00 1970 +0000
+++ b/visualizer/LogViewer/src/at/ssw/visualizer/logviewer/model/Node.java	Wed Jun 27 17:35:32 2012 +0200
@@ -0,0 +1,68 @@
+package at.ssw.visualizer.logviewer.model;
+
+import java.util.ArrayList;
+import java.util.Collections;
+import java.util.List;
+
+/**
+ *
+ * @author Alexander Stipsits
+ */
+public class Node {
+
+	private final int number;
+	private final String name;
+	private final Scope scope;
+	private List<LogLine> logs = new ArrayList<>();
+	
+	
+	Node(int number, String name, Scope scope) {
+		this.number = number;
+		this.name = name;
+		this.scope = scope;
+	}
+	
+	/**
+	 * Returns the node number.
+	 * @return the node number
+	 */
+	public int getNumber() {
+		return number;
+	}
+	
+	/**
+	 * Returns the node name.
+	 * @return the node name
+	 */
+	public String getName() {
+		return name;
+	}
+	
+	/**
+	 * Returns the parent scope.
+	 * @return the parent scope
+	 */
+	public Scope getScope() {
+		return scope;
+	}
+	
+	void add(LogLine log) {
+		logs.add(log);
+	}
+	
+	/**
+	 * Returns a list of all logs within a nodes context.
+	 * @return list of log lines
+	 */
+	public List<LogLine> getLogs() {
+		return Collections.unmodifiableList(logs);
+	}
+	
+	/**
+	 * The string represenation of a node is &quot;&lt;name&gt; (&lt;number&gt;)&quot;.
+	 */
+	@Override
+	public String toString() {
+		return getName() + "(" + getNumber() + ")";
+	}
+}
--- /dev/null	Thu Jan 01 00:00:00 1970 +0000
+++ b/visualizer/LogViewer/src/at/ssw/visualizer/logviewer/model/Scope.java	Wed Jun 27 17:35:32 2012 +0200
@@ -0,0 +1,84 @@
+package at.ssw.visualizer.logviewer.model;
+
+import java.util.ArrayList;
+import java.util.Collections;
+import java.util.List;
+
+/**
+ *
+ * @author Alexander Stipsits
+ */
+public class Scope {
+
+	private final String name;
+	private final Method method;
+	private List<Node> nodes = new ArrayList<>();
+	private List<LogLine> logs = new ArrayList<>();
+	
+	Scope(String name, Method method) {
+		this.name = name;
+		this.method = method;
+	}
+	
+	/**
+	 * Returns the scope name.
+	 * @return the scope name
+	 */
+	public String getName() {
+		return name;
+	}
+	
+	/**
+	 * Returns the parent method.
+	 * @return the parent method
+	 */
+	public Method getMethod() {
+		return method;
+	}
+	
+	void add(Node node) {
+		nodes.add(node);
+	}
+	
+	/**
+	 * Returns a list of all node-objects inside a scope.
+	 * @return list of nodes
+	 */
+	public List<Node> getNodes() {
+		return Collections.unmodifiableList(nodes);
+	}
+	
+	void add(LogLine log) {
+		logs.add(log);
+	}
+	
+	/**
+	 * Returns a list of all logs inside a scope.
+	 * @return list of log lines
+	 */
+	public List<LogLine> getLogs() {
+		return logs;
+	}
+	
+	/**
+	 * Returns the node inside a scope with the given number.
+	 * @param number node number
+	 * @return node-object
+	 */
+	public Node getNode(int number) {
+		for(Node node : nodes) {
+			if(node.getNumber() == number) return node;
+		}
+		return null;
+	}
+	
+	/**
+	 * The string representation of a scope is its name.
+	 * @see java.lang.Object#toString()
+	 */
+	@Override
+	public String toString() {
+		return getName();
+	}
+
+}
--- /dev/null	Thu Jan 01 00:00:00 1970 +0000
+++ b/visualizer/LogViewer/src/at/ssw/visualizer/logviewer/model/filter/Filter.java	Wed Jun 27 17:35:32 2012 +0200
@@ -0,0 +1,40 @@
+package at.ssw.visualizer.logviewer.model.filter;
+
+import at.ssw.visualizer.logviewer.model.LogLine;
+
+/**
+ *
+ * @author Alexander Stipsits
+ */
+public interface Filter {
+
+	/**
+	 * Sets the constraints for a filter.<br>
+	 * They can be every type of objects. In general these are strings, but it can also be
+	 * a Integer (like in the <code>NodeFilter</code>):<br>
+	 * <code>Filter nodeFilter = filterManager.getNodeFilter();
+	 * filter.setConstraints((String)nodeName, (Integer)nodeNumber);</code>
+	 * @param constraints filter constraints
+	 */
+	void setConstraints(Object ... constraints);
+	
+	/**
+	 * Indicates whether to keep a log line in the filter results, or not.
+	 * @param line log line which should be checked
+	 * @return <code>true</code> if the line should be kept, <code>false</code> otherwise
+	 */
+	boolean keep(LogLine line);
+	
+	/**
+	 * Activates or deactivates a filter. Deactivated filters will be ignored.
+	 * @param active <code>true</code> to activate, <code>false</code> to deactivate
+	 */
+	void setActive(boolean active);
+	
+	/**
+	 * Returns the activation status
+	 * @return <code>true</code> for an active filter, <code>false</code> otherwise
+	 */
+	boolean isActive();
+	
+}
--- /dev/null	Thu Jan 01 00:00:00 1970 +0000
+++ b/visualizer/LogViewer/src/at/ssw/visualizer/logviewer/model/filter/FilterManager.java	Wed Jun 27 17:35:32 2012 +0200
@@ -0,0 +1,92 @@
+package at.ssw.visualizer.logviewer.model.filter;
+
+import at.ssw.visualizer.logviewer.model.LogLine;
+import at.ssw.visualizer.logviewer.model.LogModel;
+import java.util.ArrayList;
+import java.util.List;
+
+/**
+ *
+ * @author Alexander Stipsits
+ */
+public class FilterManager {
+	
+	private final List<Filter> filters = new ArrayList<>();
+	private final Filter methodFilter, scopeFilter, nodeFilter, fulltextFilter;
+	
+	/**
+	 * Creates a new instance of the filter manager.
+	 */
+	public FilterManager() {
+		add(methodFilter = new MethodFilter());
+		add(scopeFilter = new ScopeFilter());
+		add(nodeFilter = new NodeFilter());
+		add(fulltextFilter = new FullTextFilter());
+	}
+	
+	/**
+	 * Executes a filtering process with the given log model as subject. Inactive filters will be ignored.
+	 * @param model subject
+	 * @return filtered list of log lines
+	 * @throws InterruptedException 
+	 */
+	public List<LogLine> execute(LogModel model) throws InterruptedException {
+		List<LogLine> input = model.getLogs();
+		List<LogLine> output = new ArrayList<>();
+		
+		for(LogLine line : input) {
+			if(Thread.interrupted()) throw new InterruptedException();
+			if(keep(line)) output.add(line);
+		}
+		
+		if(Thread.interrupted()) throw new InterruptedException();
+		
+		return output;
+	}
+	
+	private boolean keep(LogLine line) {
+		boolean keep = true;
+		for(Filter filter : filters) {
+			keep = keep && (!filter.isActive() || filter.keep(line));
+		}
+		return keep;
+	}
+	
+	private Filter add(Filter filter) {
+		filters.add(filter);
+		return filter;
+	}
+	
+	/**
+	 * Returns the instance of a method filter
+	 * @return method filter
+	 */
+	public Filter getMethodFilter() {
+		return methodFilter;
+	}
+	
+	/**
+	 * Returns the instance of a scope filter
+	 * @return scope filter
+	 */
+	public Filter getScopeFilter() {
+		return scopeFilter;
+	}
+	
+	/**
+	 * Returns the instance of a node filter
+	 * @return node filter
+	 */
+	public Filter getNodeFilter() {
+		return nodeFilter;
+	}
+	
+	/**
+	 * Returns the instance of a full text filter
+	 * @return full text filter
+	 */
+	public Filter getFullTextFilter() {
+		return fulltextFilter;
+	}
+	
+}
--- /dev/null	Thu Jan 01 00:00:00 1970 +0000
+++ b/visualizer/LogViewer/src/at/ssw/visualizer/logviewer/model/filter/FullTextFilter.java	Wed Jun 27 17:35:32 2012 +0200
@@ -0,0 +1,53 @@
+package at.ssw.visualizer.logviewer.model.filter;
+
+import at.ssw.visualizer.logviewer.model.LogLine;
+import java.util.regex.Matcher;
+import java.util.regex.Pattern;
+
+/**
+ *
+ * @author Alexander Stipsits
+ */
+class FullTextFilter implements Filter {
+
+	private Pattern p;
+	private boolean active = true;
+	
+	@Override
+	public void setConstraints(Object ... constraints) {
+        this.p = null;
+		for(Object constraint : constraints) {
+			setConstraint(constraint);
+		}
+	}
+	
+	private void setConstraint(Object constraint) {
+		if(constraint instanceof String) {
+			if(((String)constraint).trim().length() > 0) {
+				this.p = Pattern.compile((String)constraint);
+			}
+			else {
+				this.p = null;
+			}
+		}
+	}
+
+	@Override
+	public boolean keep(LogLine line) {
+		if(p == null) return true;
+		
+		Matcher matcher = p.matcher(line.getText());
+		return matcher.find();
+	}
+
+	@Override
+	public void setActive(boolean active) {
+		this.active = active;
+	}
+
+	@Override
+	public boolean isActive() {
+		return active;
+	}
+
+}
--- /dev/null	Thu Jan 01 00:00:00 1970 +0000
+++ b/visualizer/LogViewer/src/at/ssw/visualizer/logviewer/model/filter/MethodFilter.java	Wed Jun 27 17:35:32 2012 +0200
@@ -0,0 +1,53 @@
+package at.ssw.visualizer.logviewer.model.filter;
+
+import at.ssw.visualizer.logviewer.model.LogLine;
+import java.util.regex.Matcher;
+import java.util.regex.Pattern;
+
+/**
+ *
+ * @author Alexander Stipsits
+ */
+class MethodFilter implements Filter {
+
+	private Pattern p;
+	private boolean active = true;
+	
+	@Override
+	public void setConstraints(Object ... constraints) {
+        this.p = null;
+		for(Object constraint : constraints) {
+			setConstraint(constraint);
+		}
+	}
+	
+	private void setConstraint(Object constraint) {
+		if(constraint instanceof String) {
+			if(((String)constraint).trim().length() > 0) {
+				this.p = Pattern.compile((String)constraint);
+			}
+			else {
+				this.p = null;
+			}
+		}
+	}
+
+	@Override
+	public boolean keep(LogLine line) {
+		if(p == null) return true;
+		
+		Matcher matcher = p.matcher(line.getMethod().getName());
+		return matcher.find();
+	}
+
+	@Override
+	public void setActive(boolean active) {
+		this.active = active;
+	}
+
+	@Override
+	public boolean isActive() {
+		return active;
+	}
+
+}
--- /dev/null	Thu Jan 01 00:00:00 1970 +0000
+++ b/visualizer/LogViewer/src/at/ssw/visualizer/logviewer/model/filter/NodeFilter.java	Wed Jun 27 17:35:32 2012 +0200
@@ -0,0 +1,71 @@
+package at.ssw.visualizer.logviewer.model.filter;
+
+import at.ssw.visualizer.logviewer.model.LogLine;
+import java.util.regex.Matcher;
+import java.util.regex.Pattern;
+
+/**
+ *
+ * @author Alexander Stipsits
+ */
+class NodeFilter implements Filter {
+
+	private Pattern p;
+	private Integer nodeNum;
+	private boolean active = true;
+	
+	@Override
+	public void setConstraints(Object ... constraints) {
+        this.nodeNum = null;
+        this.p = null;
+		for(Object constraint : constraints) {
+			setConstraint(constraint);
+		}
+	}
+	
+	private void setConstraint(Object constraint) {
+		if(constraint instanceof String) {
+		
+			if(((String)constraint).trim().length() > 0) {
+				this.p = Pattern.compile((String)constraint);
+			}
+			else {
+				this.p = null;
+			}
+		}
+		else if(constraint instanceof Integer) {
+			this.nodeNum = (Integer)constraint;
+		}
+		
+	}
+
+	@Override
+	public boolean keep(LogLine line) {
+		if(p == null && nodeNum == null) return true; 
+		
+		if(line.getNode() == null) return false;
+		
+		boolean keep = false;
+		
+		if(p != null) {
+			Matcher matcher = p.matcher(line.getNode().getName());
+			keep = keep || matcher.find();
+		}
+		if(nodeNum != null) {
+			keep = keep || nodeNum.equals(line.getNode().getNumber());
+		}
+		
+		return keep;		
+	}
+
+	@Override
+	public void setActive(boolean active) {
+		this.active = active;
+	}
+
+	@Override
+	public boolean isActive() {
+		return active;
+	}
+
+}
--- /dev/null	Thu Jan 01 00:00:00 1970 +0000
+++ b/visualizer/LogViewer/src/at/ssw/visualizer/logviewer/model/filter/ScopeFilter.java	Wed Jun 27 17:35:32 2012 +0200
@@ -0,0 +1,55 @@
+package at.ssw.visualizer.logviewer.model.filter;
+
+import at.ssw.visualizer.logviewer.model.LogLine;
+import java.util.regex.Matcher;
+import java.util.regex.Pattern;
+
+/**
+ *
+ * @author Alexander Stipsits
+ */
+class ScopeFilter implements Filter {
+
+	private Pattern p;
+	private boolean active = true;
+	
+	@Override
+	public void setConstraints(Object ... constraints) {
+        this.p = null;
+		for(Object constraint : constraints) {
+			setConstraint(constraint);
+		}
+	}
+	
+	private void setConstraint(Object constraint) {
+		if(constraint instanceof String) {
+			if(((String)constraint).trim().length() > 0) {
+				this.p = Pattern.compile((String)constraint);
+			}
+			else {
+				this.p = null;
+			}
+		}
+	}
+
+	@Override
+	public boolean keep(LogLine line) {
+		if(p == null) return true;
+        
+        if(line.getScope() == null) return false;
+		
+		Matcher matcher = p.matcher(line.getScope().getName());
+		return matcher.find();
+	}
+
+	@Override
+	public void setActive(boolean active) {
+		this.active = active;
+	}
+
+	@Override
+	public boolean isActive() {
+		return active;
+	}
+
+}
--- /dev/null	Thu Jan 01 00:00:00 1970 +0000
+++ b/visualizer/LogViewer/src/at/ssw/visualizer/logviewer/model/io/FileLine.java	Wed Jun 27 17:35:32 2012 +0200
@@ -0,0 +1,58 @@
+package at.ssw.visualizer.logviewer.model.io;
+
+import java.io.IOException;
+import java.lang.ref.WeakReference;
+
+/**
+ *
+ * @author Alexander Stipsits
+ */
+public class FileLine implements CharSequence {
+
+	private final SeekableFile seekableFile;
+	private WeakReference<String> cache;
+	public final long off;
+	public final int len;
+	
+	public FileLine(SeekableFile seekableFile, long off, int len) {
+		this.seekableFile = seekableFile;
+		this.off = off;
+		this.len = len;
+	}
+	
+	@Override
+	public String toString() {
+		String line = null;
+		
+		if(cache != null) line = cache.get();
+		
+		if(line == null) {
+			try {
+				line = seekableFile.read(this);
+				cache = new WeakReference<>(line);
+			} catch (IOException e) {
+				e.printStackTrace();
+			}
+		}
+		
+		return line;
+	}
+
+	@Override
+	public int length() {
+		return len;
+	}
+
+	@Override
+	public char charAt(int index) {
+		String line = toString();
+		return line != null ? line.charAt(index) : 0;
+	}
+
+	@Override
+	public CharSequence subSequence(int start, int end) {
+		String line = toString();
+		return line != null ? line.subSequence(start, end) : "";
+	}
+	
+}
--- /dev/null	Thu Jan 01 00:00:00 1970 +0000
+++ b/visualizer/LogViewer/src/at/ssw/visualizer/logviewer/model/io/ProgressMonitor.java	Wed Jun 27 17:35:32 2012 +0200
@@ -0,0 +1,24 @@
+package at.ssw.visualizer.logviewer.model.io;
+
+/**
+ *
+ * @author Alexander Stipsits
+ */
+public interface ProgressMonitor {
+
+	/**
+	 * Triggered when some work has been done.<br><br>
+	 * <b>Attention</b>: This method runs on the same thread as the reading process.
+	 * If time-consuming tasks should be executed, please consider multithreading.
+	 * @param percentage Value in range between 0 and 1 to indicate how much work has been done.
+	 */
+	public void worked(float percentage);
+	
+	/**
+	 * Triggered when work is done completely.<br><br>
+	 * <b>Attention</b>: This method runs on the same thread as the reading process.
+	 * If time-consuming tasks should be executed, please consider multithreading.
+	 */
+	public void finished();
+	
+}
--- /dev/null	Thu Jan 01 00:00:00 1970 +0000
+++ b/visualizer/LogViewer/src/at/ssw/visualizer/logviewer/model/io/SeekableFile.java	Wed Jun 27 17:35:32 2012 +0200
@@ -0,0 +1,50 @@
+package at.ssw.visualizer.logviewer.model.io;
+
+import java.io.*;
+import java.util.ArrayList;
+import java.util.List;
+
+/**
+ *
+ * @author Alexander Stipsits
+ */
+public class SeekableFile implements Closeable {
+
+	private final RandomAccessFile raf;
+	private List<FileLine> lines = new ArrayList<>();
+	
+	SeekableFile(File file) throws FileNotFoundException {
+		raf = new RandomAccessFile(file, "r");
+	}
+	
+	void addLine(long off, int len) {
+		lines.add(new FileLine(this, off, len));
+	}
+	
+	public int size() {
+		return lines.size();
+	}
+	
+	public FileLine get(int lineNum) throws IOException {
+		FileLine line = lines.get(lineNum);
+		return line;
+	}
+	
+	String read(FileLine line) throws IOException {
+		if(line == null) throw new IllegalArgumentException("line is null");
+		raf.seek(line.off);
+
+		byte [] buf = new byte[line.len];
+		
+		if(raf.read(buf) != line.len) throw new IOException("error while reading line");
+		
+		return new String(buf);
+	}
+
+	@Override
+	public void close() throws IOException {
+		raf.close();
+	}
+	
+	
+}
--- /dev/null	Thu Jan 01 00:00:00 1970 +0000
+++ b/visualizer/LogViewer/src/at/ssw/visualizer/logviewer/model/io/SeekableFileReader.java	Wed Jun 27 17:35:32 2012 +0200
@@ -0,0 +1,101 @@
+package at.ssw.visualizer.logviewer.model.io;
+
+import java.io.File;
+import java.io.FileReader;
+import java.io.IOException;
+import java.io.Reader;
+
+/**
+ *
+ * @author Alexander Stipsits
+ */
+public class SeekableFileReader extends Reader {
+	
+	public static final int DEFAULT_GAP = 1024; // in bytes
+
+	private final FileReader reader;
+	private SeekableFile seekableFile;
+	private ProgressMonitor monitor;
+	private long filelen;
+	private int gap;
+	
+	public SeekableFileReader(File file) throws IOException {
+		super(file);
+		reader = new FileReader(file);
+		seekableFile = new SeekableFile(file);
+	}
+	
+	public SeekableFileReader(File file, ProgressMonitor monitor, int gap) throws IOException {
+		this(file);
+		this.monitor = monitor;
+		this.filelen = file.length();
+		this.gap = gap;
+	}
+	
+	public SeekableFileReader(File file, ProgressMonitor monitor) throws IOException {
+		this(file, monitor, DEFAULT_GAP);
+	}
+	
+	public SeekableFile getSeekableFile() {
+		return seekableFile;
+	}
+
+    @Override
+	public void close() throws IOException {
+		reader.close();		
+	}
+	
+	private long bytes = 0;
+	private boolean skipLF = false;
+	private long off;
+	private long lastGapped = 0;
+	private boolean finished = false;
+
+	@Override
+	public int read(char[] cbuf, int offset, int length) throws IOException {
+		
+		int l = reader.read(cbuf, offset, length);
+		
+		if(l == -1 && !finished) {
+			seekableFile.addLine(off, (int)(bytes-off));
+			finished = true;
+			if(monitor != null) {
+				monitor.worked(1);
+				monitor.finished();
+			}
+		}
+		
+		for(int i = 0; i < l; ++i) {
+			Character ch = new Character(cbuf[i]);
+			int chlen = ch.toString().getBytes(reader.getEncoding()).length;
+				
+			if(skipLF && ch != '\n') {
+				seekableFile.addLine(off, (int)(bytes-off-chlen));
+				off = bytes;
+				skipLF = false;
+			}
+			
+			bytes += chlen;
+			
+			if(monitor != null) {
+				if(bytes > lastGapped+gap) {
+					lastGapped = bytes;
+					monitor.worked(((float)bytes)/((float)filelen));
+				}
+			}
+			
+			if(ch == '\r') {
+				skipLF = true;
+			}
+			if(ch == '\n') {
+				seekableFile.addLine(off, (int)(bytes-off-(skipLF ? 2*chlen : chlen)));
+				off = bytes;
+				skipLF = false;
+			}
+		}
+		
+		return l;
+	}
+	
+	
+}
--- /dev/null	Thu Jan 01 00:00:00 1970 +0000
+++ b/visualizer/LogViewer/src/at/ssw/visualizer/logviewer/scene/BookmarkDialog.java	Wed Jun 27 17:35:32 2012 +0200
@@ -0,0 +1,94 @@
+package at.ssw.visualizer.logviewer.scene;
+
+import at.ssw.visualizer.logviewer.model.LogLine;
+import java.awt.BorderLayout;
+import java.awt.Dimension;
+import java.awt.GridLayout;
+import java.awt.Window;
+import java.awt.event.ActionEvent;
+import java.awt.event.ActionListener;
+import java.awt.event.MouseAdapter;
+import java.awt.event.MouseEvent;
+import java.util.List;
+import javax.swing.*;
+
+/**
+ *
+ * @author Katrin Strassl
+ */
+public class BookmarkDialog extends JDialog {
+    
+    private JList lstBookmarks;
+    private List<LogLine> logLines;
+    
+    public BookmarkDialog(List<LogLine> bookmarks, Window parent) {
+        super(parent, JDialog.ModalityType.APPLICATION_MODAL);
+        
+        this.logLines = bookmarks;
+        
+        DefaultListModel mdlBookmarks = new DefaultListModel();
+        lstBookmarks = new JList(mdlBookmarks);
+        JScrollPane jspBookmarks = new JScrollPane(lstBookmarks);
+        
+        for (LogLine bookmark : bookmarks) {
+            mdlBookmarks.addElement(bookmark.getLineNumber() + ": Method " + bookmark.getMethod() + " - Scope " + bookmark.getScope() + " - " + bookmark.getText());
+        }
+        
+        JButton btnOk = new JButton("Go to");
+        JButton btnCancel = new JButton("Cancel");
+
+        // init listeners
+        lstBookmarks.addMouseListener(new MouseAdapter() {
+            @Override
+            public void mouseClicked(MouseEvent e) {
+                if (e.getClickCount() == 2) {
+                    close();
+                }
+            }
+        });
+        
+        btnOk.addActionListener(new ActionListener() {
+            @Override
+            public void actionPerformed(ActionEvent ae) {
+                if (lstBookmarks.isSelectionEmpty())
+                    return;
+                close();
+            }
+        });
+        btnCancel.addActionListener(new ActionListener() {
+            @Override
+            public void actionPerformed(ActionEvent ae) {
+                lstBookmarks.clearSelection();
+                close();
+            }
+        });
+        
+        // build layout
+        JPanel pnlButtons = new JPanel();
+        
+        pnlButtons.setLayout(new GridLayout(1, 2));
+        pnlButtons.add(btnOk);
+        pnlButtons.add(btnCancel);
+        
+        this.setLayout(new BorderLayout());
+        this.add(jspBookmarks, BorderLayout.CENTER);
+        this.add(pnlButtons, BorderLayout.SOUTH);
+        
+        this.pack();
+        this.setMinimumSize(new Dimension(600, this.getPreferredSize().height));
+        this.setLocationRelativeTo(parent);
+        this.setResizable(false);
+        this.setVisible(true);
+    }
+    
+    public LogLine getTarget() {
+        if (lstBookmarks.isSelectionEmpty())
+            return null;
+        return logLines.get(lstBookmarks.getSelectedIndex());
+    }
+    
+    private void close() {
+        this.setVisible(false);
+        this.dispose();
+    }
+}
--- /dev/null	Thu Jan 01 00:00:00 1970 +0000
+++ b/visualizer/LogViewer/src/at/ssw/visualizer/logviewer/scene/BookmarkableLogViewer.java	Wed Jun 27 17:35:32 2012 +0200
@@ -0,0 +1,358 @@
+package at.ssw.visualizer.logviewer.scene;
+
+import at.ssw.visualizer.logviewer.model.LogLine;
+import java.awt.*;
+import java.awt.event.MouseAdapter;
+import java.awt.event.MouseEvent;
+import java.awt.event.MouseListener;
+import java.util.ArrayList;
+import java.util.List;
+import java.util.Map;
+import java.util.TreeMap;
+import javax.swing.JPanel;
+import javax.swing.JScrollPane;
+import javax.swing.JTextPane;
+import javax.swing.text.BadLocationException;
+import javax.swing.text.DefaultHighlighter;
+import javax.swing.text.Highlighter;
+import javax.swing.text.JTextComponent;
+import org.openide.util.Exceptions;
+
+/**
+ *
+ * @author Katrin Strassl
+ */
+public class BookmarkableLogViewer extends JPanel {
+
+    private static final Color BOOKMARK_COLOR = Color.CYAN;
+    
+    private static final Highlighter.HighlightPainter BOOKMARK_PAINTER =
+            new DefaultHighlighter.DefaultHighlightPainter(BOOKMARK_COLOR);
+    
+    private JTextPane txpText;
+    private JTextPane txpLines;
+    private Highlighter textHighlighter;
+    private Highlighter lineHighlighter;
+    private FontMetrics fm;
+    private Map<Integer, Bookmark> bookmarks = new TreeMap<>();
+    private List<LogLine> logLines = new ArrayList<>();
+
+    public BookmarkableLogViewer() {
+        init();
+    }
+
+    private void init() {
+        txpText = new JTextPane();
+        txpLines = new JTextPane();
+
+        // needed for correct layouting
+        Insets ins = txpLines.getInsets();
+        txpLines.setMargin(new Insets(ins.top + 1, ins.left, ins.bottom, ins.right));
+
+        textHighlighter = new BookmarkHighlighter();
+        lineHighlighter = new BookmarkHighlighter();
+
+        txpText.setHighlighter(textHighlighter);
+        //txpText.setMinimumSize(new Dimension(100, 100));
+        txpText.setFont(new Font(Font.MONOSPACED, Font.PLAIN, 12));
+        txpText.setEditable(false);
+
+        txpLines.setHighlighter(lineHighlighter);
+        txpLines.setFont(new Font(Font.MONOSPACED, Font.PLAIN, 12));
+        txpLines.setBackground(Color.LIGHT_GRAY);
+        txpLines.setEnabled(false);
+        txpLines.setForeground(Color.BLACK);
+        txpLines.addMouseListener(mouseInputListener);
+
+        fm = txpText.getFontMetrics(txpText.getFont());
+
+        JPanel pnlBookmarks = new JPanel();
+        pnlBookmarks.setLayout(new BorderLayout());
+        pnlBookmarks.add(txpText, BorderLayout.CENTER);
+
+        JScrollPane jspBookmarks = new JScrollPane(pnlBookmarks);
+        jspBookmarks.setRowHeaderView(txpLines);
+        jspBookmarks.setVerticalScrollBarPolicy(JScrollPane.VERTICAL_SCROLLBAR_ALWAYS);
+        jspBookmarks.getVerticalScrollBar().setUnitIncrement(15);
+
+        this.setLayout(new BorderLayout());
+        this.add(jspBookmarks, BorderLayout.CENTER);
+    }
+   
+    public int getCurrentlyDisplayedTopLine() {
+        Rectangle view = txpText.getVisibleRect();
+        return getAbsoluteLine((int) Math.ceil(view.y/fm.getHeight()));
+    }
+    
+    public int getCurrentTopLine() {
+        return logLines.get(0).getLineNumber();
+    }
+    
+    public int getCurrentBottomLine() {
+        return logLines.get(logLines.size()-1).getLineNumber();
+    }
+
+    public void clearLogLines() {
+        this.logLines = new ArrayList<>();
+        txpText.setText("");
+        txpLines.setText("");
+        clearHighlighter();
+    }
+
+    public void setLogLines(List<LogLine> logLines) {
+        this.logLines = logLines;
+
+        clearHighlighter();
+        clearLineNumbers();
+        String text = "";
+        for (int i = 0; i < logLines.size(); i++) {
+            text += logLines.get(i).getText();
+            if (i < logLines.size() - 1) {
+                text += "\n";
+            }
+        }
+        txpText.setText(text);
+        setLineNumbers();
+   
+        txpText.setCaretPosition(0);
+        txpLines.setCaretPosition(0);
+
+        for (int bookmark : bookmarks.keySet()) {
+            if (bookmark < logLines.get(0).getLineNumber()) {
+                continue;
+            }
+            if (bookmark > logLines.get(logLines.size() - 1).getLineNumber()) {
+                break;
+            }
+
+            int line = getRelativeLine(bookmark);
+
+            addHighlight(logLines.get(line));
+        }
+    }
+
+    public void setLogLines(List<LogLine> logLines, int focus) {
+        setLogLines(logLines);
+        
+        //does not work for some reason - jtextpane jumps to caret position afterwards
+        //moveToBookmark(focus);
+        
+        //workaround - set caret position bevore moving
+        Rectangle visible = txpText.getVisibleRect();
+        int visibleLines = (int)(Math.ceil(visible.height/fm.getHeight()));
+
+        String text = txpText.getText();
+        int line = logLines.get(0).getLineNumber();
+        for (int i = 0; i < text.length(); i++) {
+            if (text.charAt(i) == '\n')
+                line++;
+            if (line == focus+visibleLines || i == text.length() - 1) {
+                txpText.setCaretPosition(i);
+                break;
+            }
+        }
+        moveToBookmark(focus);
+    }
+
+    public List<LogLine> getBookmarkedLines() {
+        List<LogLine> lines = new ArrayList<>();
+        for (int bookmark : bookmarks.keySet()) {
+            lines.add(bookmarks.get(bookmark).logLine);
+        }
+        return lines;
+    }
+
+    public int tryLastBookmark() {
+        int line = getCurrentlyDisplayedTopLine();
+        
+        int firstShownLine = getCurrentTopLine();
+
+        int bookmarkKey = -1;
+        for (int key : bookmarks.keySet()) {
+            if (key >= line) {
+                break;
+            }
+
+            bookmarkKey = key;
+        }
+
+        if (bookmarkKey >= firstShownLine) {
+            moveToBookmark(bookmarkKey);
+            return -1;
+        }
+
+        return bookmarkKey;
+    }
+
+    public int tryNextBookmark() {
+        Rectangle visible = txpText.getVisibleRect();
+        int lastLine = (int) Math.floor((visible.y + visible.height) / fm.getHeight());
+        lastLine = getAbsoluteLine(lastLine);
+
+        int lastShownLine = getCurrentBottomLine();
+
+        int bookmarkKey = -1;
+        for (int key : bookmarks.keySet()) {
+            if (key > lastLine) {
+                bookmarkKey = key;
+                break;
+            }
+            if (key > lastShownLine) {
+                break;
+            }
+        }
+
+        if (bookmarkKey > 0 && bookmarkKey <= lastShownLine) {
+            moveToBookmark(bookmarkKey);
+            return -1;
+        }
+
+        return bookmarkKey;
+    }
+
+    private void moveToBookmark(int line) {
+        int relLine = getRelativeLine(line);
+        Rectangle visible = txpText.getVisibleRect();
+        Rectangle position = new Rectangle(0, relLine * fm.getHeight(), visible.width, visible.height);
+        txpText.scrollRectToVisible(position);
+    }
+
+    private int getAbsoluteLine(int line) {
+        return logLines.get(0).getLineNumber() + line;
+    }
+
+    private int getRelativeLine(int line) {
+        return line - logLines.get(0).getLineNumber();
+    }
+
+    private int getStartOffset(JTextComponent component, int line) {
+        return component.getDocument().getDefaultRootElement().getElement(line).getStartOffset();
+    }
+
+    private int getEndOffset(JTextComponent component, int line) {
+        return component.getDocument().getDefaultRootElement().getElement(line).getEndOffset();
+    }
+
+    private void addHighlight(LogLine logLine) {
+        try {
+            int line = logLine.getLineNumber();
+            int relativeLine = getRelativeLine(line);
+
+            Object hl1 = textHighlighter.addHighlight(getStartOffset(txpText, relativeLine), getEndOffset(txpText, relativeLine), BOOKMARK_PAINTER);
+            Object hl2 = lineHighlighter.addHighlight(getStartOffset(txpLines, relativeLine), getEndOffset(txpLines, relativeLine), BOOKMARK_PAINTER);
+            bookmarks.put(line, new Bookmark(logLine, hl1, hl2));
+        } catch (BadLocationException ex) {
+            Exceptions.printStackTrace(ex);
+        }
+    }
+
+    private void removeHighlight(int line) {
+        int abs = getAbsoluteLine(line);
+        Bookmark bookmark = bookmarks.get(abs);
+        textHighlighter.removeHighlight(bookmark.hl1);
+        lineHighlighter.removeHighlight(bookmark.hl2);
+        bookmarks.remove(abs);
+    }
+
+    public void clearBookmarks() {
+        bookmarks = new TreeMap<>();
+        clearHighlighter();
+    }
+
+    private void clearHighlighter() {
+        textHighlighter.removeAllHighlights();
+        lineHighlighter.removeAllHighlights();
+    }
+
+    private void clearLineNumbers() {
+        txpLines.setText("");
+    }
+
+    private void setLineNumbers() {
+        clearLineNumbers();
+
+        if (logLines.isEmpty()) {
+            return;
+        }
+
+        int colCnt = String.valueOf(logLines.get(logLines.size() - 1).getLineNumber()).length() + 2;
+
+        int firstNr = logLines.get(0).getLineNumber();
+        String text = String.format("%" + colCnt + "s", firstNr + " ");
+        for (int i = 1; i < logLines.size(); i++) {
+            text += "\n";
+            text += String.format("%" + colCnt + "s", (firstNr + i) + " ");
+        }
+        txpLines.setText(text);
+    }
+
+    private MouseListener mouseInputListener = new MouseAdapter() {
+
+        @Override
+        public void mouseClicked(MouseEvent me) {
+            if (txpLines.getText().length() == 0) {
+                return;
+            }
+            if (me.getClickCount() == 2) {
+                int caretPos = txpLines.getCaretPosition();
+
+                int lineOffset = txpLines.getDocument().getDefaultRootElement().getElementIndex(caretPos);
+                if (txpLines.getText().charAt(caretPos - 1) == '\n') {
+                    lineOffset--;
+                }
+
+                if (bookmarks.containsKey(getAbsoluteLine(lineOffset))) {
+                    removeHighlight(lineOffset);
+                } else {
+                    addHighlight(logLines.get(lineOffset));
+                }
+            }
+        }
+    };
+
+    class Bookmark {
+
+        public final LogLine logLine;
+        public final Object hl1;
+        public final Object hl2;
+
+        public Bookmark(LogLine logLine, Object hl1, Object hl2) {
+            this.logLine = logLine;
+            this.hl1 = hl1;
+            this.hl2 = hl2;
+        }
+    }
+
+    class BookmarkHighlighter extends DefaultHighlighter {
+
+        private JTextComponent component;
+
+        @Override
+        public void install(JTextComponent component) {
+            super.install(component);
+            this.component = component;
+        }
+
+        @Override
+        public void deinstall(JTextComponent component) {
+            super.deinstall(component);
+            this.component = null;
+        }
+
+        @Override
+        public void paint(Graphics g) {
+            Highlighter.Highlight[] highlights = getHighlights();
+
+            for (int i = 0; i < highlights.length; i++) {
+                Highlighter.Highlight hl = highlights[i];
+                Rectangle bg = component.getBounds();
+                Insets insets = component.getInsets();
+                bg.x = insets.left;
+                bg.y = insets.top;
+                bg.height = insets.top + insets.bottom;
+                Highlighter.HighlightPainter painter = hl.getPainter();
+                painter.paint(g, hl.getStartOffset(), hl.getEndOffset(), bg, component);
+            }
+        }
+    }
+}
--- /dev/null	Thu Jan 01 00:00:00 1970 +0000
+++ b/visualizer/LogViewer/src/at/ssw/visualizer/logviewer/scene/ImportLogErrorDialog.java	Wed Jun 27 17:35:32 2012 +0200
@@ -0,0 +1,31 @@
+package at.ssw.visualizer.logviewer.scene;
+
+import at.ssw.visualizer.logviewer.model.LogParser;
+import java.awt.Component;
+import java.util.List;
+import javax.swing.JOptionPane;
+import javax.swing.JScrollPane;
+import javax.swing.JTextArea;
+
+/**
+ *
+ * @author Katrin Strassl
+ */
+public class ImportLogErrorDialog {
+    
+    public static void showDialog(Component parent, List<LogParser.ParseError> errors) {
+        JTextArea txaErrors = new JTextArea();
+        
+        for (LogParser.ParseError error : errors) {
+            txaErrors.append("Error at line " + error.getLineNumber());
+            txaErrors.append(": " + error.getMessage());
+            txaErrors.append("\n");
+            txaErrors.append("Log line: " + error.getLine());
+            txaErrors.append("\n");
+        }
+        
+        JScrollPane scpErrors = new JScrollPane(txaErrors);
+        
+        JOptionPane.showMessageDialog(parent, scpErrors, "Parse errors", JOptionPane.ERROR_MESSAGE);
+    }
+}
--- /dev/null	Thu Jan 01 00:00:00 1970 +0000
+++ b/visualizer/LogViewer/src/at/ssw/visualizer/logviewer/scene/LogScene.java	Wed Jun 27 17:35:32 2012 +0200
@@ -0,0 +1,651 @@
+package at.ssw.visualizer.logviewer.scene;
+
+import at.ssw.visualizer.logviewer.model.LogLine;
+import at.ssw.visualizer.logviewer.model.LogModel;
+import at.ssw.visualizer.logviewer.model.LogParser;
+import at.ssw.visualizer.logviewer.model.filter.Filter;
+import at.ssw.visualizer.logviewer.model.filter.FilterManager;
+import at.ssw.visualizer.logviewer.model.io.ProgressMonitor;
+import at.ssw.visualizer.logviewer.scene.model.LogTableModel;
+import at.ssw.visualizer.logviewer.scene.model.TableLine;
+import java.awt.*;
+import java.awt.event.*;
+import java.io.File;
+import java.io.IOException;
+import java.util.ArrayList;
+import java.util.List;
+import java.util.Timer;
+import java.util.TimerTask;
+import java.util.regex.PatternSyntaxException;
+import javax.swing.*;
+import javax.swing.event.ListSelectionEvent;
+import javax.swing.event.ListSelectionListener;
+import javax.swing.table.TableCellRenderer;
+import javax.swing.table.TableColumn;
+import org.openide.util.Exceptions;
+import org.openide.util.NbPreferences;
+import sun.awt.CausedFocusEvent;
+
+/**
+ *
+ * @author Katrin Strassl
+ */
+public class LogScene extends JPanel {
+    private static final String PREFERENCE_DIR = "dir";
+    
+    private static final String ICON_PREFIX = "/at/ssw/visualizer/logviewer/scene/icons/";
+    private static final String ICON_ARROW_DOWN = ICON_PREFIX + "arrow_down.png";
+    private static final String ICON_ARROW_UP = ICON_PREFIX + "arrow_up.png";
+    private static final String ICON_BOOKMARK_BACK = ICON_PREFIX + "bookmark_back.png";
+    private static final String ICON_BOOKMARK_FWD = ICON_PREFIX + "bookmark_forward.png";
+    private static final String ICON_BOOKMARK_LIST = ICON_PREFIX + "bookmark_list.png";
+    private static final String ICON_LOAD = ICON_PREFIX + "loading.gif";
+    
+    private static final ImageIcon IMGICON_LOAD = new ImageIcon(LogScene.class.getResource(ICON_LOAD));
+    
+    private static final int LOG_LINES = 50;
+    private static final int KEYSTROKE_RATE = 100; // rate at which the keystrokes are checked
+    private static final int KEYSTROKE_TRESHOLD = 400; // time in ms after which a search is triggered
+            
+    private FilterManager filterManager;
+    private Thread filterRunThread;
+    
+    private int timeSinceLastKeystroke = -1;
+    
+    private JLabel lblMessage;
+    private JLabel lblIcon;
+    
+    private JLabel lblMethodFilter;
+    private JLabel lblScopeFilter;
+    private JLabel lblNodeFilter;
+    private JLabel lblFulltextFilter;
+    private JTextField txfMethodFilter;
+    private JTextField txfScopeFilter;
+    private JTextField txfNodeFilter;
+    private JTextField txfFulltextFilter;
+    
+    private JTable tblResult;
+    private LogTableModel tblResultModel;
+    
+    private BookmarkableLogViewer logViewer;
+    private JButton btnBookmarkBack;
+    private JButton btnBookmarkFwd;
+    private JButton btnBookmarkList;
+    private JButton btnLoadMoreUp;
+    private JButton btnLoadMoreDown;
+    
+    private LogParser parser = new LogParser();
+    private LogModel logModel = null;
+    private LogStatus logStatus = LogStatus.NO_LOG;
+    
+    private static LogScene instance;
+    
+    private enum LogStatus {
+        NO_LOG, LOADING, ACTIVE
+    }
+
+    public static LogScene getInstance() {
+        return instance;
+    }
+    
+    public LogScene() {
+        initComponents();
+    }
+    
+    private void initComponents() {
+        filterManager = new FilterManager();
+        
+        lblMessage = new JLabel("No logfile loaded.");
+        lblIcon = new JLabel("");
+        
+        // Initialize filter components
+        lblMethodFilter = new JLabel("Target Method:");
+        lblScopeFilter = new JLabel("Scope:");
+        lblNodeFilter = new JLabel("Node:");
+        lblFulltextFilter = new JLabel("Fulltext:");
+        
+        txfMethodFilter = new JTextField();
+        txfScopeFilter = new JTextField();
+        txfNodeFilter = new JTextField();
+        txfFulltextFilter = new JTextField();
+        
+        tblResultModel = new LogTableModel();
+        tblResult = new JTable(tblResultModel);
+        tblResult.setSelectionMode(ListSelectionModel.SINGLE_SELECTION);
+        tblResult.getTableHeader().setReorderingAllowed(false);
+        tblResult.setAutoResizeMode(JTable.AUTO_RESIZE_ALL_COLUMNS);
+        
+        Dimension dim = tblResult.getPreferredScrollableViewportSize();
+        tblResult.setPreferredScrollableViewportSize(new Dimension(dim.width, 10*tblResult.getRowHeight() + tblResult.getTableHeader().getHeight()));
+        JScrollPane scpTblResult = new JScrollPane(tblResult);
+        scpTblResult.setMinimumSize(new Dimension(dim.width, 10*tblResult.getRowHeight() + tblResult.getTableHeader().getHeight()));
+        
+        logViewer = new BookmarkableLogViewer();
+        
+        btnLoadMoreUp = new JButton(new ImageIcon(LogScene.class.getResource(ICON_ARROW_UP)));
+        btnLoadMoreDown = new JButton(new ImageIcon(LogScene.class.getResource(ICON_ARROW_DOWN)));
+        
+        btnLoadMoreUp.setFocusable(false);
+        btnLoadMoreUp.setToolTipText("Load more previous log lines");
+        
+        btnLoadMoreDown.setFocusable(false);
+        btnLoadMoreDown.setToolTipText("Load more following log lines");
+        
+        btnBookmarkBack = new JButton(new ImageIcon(LogScene.class.getResource(ICON_BOOKMARK_BACK)));
+        btnBookmarkList = new JButton(new ImageIcon(LogScene.class.getResource(ICON_BOOKMARK_LIST)));
+        btnBookmarkFwd = new JButton(new ImageIcon(LogScene.class.getResource(ICON_BOOKMARK_FWD)));
+        
+        btnBookmarkBack.setFocusable(false);
+        btnBookmarkBack.setToolTipText("Navigate to the last bookmark.");
+        
+        btnBookmarkFwd.setFocusable(false);
+        btnBookmarkFwd.setToolTipText("Navigate to the next bookmark.");
+        
+        btnBookmarkList.setFocusable(false);
+        btnBookmarkList.setToolTipText("List all known bookmarks.");
+        
+        JPanel pnl = new JPanel();
+        pnl.setLayout(new GridBagLayout());
+        pnl.setOpaque(false);
+        
+        JPanel logViewerPanel = new JPanel();
+        logViewerPanel.setLayout(new GridBagLayout());
+        logViewerPanel.setOpaque(false);
+        
+        JPanel messagePanel = new JPanel();
+        messagePanel.setLayout(new BoxLayout(messagePanel, BoxLayout.LINE_AXIS));
+        messagePanel.setOpaque(false);
+        
+        // Initialize Listeners
+        initListeners();
+        
+        // Layout components
+        GridBagConstraints gbc = new GridBagConstraints();
+        
+        messagePanel.add(lblMessage);
+        messagePanel.add(Box.createHorizontalGlue());
+        messagePanel.add(lblIcon);
+        
+        Insets standardInsets = gbc.insets;
+        
+        gbc.gridx = 0;
+        gbc.gridy = 0;
+        gbc.anchor = GridBagConstraints.WEST;
+        gbc.fill = GridBagConstraints.HORIZONTAL;
+        gbc.weightx = 1;
+        gbc.weighty = 0;
+        gbc.gridwidth = 2;
+        gbc.insets = new Insets(2, 0, 2, 0);
+        pnl.add(messagePanel, gbc);
+        
+        gbc.fill = GridBagConstraints.NONE;
+        gbc.gridwidth = 1;
+        gbc.weightx = 0;
+        gbc.weighty = 0;
+        gbc.gridy++;
+        gbc.insets = standardInsets;
+        pnl.add(lblMethodFilter, gbc);
+        
+        gbc.gridy++;
+        pnl.add(lblScopeFilter, gbc);
+        
+        gbc.gridy++;
+        pnl.add(lblNodeFilter, gbc);
+        
+        gbc.gridy++;
+        pnl.add(lblFulltextFilter, gbc);
+        
+        gbc.gridx++;
+        gbc.gridy = 1;
+        gbc.fill = GridBagConstraints.HORIZONTAL;
+        gbc.weightx = 0.75;
+        pnl.add(txfMethodFilter, gbc);
+        
+        gbc.gridy++;
+        pnl.add(txfScopeFilter, gbc);
+        
+        gbc.gridy++;
+        pnl.add(txfNodeFilter, gbc);
+        
+        gbc.gridy++;
+        pnl.add(txfFulltextFilter, gbc);
+        
+        gbc.gridx = 0;
+        //gbc.gridy++;
+        gbc.gridwidth = 2;
+        gbc.weightx = 1;
+        gbc.anchor = GridBagConstraints.FIRST_LINE_START;
+        //pnl.add(btnSearch, gbc);
+        
+        gbc.gridy++;
+        pnl.add(scpTblResult, gbc);
+        
+        gbc.gridy++;
+        gbc.weighty = 1;
+        gbc.fill = GridBagConstraints.BOTH;
+        pnl.add(logViewerPanel, gbc);
+        
+        gbc = new GridBagConstraints();
+        gbc.gridx = 0;
+        gbc.gridy = 0;
+        gbc.gridheight = 6;
+        gbc.weightx = 1;
+        gbc.weighty = 1;
+        gbc.fill = GridBagConstraints.BOTH;
+        logViewerPanel.add(logViewer, gbc);
+        
+        gbc.gridx++;
+        gbc.weightx = 0;
+        gbc.weighty = 0;
+        gbc.fill = GridBagConstraints.NONE;
+        gbc.anchor = GridBagConstraints.PAGE_START;
+        gbc.gridheight = 1;
+        logViewerPanel.add(btnLoadMoreUp, gbc);
+        
+        gbc.gridy++;
+        logViewerPanel.add(btnLoadMoreDown, gbc);
+        
+        gbc.gridy++;
+        logViewerPanel.add(Box.createRigidArea(btnLoadMoreUp.getSize()));
+        
+        gbc.gridy++;
+        logViewerPanel.add(btnBookmarkBack, gbc);
+        
+        gbc.gridy++;
+        logViewerPanel.add(btnBookmarkFwd, gbc);
+        
+        gbc.gridy++;
+        logViewerPanel.add(btnBookmarkList, gbc);
+        
+        this.setLayout(new BorderLayout());
+        this.add(pnl, BorderLayout.CENTER);
+        
+        Timer keyTimer = new Timer();
+        keyTimer.scheduleAtFixedRate(new KeystrokeTimer(), KEYSTROKE_RATE, KEYSTROKE_RATE);
+        
+        LogScene.instance = this;
+    }
+    
+    private void initListeners() {
+        txfMethodFilter.addKeyListener(new FilterKeyListener());
+        txfScopeFilter.addKeyListener(new FilterKeyListener());
+        txfNodeFilter.addKeyListener(new FilterKeyListener());
+        txfFulltextFilter.addKeyListener(new FilterKeyListener());
+        
+        txfMethodFilter.addFocusListener(new FilterFocusListener());
+        txfScopeFilter.addFocusListener(new FilterFocusListener());
+        txfNodeFilter.addFocusListener(new FilterFocusListener());
+        txfFulltextFilter.addFocusListener(new FilterFocusListener());
+        
+        btnLoadMoreUp.addActionListener(new ActionListener() {
+            @Override
+            public void actionPerformed(ActionEvent ae) {
+                int line = logViewer.getCurrentlyDisplayedTopLine();
+                int top = logViewer.getCurrentTopLine();
+                int bottom = logViewer.getCurrentBottomLine();
+                logViewer.setLogLines(logModel.range(line, line - top + LOG_LINES, bottom - line), line); 
+            } 
+        });
+        
+        btnLoadMoreDown.addActionListener(new ActionListener() {
+            @Override
+            public void actionPerformed(ActionEvent ae) {
+                int line = logViewer.getCurrentlyDisplayedTopLine();
+                int top = logViewer.getCurrentTopLine();
+                int bottom = logViewer.getCurrentBottomLine();
+                logViewer.setLogLines(logModel.range(line, line - top, bottom - line + LOG_LINES), line); 
+            } 
+        });
+        
+        btnBookmarkBack.addActionListener(new ActionListener() {
+            @Override
+            public void actionPerformed(ActionEvent ae) {
+                int result = logViewer.tryLastBookmark();
+                if (result >= 0) {
+                    tblResult.clearSelection();
+                    logViewer.setLogLines(logModel.range(result, LOG_LINES, LOG_LINES), result); 
+                }
+            }
+        });
+        
+        btnBookmarkFwd.addActionListener(new ActionListener() {
+            @Override
+            public void actionPerformed(ActionEvent ae) {
+                int result = logViewer.tryNextBookmark();
+                if (result >= 0) {
+                    tblResult.clearSelection();
+                    logViewer.setLogLines(logModel.range(result, LOG_LINES, LOG_LINES), result);
+                }
+            }
+        });
+        
+        btnBookmarkList.addActionListener(new ActionListener() {
+            @Override
+            public void actionPerformed(ActionEvent ae) {
+                List<LogLine> bookmarkedLines = logViewer.getBookmarkedLines();
+                if (bookmarkedLines.size() > 0) {
+                    BookmarkDialog bd = new BookmarkDialog(bookmarkedLines, null);
+                    if (bd.getTarget() != null) {
+                        LogLine target = bd.getTarget();
+                        int line = target.getLineNumber();
+                        logViewer.setLogLines(logModel.range(line, LOG_LINES, LOG_LINES), line); 
+                    }
+                } else {
+                    JOptionPane.showMessageDialog(LogScene.this, "No bookmarks set.", "", JOptionPane.INFORMATION_MESSAGE);
+                }
+            }
+        });
+        
+        tblResult.getSelectionModel().addListSelectionListener(new ListSelectionListener() {
+            @Override
+            public void valueChanged(ListSelectionEvent lse) {
+                if (tblResult.getSelectedRow() == -1) {
+                    logViewer.clearLogLines();
+                    return;
+                }
+                
+                TableLine selectedLine = tblResultModel.getTableLine(tblResult.getSelectedRow());
+                int firstLine = selectedLine.getFirstLine();
+                int lastLine = selectedLine.getLastLine();
+                
+                if (firstLine == lastLine) { 
+                    // only display selected line
+                    List<LogLine> line = new ArrayList<>();
+                    line.add(selectedLine.getLogLine());
+                    logViewer.setLogLines(line);
+                } else {
+                    // display selected range
+                    int pre = firstLine==lastLine?LOG_LINES:selectedLine.getLogLine().getLineNumber()-firstLine;
+                    int post = firstLine==lastLine?LOG_LINES:lastLine-selectedLine.getLogLine().getLineNumber();
+
+                    logViewer.setLogLines(logModel.range(selectedLine.getLogLine(), pre, post));           
+                }
+            }
+        });
+    }
+    
+    private boolean executeFilters() {
+        if (logStatus == LogStatus.NO_LOG) {
+            loadLogFile();
+            return false;
+        }
+        if (logStatus == LogStatus.LOADING) {
+            return false;
+        }
+
+        boolean execute = trySetFilter(filterManager.getMethodFilter(), txfMethodFilter.getText().trim(), lblMethodFilter);
+        execute = trySetFilter(filterManager.getScopeFilter(), txfScopeFilter.getText().trim(), lblScopeFilter) || execute;
+        try {
+            // node number
+            int node = Integer.parseInt(txfNodeFilter.getText().trim());
+            execute = trySetFilter(filterManager.getNodeFilter(), node, lblNodeFilter) || execute;
+        } catch (Exception e) {
+            // node name
+            execute = trySetFilter(filterManager.getNodeFilter(), txfNodeFilter.getText().trim(), lblNodeFilter) || execute;
+        }
+        execute = trySetFilter(filterManager.getFullTextFilter(), txfFulltextFilter.getText().trim(), lblFulltextFilter) || execute;
+
+        if (!execute)
+            return true;
+        
+        tryInterruptFilter();
+
+        filterRunThread = new Thread(new Runnable() {
+
+            @Override
+            public void run() {
+                try {
+                    List<LogLine> result = filterManager.execute(logModel);
+                    lblIcon.setIcon(null);
+
+                    if (!Thread.interrupted())
+                        buildTableEntries(result);
+                } catch (InterruptedException e) {
+                }
+            }
+
+        });
+        lblIcon.setIcon(IMGICON_LOAD);
+        filterRunThread.start();
+        
+        return true;
+    }
+    
+    private boolean trySetFilter(Filter filter, Object constraint, JLabel marker) {
+        try {
+            filter.setConstraints(constraint);
+            marker.setForeground(Color.BLACK);
+            return constraint != null && (!(constraint instanceof String) || (constraint instanceof String && ((String)constraint).trim().length() > 0));
+        } catch (PatternSyntaxException e) {
+            filter.setConstraints(new Object[]{});
+            marker.setForeground(Color.RED);
+            return false;
+        }
+    }
+    
+    private void tryInterruptFilter() {
+        if (filterRunThread != null && (filterRunThread.isAlive() || !filterRunThread.isInterrupted())) {
+            lblIcon.setIcon(null);
+            filterRunThread.interrupt();
+        }
+    }
+    
+    private void buildTableEntries(List<LogLine> filterResult) {
+        List<TableLine> tableEntries;
+        
+        int methodLength = txfMethodFilter.getText().trim().length();
+        int scopeLength = txfScopeFilter.getText().trim().length();
+        int nodeLength = txfNodeFilter.getText().trim().length();
+        int fulltextLength = txfFulltextFilter.getText().trim().length();
+        
+        boolean preventGrouping = nodeLength != 0 || fulltextLength != 0;
+        
+        if (!preventGrouping && methodLength > 0 && scopeLength == 0) {
+            tableEntries = groupByMethod(filterResult);
+        } else if (!preventGrouping && scopeLength > 0) {
+            tableEntries = groupByScope(filterResult);
+        } else {
+            tableEntries = showAllLines(filterResult);
+        }
+        
+        tblResultModel.setLogEntries(tableEntries);
+        
+        int width = 0;
+        for (int i = 0; i < tableEntries.size(); i++) {
+            TableCellRenderer tcr = tblResult.getCellRenderer(i, 0);
+            Component c = tcr.getTableCellRendererComponent(tblResult, tblResultModel.getValueAt(i, 0), false, false, i, 0);
+            if (c instanceof JLabel) {
+                JLabel l = (JLabel) c;
+                FontMetrics fm = l.getFontMetrics(l.getFont());
+                width = Math.max(width, fm.stringWidth(l.getText()));
+            }
+        }
+       
+        // proper resizing of column
+        // setWidth and setPreferredWidth do not trigger resize
+        TableColumn col = tblResult.getColumnModel().getColumn(0);
+        col.setMinWidth(width+5);
+        col.setMaxWidth(width+5);
+        
+        // reenable resizing for user
+        col.setMinWidth(0);
+        col.setMaxWidth(999);
+    }
+    
+    private List<TableLine> groupByMethod(List<LogLine> filterResult) {
+        List<TableLine> tableEntries = new ArrayList<>();
+        int firstLine = -1;
+        for (int i = 0; i < filterResult.size(); i++) {
+            LogLine line = filterResult.get(i);
+            
+            if (firstLine < 0)
+                firstLine = line.getLineNumber();
+
+            if (i < filterResult.size() - 1) {
+                LogLine next = filterResult.get(i+1);
+                if (line.getMethod() != next.getMethod()) {
+                    tableEntries.add(new TableLine(line, firstLine));
+                    firstLine = next.getLineNumber();
+                }
+            } else {
+                tableEntries.add(new TableLine(line, firstLine));
+            }
+        }
+        return tableEntries;
+    }
+    
+    private List<TableLine> groupByScope(List<LogLine> filterResult) {
+        List<TableLine> tableEntries = new ArrayList<>();
+        LogLine firstScopeLine = null;
+        for (int i = 0; i < filterResult.size(); i++) {
+            LogLine line = filterResult.get(i);
+            
+            if (firstScopeLine == null)
+                firstScopeLine = line;
+
+            if (i < filterResult.size() - 1) {
+                LogLine next = filterResult.get(i+1);
+                if (line.getScope() != next.getScope()) {
+                    tableEntries.add(new TableLine(firstScopeLine, line.getLineNumber()));
+                    firstScopeLine = next;
+                }
+            } else {
+                tableEntries.add(new TableLine(firstScopeLine, line.getLineNumber()));
+            }
+        }
+        return tableEntries;
+    }
+    
+    private List<TableLine> showAllLines(List<LogLine> filterResult) {
+        List<TableLine> tableEntries = new ArrayList<>();
+        for (LogLine line : filterResult) {
+            tableEntries.add(new TableLine(line));
+        }
+        return tableEntries;
+    }
+    
+    public void loadLogFile() {
+        JFileChooser fc = new JFileChooser();
+        fc.setFileFilter(new javax.swing.filechooser.FileFilter() {
+
+            @Override
+            public boolean accept(File f) {
+                return f.isDirectory() ||
+                       f.getName().toLowerCase().endsWith(".txt") ||
+                       f.getName().toLowerCase().endsWith(".log");
+            }
+
+            @Override
+            public String getDescription() {
+                return "Log files (*.txt, *.log)";
+            }
+        });
+        fc.setCurrentDirectory(new File(NbPreferences.forModule(LogScene.class).get(PREFERENCE_DIR, "~")));
+        fc.setDialogTitle("Load log file");
+
+        if (fc.showOpenDialog(this) == JFileChooser.APPROVE_OPTION) {
+            tryInterruptFilter();
+            
+            final File file = fc.getSelectedFile();
+            
+            NbPreferences.forModule(LogScene.class).put(PREFERENCE_DIR, file.getParent());
+            
+            lblIcon.setIcon(IMGICON_LOAD);
+            logStatus = LogStatus.LOADING;
+            new Thread(new Runnable(){
+
+                @Override
+                public void run() {
+                    try {
+                        logModel = parser.parse(file, new LoadProgressMonitor(lblMessage, "Loading file " + file.getName() + "..."));
+                        lblIcon.setIcon(null);
+                        lblMessage.setText("Current logfile: " + file.getName());
+                        logStatus = LogStatus.ACTIVE;
+                    } catch (IOException ex) {
+                        Exceptions.printStackTrace(ex);
+                    }
+                }
+
+            }).start();
+        }
+    }
+    
+    private class FilterKeyListener extends KeyAdapter {
+        @Override
+        public void keyTyped(KeyEvent e) {
+            tryInterruptFilter();
+            timeSinceLastKeystroke = 0;
+        }
+    }
+    
+    private class FilterFocusListener implements FocusListener {
+        private boolean ignore = false;
+        
+        @Override
+        public void focusGained(FocusEvent fe) {
+            if (!ignore && logStatus == LogStatus.NO_LOG) {
+                ignore = true;
+                loadLogFile();
+            }
+        }
+
+        @Override
+        public void focusLost(FocusEvent fe) {
+            if (fe instanceof CausedFocusEvent) {
+                CausedFocusEvent cfe = (CausedFocusEvent) fe;
+                ignore = ignore && // don't change ignore to true if it is already false
+                         cfe.getCause() == CausedFocusEvent.Cause.ACTIVATION; // ACTIVATION is triggered if the
+                                                                              // FileChooser is opened
+            }
+        }
+        
+    }
+    
+    private class KeystrokeTimer extends TimerTask {
+        @Override
+        public void run() {
+            if (timeSinceLastKeystroke < 0)
+                return;
+            
+            timeSinceLastKeystroke += KEYSTROKE_RATE;
+            
+            if (timeSinceLastKeystroke >= KEYSTROKE_TRESHOLD) {
+                int save = timeSinceLastKeystroke;
+                timeSinceLastKeystroke = -1; // needs to be set to -1 to prevent 
+                                             // multiple parallel filter execution
+                
+                if (!executeFilters())
+                    timeSinceLastKeystroke = save;
+            }
+        }
+    }
+    
+    private class LoadProgressMonitor implements ProgressMonitor {
+        private String staticText;
+        private JLabel lblStatus;
+        
+        public LoadProgressMonitor(JLabel lblStatus, String staticText) {
+            this.lblStatus = lblStatus;
+            this.staticText = staticText;
+            
+            lblStatus.setText(staticText);
+        }
+        
+        @Override
+        public void worked(float percentage) {
+            int perc = Math.round(percentage*100);
+            lblStatus.setText(staticText + " (" + perc + " %)");
+        }
+
+        @Override
+        public void finished() {
+            if (parser.hasErrors()) {
+                if (JOptionPane.showConfirmDialog(LogScene.this, "Parsing log file finished with errors. Show error messages?", "Warning", JOptionPane.YES_NO_OPTION, JOptionPane.WARNING_MESSAGE) == JOptionPane.YES_OPTION) {
+                    List<LogParser.ParseError> errors = parser.getErrors();
+                    ImportLogErrorDialog.showDialog(LogScene.this, errors);
+                }
+            }
+        }
+        
+    };
+}
--- /dev/null	Thu Jan 01 00:00:00 1970 +0000
+++ b/visualizer/LogViewer/src/at/ssw/visualizer/logviewer/scene/actions/ImportLogAction.java	Wed Jun 27 17:35:32 2012 +0200
@@ -0,0 +1,25 @@
+package at.ssw.visualizer.logviewer.scene.actions;
+
+import at.ssw.visualizer.logviewer.scene.LogScene;
+import java.awt.event.ActionEvent;
+import java.awt.event.ActionListener;
+import org.openide.awt.ActionID;
+import org.openide.awt.ActionReference;
+import org.openide.awt.ActionRegistration;
+
+
+/**
+ *
+ * @author Katrin Strassl
+ */
+
+@ActionID(id = "at.ssw.visualizer.logviewer.scene.actions.ImportLogActions", category = "File")
+@ActionRegistration(displayName = "Import Log", iconBase = "at/ssw/visualizer/logviewer/scene/icons/import_log.png")
+@ActionReference(path = "Menu/File", position = 600)
+public class ImportLogAction implements ActionListener {
+
+    @Override
+    public void actionPerformed(ActionEvent ae) {
+        LogScene.getInstance().loadLogFile();
+    }
+}
Binary file visualizer/LogViewer/src/at/ssw/visualizer/logviewer/scene/icons/arrow_down.png has changed
Binary file visualizer/LogViewer/src/at/ssw/visualizer/logviewer/scene/icons/arrow_up.png has changed
Binary file visualizer/LogViewer/src/at/ssw/visualizer/logviewer/scene/icons/bookmark_back.png has changed
Binary file visualizer/LogViewer/src/at/ssw/visualizer/logviewer/scene/icons/bookmark_forward.png has changed
Binary file visualizer/LogViewer/src/at/ssw/visualizer/logviewer/scene/icons/bookmark_list.png has changed
Binary file visualizer/LogViewer/src/at/ssw/visualizer/logviewer/scene/icons/import_log.png has changed
Binary file visualizer/LogViewer/src/at/ssw/visualizer/logviewer/scene/icons/loading.gif has changed
--- /dev/null	Thu Jan 01 00:00:00 1970 +0000
+++ b/visualizer/LogViewer/src/at/ssw/visualizer/logviewer/scene/model/LogTableModel.java	Wed Jun 27 17:35:32 2012 +0200
@@ -0,0 +1,59 @@
+package at.ssw.visualizer.logviewer.scene.model;
+
+import java.util.ArrayList;
+import java.util.List;
+import javax.swing.table.AbstractTableModel;
+
+/**
+ *
+ * @author Katrin Strassl
+ */
+public class LogTableModel extends AbstractTableModel {
+    private final static String[] columnNames = {"Line #", "Method", "Scope", "Node", "Log Text"};
+    
+    private List<TableLine> entries = new ArrayList<>();
+    
+    public void setLogEntries(List<TableLine> entries) {
+        this.entries = entries;
+        fireTableDataChanged();
+    }
+    
+    public TableLine getTableLine(int line) {
+        return entries.get(line);
+    }
+    
+    @Override
+    public int getRowCount() {
+        return entries.size();
+    }
+
+    @Override
+    public int getColumnCount() {
+        return columnNames.length;
+    }
+
+    @Override
+    public String getColumnName(int column) {
+        return columnNames[column];
+    }
+
+    @Override
+    public Object getValueAt(int i, int i1) {
+        switch (i1) {
+            case 0:
+                return entries.get(i).getLineNr();
+            case 1:
+                return entries.get(i).getLogLine().getMethod().getName();
+            case 2:
+                return entries.get(i).getLogLine().getScope()!=null?
+                           entries.get(i).getLogLine().getScope().getName():"";
+            case 3:
+                return entries.get(i).getLogLine().getNode()!=null?
+                           entries.get(i).getLogLine().getNode().getName():"";
+            case 4:
+                return entries.get(i).getLogLine().getText();
+        }
+        return null;
+    }
+    
+}
--- /dev/null	Thu Jan 01 00:00:00 1970 +0000
+++ b/visualizer/LogViewer/src/at/ssw/visualizer/logviewer/scene/model/TableLine.java	Wed Jun 27 17:35:32 2012 +0200
@@ -0,0 +1,43 @@
+package at.ssw.visualizer.logviewer.scene.model;
+
+import at.ssw.visualizer.logviewer.model.LogLine;
+
+/**
+ *
+ * @author Katrin Strassl
+ */
+public class TableLine {
+
+    private int lineNr;
+    private LogLine logLine;
+    
+    // display single log line
+    public TableLine(LogLine logLine) {
+        this(logLine, logLine.getLineNumber());
+    }
+    
+    // display line number range (from logLine to lineNr or vice versa)
+    public TableLine(LogLine logLine, int lineNr) {
+        this.logLine = logLine;
+        this.lineNr = lineNr;
+    }
+    
+    public int getFirstLine() {
+        return lineNr < logLine.getLineNumber()?lineNr:logLine.getLineNumber();
+    }
+    
+    public int getLastLine() {
+        return lineNr > logLine.getLineNumber()?lineNr:logLine.getLineNumber();
+    }
+    
+    public String getLineNr() {
+        if (lineNr == logLine.getLineNumber()) {
+            return String.valueOf(lineNr);
+        }
+        return getFirstLine() + "-" + getLastLine();
+    }
+    
+    public LogLine getLogLine() {
+        return logLine;
+    }
+}
--- a/visualizer/nbproject/project.properties	Wed Jun 27 15:40:03 2012 +0200
+++ b/visualizer/nbproject/project.properties	Wed Jun 27 17:35:32 2012 +0200
@@ -1,48 +1,50 @@
-app.icon=branding/core/core.jar/org/netbeans/core/startup/frame48.gif
-app.name=graalvisualizer
-app.title=Graal Visualizer
-branding.token=${app.name}
-modules=\
-    ${project.com.sun.hotspot.igv.graph}:\
-    ${project.com.sun.hotspot.igv.filter}:\
-    ${project.com.sun.hotspot.igv.hierarchicallayout}:\
-    ${project.com.sun.hotspot.igv.layout}:\
-    ${project.com.sun.hotspot.igv.data}:\
-    ${project.com.sun.hotspot.igv.view}:\
-    ${project.com.sun.hotspot.igv.bytecodes}:\
-    ${project.com.sun.hotspot.igv.difference}:\
-    ${project.com.sun.hotspot.igv.settings}:\
-    ${project.com.sun.hotspot.igv.util}:\
-    ${project.com.sun.hotspot.igv.svg}:\
-    ${project.com.sun.hotspot.igv.filterwindow}:\
-    ${project.com.sun.hotspot.igv.graal}:\
-    ${project.at.ssw.visualizer.cfg}:\
-    ${project.org.eclipse.draw2d}:\
-    ${project.com.oracle.graal.visualizer.editor}:\
-    ${project.com.oracle.graal.visualizer.outline}:\
-    ${project.com.oracle.graal.visualizer.snapshots}:\
-    ${project.com.oracle.graal.visualizer.sharedactions}
-project.at.ssw.visualizer.cfg=ControlFlowEditor
-project.com.oracle.graal.visualizer.editor=Editor
-project.com.oracle.graal.visualizer.outline=OutlineView
-project.com.oracle.graal.visualizer.sharedactions=SharedActions
-project.com.oracle.graal.visualizer.snapshots=SnapshotsView
-project.com.sun.hotspot.igv.bytecodes=Bytecodes
-project.com.sun.hotspot.igv.data=Data
-project.com.sun.hotspot.igv.difference=Difference
-project.com.sun.hotspot.igv.filter=Filter
-project.com.sun.hotspot.igv.filterwindow=FilterWindow
-project.com.sun.hotspot.igv.graal=Graal
-project.com.sun.hotspot.igv.graph=Graph
-project.com.sun.hotspot.igv.hierarchicallayout=HierarchicalLayout
-project.com.sun.hotspot.igv.layout=Layout
-project.com.sun.hotspot.igv.settings=Settings
-project.com.sun.hotspot.igv.svg=BatikSVGProxy
-project.com.sun.hotspot.igv.view=View
-project.com.sun.hotspot.igv.util=Util
-
-project.org.eclipse.draw2d=Draw2DLibrary
-# Disable assertions for RequestProcessor to prevent annoying messages in case
-# of multiple SceneAnimator update tasks in the default RequestProcessor.
-run.args.extra = -J-client -J-da:org.openide.util.RequestProcessor
-debug.args.extra = -J-client -J-da:org.openide.util.RequestProcessor
+app.icon=branding/core/core.jar/org/netbeans/core/startup/frame48.gif
+app.name=graalvisualizer
+app.title=Graal Visualizer
+branding.token=${app.name}
+modules=\
+    ${project.com.sun.hotspot.igv.graph}:\
+    ${project.com.sun.hotspot.igv.filter}:\
+    ${project.com.sun.hotspot.igv.hierarchicallayout}:\
+    ${project.com.sun.hotspot.igv.layout}:\
+    ${project.com.sun.hotspot.igv.data}:\
+    ${project.com.sun.hotspot.igv.view}:\
+    ${project.com.sun.hotspot.igv.bytecodes}:\
+    ${project.com.sun.hotspot.igv.difference}:\
+    ${project.com.sun.hotspot.igv.settings}:\
+    ${project.com.sun.hotspot.igv.util}:\
+    ${project.com.sun.hotspot.igv.svg}:\
+    ${project.com.sun.hotspot.igv.filterwindow}:\
+    ${project.com.sun.hotspot.igv.graal}:\
+    ${project.at.ssw.visualizer.cfg}:\
+    ${project.org.eclipse.draw2d}:\
+    ${project.com.oracle.graal.visualizer.editor}:\
+    ${project.com.oracle.graal.visualizer.outline}:\
+    ${project.com.oracle.graal.visualizer.snapshots}:\
+    ${project.com.oracle.graal.visualizer.sharedactions}:\
+    ${project.at.ssw.visualizer.logviewer}
+project.at.ssw.visualizer.cfg=ControlFlowEditor
+project.at.ssw.visualizer.logviewer=LogViewer
+project.com.oracle.graal.visualizer.editor=Editor
+project.com.oracle.graal.visualizer.outline=OutlineView
+project.com.oracle.graal.visualizer.sharedactions=SharedActions
+project.com.oracle.graal.visualizer.snapshots=SnapshotsView
+project.com.sun.hotspot.igv.bytecodes=Bytecodes
+project.com.sun.hotspot.igv.data=Data
+project.com.sun.hotspot.igv.difference=Difference
+project.com.sun.hotspot.igv.filter=Filter
+project.com.sun.hotspot.igv.filterwindow=FilterWindow
+project.com.sun.hotspot.igv.graal=Graal
+project.com.sun.hotspot.igv.graph=Graph
+project.com.sun.hotspot.igv.hierarchicallayout=HierarchicalLayout
+project.com.sun.hotspot.igv.layout=Layout
+project.com.sun.hotspot.igv.settings=Settings
+project.com.sun.hotspot.igv.svg=BatikSVGProxy
+project.com.sun.hotspot.igv.view=View
+project.com.sun.hotspot.igv.util=Util
+
+project.org.eclipse.draw2d=Draw2DLibrary
+# Disable assertions for RequestProcessor to prevent annoying messages in case
+# of multiple SceneAnimator update tasks in the default RequestProcessor.
+run.args.extra = -J-client -J-da:org.openide.util.RequestProcessor
+debug.args.extra = -J-client -J-da:org.openide.util.RequestProcessor