changeset 5734:6a812002a918

Initial commit: LogViewer backend
author Alexander Stipsits <stipsits_alexander@gmx.at>
date Fri, 22 Jun 2012 23:13:34 +0200
parents 2558ff0945f8
children 119c77c83ede
files visualizer/LogViewer/build.xml visualizer/LogViewer/manifest.mf visualizer/LogViewer/nbproject/build-impl.xml visualizer/LogViewer/nbproject/genfiles.properties visualizer/LogViewer/nbproject/project.properties visualizer/LogViewer/nbproject/project.xml visualizer/LogViewer/src/at/ssw/visualizer/logviewer/model/LogLine.java visualizer/LogViewer/src/at/ssw/visualizer/logviewer/model/LogModel.java visualizer/LogViewer/src/at/ssw/visualizer/logviewer/model/LogParser.java visualizer/LogViewer/src/at/ssw/visualizer/logviewer/model/Method.java visualizer/LogViewer/src/at/ssw/visualizer/logviewer/model/Node.java visualizer/LogViewer/src/at/ssw/visualizer/logviewer/model/Scope.java visualizer/LogViewer/src/at/ssw/visualizer/logviewer/model/filter/Filter.java visualizer/LogViewer/src/at/ssw/visualizer/logviewer/model/filter/FilterManager.java visualizer/LogViewer/src/at/ssw/visualizer/logviewer/model/filter/FullTextFilter.java visualizer/LogViewer/src/at/ssw/visualizer/logviewer/model/filter/MethodFilter.java visualizer/LogViewer/src/at/ssw/visualizer/logviewer/model/filter/NodeFilter.java visualizer/LogViewer/src/at/ssw/visualizer/logviewer/model/filter/ScopeFilter.java visualizer/LogViewer/src/at/ssw/visualizer/logviewer/model/io/FileLine.java visualizer/LogViewer/src/at/ssw/visualizer/logviewer/model/io/ProgressMonitor.java visualizer/LogViewer/src/at/ssw/visualizer/logviewer/model/io/SeekableFile.java visualizer/LogViewer/src/at/ssw/visualizer/logviewer/model/io/SeekableFileReader.java
diffstat 22 files changed, 1438 insertions(+), 0 deletions(-) [+]
line wrap: on
line diff
--- /dev/null	Thu Jan 01 00:00:00 1970 +0000
+++ b/visualizer/LogViewer/build.xml	Fri Jun 22 23:13:34 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	Fri Jun 22 23:13:34 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	Fri Jun 22 23:13:34 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	Fri Jun 22 23:13:34 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	Fri Jun 22 23:13:34 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	Fri Jun 22 23:13:34 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/src/at/ssw/visualizer/logviewer/model/LogLine.java	Fri Jun 22 23:13:34 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	Fri Jun 22 23:13:34 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	Fri Jun 22 23:13:34 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	Fri Jun 22 23:13:34 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	Fri Jun 22 23:13:34 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	Fri Jun 22 23:13:34 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	Fri Jun 22 23:13:34 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	Fri Jun 22 23:13:34 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	Fri Jun 22 23:13:34 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	Fri Jun 22 23:13:34 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	Fri Jun 22 23:13:34 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	Fri Jun 22 23:13:34 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	Fri Jun 22 23:13:34 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	Fri Jun 22 23:13:34 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	Fri Jun 22 23:13:34 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	Fri Jun 22 23:13:34 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;
+	}
+	
+	
+}