Index: java/main/src/main/java/com/framsticks/communication/ClientSideManagedConnection.java
===================================================================
--- java/main/src/main/java/com/framsticks/communication/ClientSideManagedConnection.java	(revision 100)
+++ java/main/src/main/java/com/framsticks/communication/ClientSideManagedConnection.java	(revision 101)
@@ -3,4 +3,6 @@
 import com.framsticks.communication.queries.ApplicationRequest;
 import com.framsticks.communication.queries.CallRequest;
+import com.framsticks.communication.queries.NeedFile;
+import com.framsticks.communication.queries.NeedFileAcceptor;
 import com.framsticks.communication.queries.ProtocolRequest;
 import com.framsticks.communication.queries.RegisterRequest;
@@ -43,4 +45,19 @@
 	private boolean isHandshakeDone = false;
 
+	protected NeedFileAcceptor needFileAcceptor;
+
+	/**
+	 * @return the needFileAcceptor
+	 */
+	public NeedFileAcceptor getNeedFileAcceptor() {
+		return needFileAcceptor;
+	}
+
+	/**
+	 * @param needFileAcceptor the needFileAcceptor to set
+	 */
+	public void setNeedFileAcceptor(NeedFileAcceptor needFileAcceptor) {
+		this.needFileAcceptor = needFileAcceptor;
+	}
 
 	/**
@@ -64,6 +81,4 @@
 		protocolVersion = -1;
 	}
-
-
 
 	protected List<String> readFileContent() {
@@ -116,8 +131,4 @@
 	}
 
-	private Map<Integer, SentQuery<?>> queryMap = new HashMap<>();
-
-	private SentQuery<?> currentlySentQuery;
-
 	public void send(ProtocolRequest request, ClientSideResponseFuture callback) {
 		//TODO RunAt
@@ -154,25 +165,10 @@
 		sentQuery.dispatcher = dispatcher;
 
+
 		senderThread.dispatch(new RunAt<Connection>(callback) {
 			@Override
 			protected void runAt() {
-				Integer id;
-				synchronized (ClientSideManagedConnection.this) {
-
-					while (!(requestIdEnabled || currentlySentQuery == null)) {
-						try {
-							ClientSideManagedConnection.this.wait();
-						} catch (InterruptedException ignored) {
-							break;
-						}
-					}
-					if (requestIdEnabled) {
-						queryMap.put(nextQueryId, sentQuery);
-						id = nextQueryId++;
-					} else {
-						currentlySentQuery = sentQuery;
-						id = null;
-					}
-				}
+				Integer id = sentQueries.put(null, sentQuery);
+
 				String command = sentQuery.request.getCommand();
 				StringBuilder message = new StringBuilder();
@@ -188,14 +184,6 @@
 				flushOut();
 				log.debug("sending query: {}", out);
-
 			}
 		});
-		/*
-		synchronized (this) {
-			log.debug("queueing query: {}", query);
-			queryQueue.offer(sentQuery);
-			notifyAll();
-		}
-		 */
 	}
 
@@ -204,5 +192,4 @@
 		return "client connection " + address;
 	}
-
 
 	private void sendQueryVersion(final int version, final Future<Void> future) {
@@ -229,32 +216,70 @@
 	}
 
-	private synchronized @Nonnull SentQuery<?> fetchQuery(@Nullable Integer id, boolean remove) {
-		try {
-			if (id == null) {
-				if (requestIdEnabled) {
-					throw new FramsticksException().msg("request_id is enabled and id is missing");
-				}
-				SentQuery<?> result = currentlySentQuery;
-				if (remove) {
-					currentlySentQuery = null;
-					notifyAll();
-				}
-				return result;
-			}
-
-			if (!queryMap.containsKey(id)) {
-				throw new FramsticksException().msg("id is unknown").arg("id", id);
-			}
-
-			SentQuery<?> result = queryMap.get(id);
-			if (remove) {
-				queryMap.remove(id);
-			}
-			return result;
-
-		} catch (FramsticksException e) {
-			throw new FramsticksException().msg("failed to match response to sent query").cause(e);
-		}
-	}
+	protected class IdCollection<T> {
+
+
+		protected final Map<Integer, T> map = new HashMap<>();
+		protected T current;
+
+		public Integer put(Integer idProposition, T value) {
+			synchronized (ClientSideManagedConnection.this) {
+				while (!(requestIdEnabled || current == null)) {
+					try {
+						ClientSideManagedConnection.this.wait();
+					} catch (InterruptedException ignored) {
+						break;
+					}
+				}
+				if (!requestIdEnabled) {
+					current = value;
+					return null;
+				}
+				if (idProposition == null) {
+					idProposition = nextQueryId++;
+				}
+				map.put(idProposition, value);
+				return idProposition;
+			}
+		}
+
+		public void clear(Integer id) {
+			if (requestIdEnabled) {
+				current = null;
+			} else {
+				map.remove(id);
+			}
+		}
+
+		public @Nonnull T fetch(@Nullable Integer id, boolean remove) {
+			synchronized (ClientSideManagedConnection.this) {
+				try {
+					if (id == null) {
+						if (requestIdEnabled) {
+							throw new FramsticksException().msg("request_id is enabled and id is missing");
+						}
+						T result = current;
+						current = null;
+						ClientSideManagedConnection.this.notifyAll();
+						return result;
+					}
+					if (!map.containsKey(id)) {
+						throw new FramsticksException().msg("id is unknown").arg("id", id);
+					}
+
+					T result = map.get(id);
+					if (remove) {
+						map.remove(id);
+					}
+					return result;
+
+				} catch (FramsticksException e) {
+					throw new FramsticksException().msg("failed to match response to sent query").cause(e);
+				}
+			}
+		}
+	}
+
+	protected IdCollection<SentQuery<?>> sentQueries = new IdCollection<>();
+	protected IdCollection<NeedFile> needFiles = new IdCollection<>();
 
 	private int nextQueryId = 0;
@@ -269,6 +294,6 @@
 			throw new FramsticksException().msg("expected file line").arg("got", fileLine);
 		}
-		String eventObjectPath = Request.takeGroup(rest, matcher, 1).toString();
-		String eventCalleePath = Request.takeGroup(rest, matcher, 2).toString();
+		String eventObjectPath = Strings.takeGroup(rest, matcher, 1).toString();
+		String eventCalleePath = Strings.takeGroup(rest, matcher, 2).toString();
 		final File file = new File("", new ListSource(readFileContent()));
 		log.debug("firing event {}", eventObjectPath);
@@ -277,5 +302,5 @@
 			listener = registeredListeners.get(eventObjectPath);
 		}
-		if (listener  == null) {
+		if (listener == null) {
 			throw new FramsticksException().msg("failed to find registered event").arg("event path", eventObjectPath).arg("object", eventCalleePath);
 		}
@@ -283,6 +308,48 @@
 	}
 
+	protected void processNeedFile(Pair<Integer, CharSequence> rest) {
+		final Integer id = rest.first;
+		String suggestedName = null;
+		String description = null;
+		Pair<CharSequence, CharSequence> s = Request.takeString(rest.second);
+		if (s != null) {
+			suggestedName = s.first.toString();
+			Pair<CharSequence, CharSequence> d = Request.takeString(s.second);
+			if (d != null) {
+				description = d.first.toString();
+			}
+		}
+
+		final Future<File> future = new Future<File>() {
+
+			protected void send(final File result) {
+				log.info("sending file: " + result);
+				needFiles.clear(id);
+				sendFile(null, result, id, ClientSideManagedConnection.this);
+
+			}
+
+			@Override
+			protected void result(File result) {
+				send(result);
+			}
+
+			@Override
+			public void handle(FramsticksException exception) {
+				send(new File("", ListSource.createFrom("# invalid", "# " + exception.getMessage())));
+			}
+		};
+
+		NeedFile needFile = new NeedFile(suggestedName, description, future);
+
+		if (needFileAcceptor.acceptNeed(needFile)) {
+			return;
+		}
+
+		future.handle(new FramsticksException().msg("acceptor did not accepted need"));
+	}
+
 	protected void processFile(Pair<Integer, CharSequence> rest) {
-		final SentQuery<?> sentQuery = fetchQuery(rest.first, false);
+		final SentQuery<?> sentQuery = sentQueries.fetch(rest.first, false);
 
 		String currentFilePath = rest.second.toString();
@@ -292,5 +359,4 @@
 
 		sentQuery.files.add(new File(currentFilePath, new ListSource(readFileContent())));
-
 	}
 
@@ -318,9 +384,13 @@
 			if (keyword.equals("ok") || keyword.equals("error")) {
 
-				final SentQuery<?> sentQuery = fetchQuery(rest.first, true);
+				final SentQuery<?> sentQuery = sentQueries.fetch(rest.first, true);
 
 				log.debug("parsing response for request {}", sentQuery);
 
 				sentQuery.dispatchResponseProcess(new Response(command.first.equals("ok"), rest.second.toString(), sentQuery.getFiles()));
+				return;
+			}
+			if (keyword.equals("needfile")) {
+				processNeedFile(rest);
 				return;
 			}
@@ -336,5 +406,5 @@
 		@Override
 		public void handle(FramsticksException exception) {
-			interrupt();
+			interruptJoinable();
 			// finish();
 		}
Index: java/main/src/main/java/com/framsticks/communication/Connection.java
===================================================================
--- java/main/src/main/java/com/framsticks/communication/Connection.java	(revision 100)
+++ java/main/src/main/java/com/framsticks/communication/Connection.java	(revision 101)
@@ -1,4 +1,5 @@
 package com.framsticks.communication;
 
+import com.framsticks.params.Source;
 import com.framsticks.params.annotations.AutoAppendAnnotation;
 import com.framsticks.params.annotations.FramsClassAnnotation;
@@ -60,5 +61,4 @@
 		threads.add(senderThread);
 		threads.add(receiverThread);
-
 	}
 
@@ -242,6 +242,6 @@
 			protected void runAt() {
 				receiverThreadRoutine();
-				interrupt();
-				finish();
+				interruptJoinable();
+				finishJoinable();
 			}
 		});
@@ -251,5 +251,5 @@
 	protected void joinableInterrupt() {
 		Dispatching.drop(threads, this);
-		finish();
+		finishJoinable();
 	}
 
@@ -331,3 +331,30 @@
 
 
+	protected static String idToString(Integer id) {
+		return id != null ? " " + id.toString() : "";
+	}
+
+	protected final void putFile(File file, Integer outId) {
+		putLine("file" + idToString(outId)/* + " " + f.getPath()*/);
+		Source content = file.getContent();
+		String line;
+		while ((line = content.readLine()) != null) {
+			putLine(line);
+		}
+		putLine("eof");
+	}
+
+	public final void sendFile(final String header, final File file, final Integer id, ExceptionResultHandler handler) {
+		senderThread.dispatch(new RunAt<Connection>(handler) {
+			@Override
+			protected void runAt() {
+				if (header != null) {
+					putLine(header);
+				}
+				putFile(file, id);
+				flushOut();
+			}
+		});
+	}
+
 }
Index: java/main/src/main/java/com/framsticks/communication/File.java
===================================================================
--- java/main/src/main/java/com/framsticks/communication/File.java	(revision 100)
+++ java/main/src/main/java/com/framsticks/communication/File.java	(revision 101)
@@ -6,6 +6,7 @@
 import javax.annotation.Nonnull;
 
-import com.framsticks.params.SourceInterface;
+import com.framsticks.params.Source;
 // import com.framsticks.util.lang.Strings;
+import com.framsticks.util.lang.Strings;
 
 /**
@@ -14,7 +15,7 @@
 public final class File {
 	protected final String path;
-	protected final SourceInterface content;
+	protected final Source content;
 
-	public File(@Nonnull String path, @Nonnull SourceInterface content) {
+	public File(@Nonnull String path, @Nonnull Source content) {
 		// assert Strings.notEmpty(path);
 		this.path = path;
@@ -26,5 +27,5 @@
 	}
 
-	public SourceInterface getContent() {
+	public Source getContent() {
 		return content;
 	}
@@ -35,3 +36,14 @@
 		return result;
 	}
+
+	@Override
+	public String toString() {
+		StringBuilder b = new StringBuilder();
+		if (Strings.notEmpty(path)) {
+			b.append(path).append(": ");
+		}
+		b.append(content.toString());
+		return b.toString();
+	}
+
 }
Index: java/main/src/main/java/com/framsticks/communication/Request.java
===================================================================
--- java/main/src/main/java/com/framsticks/communication/Request.java	(revision 100)
+++ java/main/src/main/java/com/framsticks/communication/Request.java	(revision 101)
@@ -7,4 +7,5 @@
 import com.framsticks.util.FramsticksException;
 import com.framsticks.util.lang.Pair;
+import com.framsticks.util.lang.Strings;
 
 /**
@@ -78,8 +79,4 @@
 	}
 
-	public static CharSequence takeGroup(CharSequence input, Matcher matcher, int group) {
-		// return (matcher.start(group) == matcher.end(group)) ? null : input.subSequence(matcher.start(group), matcher.end(group));
-		return input.subSequence(matcher.start(group), matcher.end(group));
-	}
 
 	public static Pair<CharSequence, CharSequence> takeString(CharSequence line) {
@@ -89,8 +86,8 @@
 		}
 		assert ((matcher.start(1) == -1) != (matcher.start(2) == -1));
-		return new Pair<CharSequence, CharSequence>(takeGroup(line, matcher, (matcher.start(1) != -1 ? 1 : 2)), takeGroup(line, matcher, 3));
+		return new Pair<CharSequence, CharSequence>(Strings.takeGroup(line, matcher, (matcher.start(1) != -1 ? 1 : 2)), Strings.takeGroup(line, matcher, 3));
 	}
 
-	protected static final Pattern REQUEST_ID_BREAKER_PATTERN = Pattern.compile("^\\s*([0-9]+)\\s*(.*)$");
+	protected static final Pattern REQUEST_ID_BREAKER_PATTERN = Pattern.compile("^\\s*(-?[0-9]+)\\s*(.*)$");
 
 	protected final static Pair<Integer, CharSequence> takeRequestId(boolean withId, CharSequence line) {
@@ -100,5 +97,5 @@
 				return null;
 			}
-			return new Pair<Integer, CharSequence>(Integer.valueOf(takeGroup(line, matcher, 1).toString()), takeGroup(line, matcher, 2));
+			return new Pair<Integer, CharSequence>(Integer.valueOf(Strings.takeGroup(line, matcher, 1).toString()), Strings.takeGroup(line, matcher, 2));
 		}
 		return new Pair<Integer, CharSequence>(null, line);
Index: java/main/src/main/java/com/framsticks/communication/ServerSideManagedConnection.java
===================================================================
--- java/main/src/main/java/com/framsticks/communication/ServerSideManagedConnection.java	(revision 100)
+++ java/main/src/main/java/com/framsticks/communication/ServerSideManagedConnection.java	(revision 101)
@@ -2,5 +2,4 @@
 
 import com.framsticks.communication.queries.*;
-import com.framsticks.params.SourceInterface;
 import com.framsticks.util.FramsticksException;
 import com.framsticks.util.lang.Holder;
@@ -69,24 +68,5 @@
 	}
 
-	protected final void putFile(File file, String outId) {
-		putLine("file" + outId/* + " " + f.getPath()*/);
-		SourceInterface content = file.getContent();
-		String line;
-		while ((line = content.readLine()) != null) {
-			putLine(line);
-		}
-		putLine("eof");
-	}
 
-	public final void sendFile(final String header, final File file) {
-		senderThread.dispatch(new RunAt<Connection>(requestHandler) {
-			@Override
-			protected void runAt() {
-				putLine(header);
-				putFile(file, "");
-				flushOut();
-			}
-		});
-	}
 
 	protected final void respond(final Response response, final Integer id) {
@@ -94,12 +74,11 @@
 			@Override
 			protected void runAt() {
-				String outId = id != null ? " " + id : "";
 				if (response.getFiles() != null) {
 					for (File f : response.getFiles()) {
-						putFile(f, outId);
+						putFile(f, id);
 					}
 				}
 				StringBuilder statusLine = new StringBuilder();
-				statusLine.append(response.getOk() ? "ok" : "error").append(outId);
+				statusLine.append(response.getOk() ? "ok" : "error").append(idToString(id));
 				if (Strings.notEmpty(response.getComment())) {
 					Request.quoteValue(statusLine.append(" "), response.getComment());
Index: java/main/src/main/java/com/framsticks/communication/queries/CallRequest.java
===================================================================
--- java/main/src/main/java/com/framsticks/communication/queries/CallRequest.java	(revision 100)
+++ java/main/src/main/java/com/framsticks/communication/queries/CallRequest.java	(revision 101)
@@ -17,5 +17,4 @@
 
 	public CallRequest addArguments(String arguments) {
-		// this.arguments = arguments;
 		return this;
 	}
Index: java/main/src/main/java/com/framsticks/communication/queries/NeedFile.java
===================================================================
--- java/main/src/main/java/com/framsticks/communication/queries/NeedFile.java	(revision 101)
+++ java/main/src/main/java/com/framsticks/communication/queries/NeedFile.java	(revision 101)
@@ -0,0 +1,50 @@
+package com.framsticks.communication.queries;
+
+import com.framsticks.communication.File;
+import com.framsticks.util.dispatching.Future;
+import com.framsticks.util.lang.Strings;
+
+public class NeedFile {
+
+	protected final String suggestedName;
+	protected final String description;
+	protected final Future<File> future;
+
+	/**
+	 * @param suggestedName
+	 * @param description
+	 * @param future
+	 */
+	public NeedFile(String suggestedName, String description, Future<File> future) {
+		this.suggestedName = suggestedName;
+		this.description = description;
+		this.future = future;
+	}
+
+	/**
+	 * @return the suggestedName
+	 */
+	public String getSuggestedName() {
+		return suggestedName;
+	}
+
+	/**
+	 * @return the description
+	 */
+	public String getDescription() {
+		return description;
+	}
+
+	/**
+	 * @return the future
+	 */
+	public Future<File> getFuture() {
+		return future;
+	}
+
+	@Override
+	public String toString() {
+		return Strings.toStringEmptyProof(suggestedName, "?") + " " + Strings.toStringEmptyProof(description, "?");
+	}
+
+}
Index: java/main/src/main/java/com/framsticks/communication/queries/NeedFileAcceptor.java
===================================================================
--- java/main/src/main/java/com/framsticks/communication/queries/NeedFileAcceptor.java	(revision 101)
+++ java/main/src/main/java/com/framsticks/communication/queries/NeedFileAcceptor.java	(revision 101)
@@ -0,0 +1,8 @@
+package com.framsticks.communication.queries;
+
+
+public interface NeedFileAcceptor {
+
+	public boolean acceptNeed(NeedFile needFile);
+
+}
Index: java/main/src/main/java/com/framsticks/core/AbstractTree.java
===================================================================
--- java/main/src/main/java/com/framsticks/core/AbstractTree.java	(revision 100)
+++ java/main/src/main/java/com/framsticks/core/AbstractTree.java	(revision 101)
@@ -1,5 +1,8 @@
 package com.framsticks.core;
 
+import java.util.Comparator;
+import java.util.Iterator;
 import java.util.Map;
+import java.util.PriorityQueue;
 
 import javax.annotation.Nonnull;
@@ -9,7 +12,10 @@
 import org.apache.logging.log4j.LogManager;
 
+import com.framsticks.communication.queries.NeedFile;
+import com.framsticks.communication.queries.NeedFileAcceptor;
 import com.framsticks.params.Access;
 import com.framsticks.params.CompositeParam;
 import com.framsticks.params.FramsClass;
+import com.framsticks.params.ParamFlags;
 import com.framsticks.params.ParamsPackage;
 import com.framsticks.params.Registry;
@@ -20,9 +26,9 @@
 import com.framsticks.util.Misc;
 import com.framsticks.util.dispatching.AbstractJoinable;
+import com.framsticks.util.dispatching.BufferedDispatcher;
 import com.framsticks.util.dispatching.Dispatcher;
 import com.framsticks.util.dispatching.Dispatching;
 import com.framsticks.util.dispatching.ExceptionResultHandler;
 import com.framsticks.util.dispatching.Joinable;
-import com.framsticks.util.dispatching.JoinableDispatcher;
 import com.framsticks.util.dispatching.JoinableParent;
 import com.framsticks.util.dispatching.JoinableState;
@@ -30,5 +36,5 @@
 import com.framsticks.util.dispatching.Thread;
 import com.framsticks.util.dispatching.ThrowExceptionHandler;
-import com.framsticks.util.lang.Casting;
+import com.framsticks.util.lang.Pair;
 
 /**
@@ -36,5 +42,5 @@
  */
 @FramsClassAnnotation
-public abstract class AbstractTree extends AbstractJoinable implements Dispatcher<Tree>, Tree, JoinableParent {
+public abstract class AbstractTree extends AbstractJoinable implements Dispatcher<Tree>, Tree, JoinableParent, NeedFileAcceptor {
 
 	private static final Logger log = LogManager.getLogger(AbstractTree.class);
@@ -43,5 +49,19 @@
 	private ExceptionResultHandler handler = ThrowExceptionHandler.getInstance();
 
-	private JoinableDispatcher<Tree> dispatcher;
+	protected final BufferedDispatcher<Tree> bufferedDispatcher = new BufferedDispatcher<>();
+
+	protected final PriorityQueue<Pair<Integer, NeedFileAcceptor>> needFileAcceptors = new PriorityQueue<>(32, new Comparator<Pair<Integer, NeedFileAcceptor>>() {
+
+		@Override
+		public int compare(Pair<Integer, NeedFileAcceptor> arg0, Pair<Integer, NeedFileAcceptor> arg1) {
+			if (arg0.first < arg1.first) {
+				return -1;
+			}
+			if (arg0.first > arg1.first) {
+				return 1;
+			}
+			return 0;
+		}
+	});
 
 	@Override
@@ -152,6 +172,6 @@
 	 */
 	@Override
-	public JoinableDispatcher<Tree> getDispatcher() {
-		return dispatcher;
+	public Dispatcher<Tree> getDispatcher() {
+		return bufferedDispatcher.getTargetDispatcher();
 	}
 
@@ -160,9 +180,9 @@
 	 */
 	@Override
-	public void setDispatcher(JoinableDispatcher<Tree> dispatcher) {
-		if (this.dispatcher != null) {
-			throw new FramsticksException().msg("dispatcher is already set").arg("tree", this).arg("dispatcher", dispatcher);
-		}
-		this.dispatcher = dispatcher;
+	public void setDispatcher(Dispatcher<Tree> dispatcher) {
+		if (bufferedDispatcher.getTargetDispatcher() != null) {
+			throw new FramsticksException().msg("dispatcher is already set").arg("tree", this).arg("dispatcher", bufferedDispatcher.getTargetDispatcher());
+		}
+		bufferedDispatcher.setTargetDispatcher(dispatcher);
 	}
 
@@ -170,5 +190,5 @@
 	 * @return the name
 	 */
-	@ParamAnnotation
+	@ParamAnnotation(flags = ParamFlags.USERREADONLY)
 	public String getName() {
 		return name;
@@ -193,13 +213,13 @@
 	@Override
 	protected void joinableStart() {
-		if (dispatcher == null) {
-			dispatcher = new Thread<Tree>();
-		}
-		Dispatching.use(dispatcher, this);
+		if (bufferedDispatcher.getTargetDispatcher() == null) {
+			bufferedDispatcher.setTargetDispatcher(new Thread<Tree>());
+		}
+		Dispatching.use(bufferedDispatcher.getTargetDispatcher(), this);
 	}
 
 	@Override
 	protected void joinableInterrupt() {
-		Dispatching.drop(dispatcher, this);
+		Dispatching.drop(bufferedDispatcher.getTargetDispatcher(), this);
 	}
 
@@ -211,10 +231,10 @@
 	@Override
 	protected void joinableJoin() throws InterruptedException {
-		Dispatching.join(dispatcher);
+		Dispatching.join(bufferedDispatcher.getTargetDispatcher());
 	}
 
 	@Override
 	public void childChangedState(Joinable joinable, JoinableState state) {
-		if (joinable == dispatcher) {
+		if (joinable == bufferedDispatcher.getTargetDispatcher()) {
 			proceedToState(state);
 		}
@@ -223,16 +243,10 @@
 	@Override
 	public boolean isActive() {
-		if (dispatcher == null) {
-			throw new FramsticksException().msg("no dispatcher is set for tree yet").arg("tree", this);
-		}
-		return dispatcher.isActive();
+		return bufferedDispatcher.isActive();
 	}
 
 	@Override
 	public void dispatch(RunAt<? extends Tree> runnable) {
-		if (dispatcher == null) {
-			throw new FramsticksException().msg("no dispatcher is set for tree yet").arg("tree", this);
-		}
-		dispatcher.dispatch(runnable);
+		bufferedDispatcher.dispatch(runnable);
 	}
 
@@ -242,5 +256,5 @@
 
 	@Override
-	public void putSideNote(Object object, Object key, Object value) {
+	public <T> void putSideNote(Object object, SideNoteKey<T> key, T value) {
 		assert isActive();
 		Misc.throwIfNull(object);
@@ -253,10 +267,11 @@
 		}
 		@SuppressWarnings("unchecked")
-		Map<Object, Object> sideNotesMap = (Map<Object, Object>) sideNote;
+		Map<SideNoteKey<?>, Object> sideNotesMap = (Map<SideNoteKey<?>, Object>) sideNote;
 		sideNotesMap.put(key, value);
 	}
 
-	@Override
-	public <T> T getSideNote(Object object, Object key, Class<T> valueType) {
+	@SuppressWarnings("unchecked")
+	@Override
+	public <T> T getSideNote(Object object, SideNoteKey<T> key) {
 		assert isActive();
 		Misc.throwIfNull(object);
@@ -266,9 +281,14 @@
 			return null;
 		}
-		return Casting.nullOrThrowCast(valueType, ((Map<?, ?>) sideNote).get(key));
-	}
-
-	@Override
-	public boolean removeSideNote(Object object, Object key) {
+		Object value = ((Map<SideNoteKey<?>, Object>) sideNote).get(key);
+		if (value == null) {
+			return null;
+		}
+		return (T) value;
+	}
+
+	@Override
+	public boolean removeSideNote(Object object, SideNoteKey<?> key) {
+		assert isActive();
 		Object sideNote = sideNotes.get(object);
 		if (sideNote == null) {
@@ -276,5 +296,5 @@
 		}
 		@SuppressWarnings("unchecked")
-		Map<Object, Object> sideNotesMap = (Map<Object, Object>) sideNote;
+		Map<SideNoteKey<?>, Object> sideNotesMap = (Map<SideNoteKey<?>, Object>) sideNote;
 		boolean result = (sideNotesMap.remove(key) != null);
 		if (sideNotesMap.isEmpty()) {
@@ -284,4 +304,39 @@
 	}
 
+	@Override
+	public void addNeedFileAcceptor(int priority, NeedFileAcceptor acceptor) {
+		assert isActive();
+		needFileAcceptors.add(Pair.make(priority, acceptor));
+	}
+
+	@Override
+	public void removeNeedFileAcceptor(NeedFileAcceptor acceptor) {
+		assert isActive();
+		Iterator<Pair<Integer, NeedFileAcceptor>> i = needFileAcceptors.iterator();
+		while (i.hasNext()) {
+			if (i.next().second == acceptor) {
+				i.remove();
+				break;
+			}
+		}
+	}
+
+	@Override
+	public boolean acceptNeed(final NeedFile needFile) {
+		Dispatching.dispatchIfNotActive(this, new RunAt<AbstractTree>(needFile.getFuture()) {
+
+			@Override
+			protected void runAt() {
+				for (Pair<Integer, NeedFileAcceptor> acceptor : needFileAcceptors) {
+					if (acceptor.second.acceptNeed(needFile)) {
+						return;
+					}
+				}
+				throw new FramsticksException().msg("failed to find need file acceptor in tree").arg("tree", AbstractTree.this);
+			}
+		});
+		return true;
+	}
+
 }
 
Index: java/main/src/main/java/com/framsticks/core/Framsticks.java
===================================================================
--- java/main/src/main/java/com/framsticks/core/Framsticks.java	(revision 100)
+++ java/main/src/main/java/com/framsticks/core/Framsticks.java	(revision 101)
@@ -29,5 +29,5 @@
 	public static void main(final String[] args) {
 
-		String config = "/configs/framsticks.xml";
+		String config = "/framsticks.xml";
 		if (args.length != 0) {
 			config = args[0];
Index: java/main/src/main/java/com/framsticks/core/ListChangeHandler.java
===================================================================
--- java/main/src/main/java/com/framsticks/core/ListChangeHandler.java	(revision 100)
+++ 	(revision )
@@ -1,8 +1,0 @@
-package com.framsticks.core;
-
-/**
- * @author Piotr Sniegowski
- */
-public interface ListChangeHandler {
-    public void handle(ListChange change);
-}
Index: java/main/src/main/java/com/framsticks/core/LocalTree.java
===================================================================
--- java/main/src/main/java/com/framsticks/core/LocalTree.java	(revision 100)
+++ java/main/src/main/java/com/framsticks/core/LocalTree.java	(revision 101)
@@ -15,5 +15,8 @@
 import com.framsticks.params.types.ProcedureParam;
 import com.framsticks.util.FramsticksException;
+import com.framsticks.util.dispatching.Dispatching;
 import com.framsticks.util.dispatching.Future;
+import com.framsticks.util.dispatching.Joinable;
+import com.framsticks.util.dispatching.JoinableState;
 
 import static com.framsticks.core.TreeOperations.*;
@@ -23,5 +26,5 @@
 	private static final Logger log = LogManager.getLogger(LocalTree.class);
 
-	protected Object rootObject;
+	protected Joinable joinableRootObject;
 
 	/**
@@ -42,4 +45,8 @@
 		assignRootParam(access.buildParam(new ParamBuilder()).id(getName()).finish(CompositeParam.class));
 		assignRootObject(object);
+
+		if (object instanceof Joinable) {
+			joinableRootObject = (Joinable) object;
+		}
 	}
 
@@ -69,7 +76,7 @@
 	// @Override
 	// public void get(Path path, ValueParam param, Future<Object> future) {
-	// 	assert isActive();
-	// 	path = resolveTopSync(path);
-	// 	future.pass(bindAccess(path).get(param, Object.class));
+	//	assert isActive();
+	//	path = resolveTopSync(path);
+	//	future.pass(bindAccess(path).get(param, Object.class));
 	// }
 
@@ -137,3 +144,40 @@
 		}
 	}
+
+	@Override
+	protected void joinableStart() {
+		super.joinableStart();
+		if (joinableRootObject != null) {
+			Dispatching.use(joinableRootObject, this);
+		}
+	}
+
+	@Override
+	protected void joinableInterrupt() {
+		if (joinableRootObject != null) {
+			Dispatching.drop(joinableRootObject, this);
+		}
+		super.joinableInterrupt();
+	}
+
+	@Override
+	protected void joinableFinish() {
+		super.joinableFinish();
+	}
+
+	@Override
+	protected void joinableJoin() throws InterruptedException {
+		if (joinableRootObject != null) {
+			Dispatching.join(joinableRootObject);
+		}
+		super.joinableJoin();
+	}
+
+	@Override
+	public void childChangedState(Joinable joinable, JoinableState state) {
+		super.childChangedState(joinable, state);
+		if (joinable == joinableRootObject) {
+			proceedToState(state);
+		}
+	}
 }
Index: java/main/src/main/java/com/framsticks/core/ObjectListener.java
===================================================================
--- java/main/src/main/java/com/framsticks/core/ObjectListener.java	(revision 100)
+++ 	(revision )
@@ -1,11 +1,0 @@
-package com.framsticks.core;
-
-import com.framsticks.params.Param;
-
-/**
- * @author Piotr Sniegowski
- */
-public interface ObjectListener {
-    public void onChange(Param param);
-    public void onRemove();
-}
Index: java/main/src/main/java/com/framsticks/core/Path.java
===================================================================
--- java/main/src/main/java/com/framsticks/core/Path.java	(revision 100)
+++ java/main/src/main/java/com/framsticks/core/Path.java	(revision 101)
@@ -31,4 +31,6 @@
 	final LinkedList<Node> nodes;
 
+	public static final SideNoteKey<CompositeParam> OBJECT_PARAM_KEY = SideNoteKey.make(CompositeParam.class);
+
 	protected static Object getKnownChild(Tree tree, Access access, CompositeParam param, ExceptionResultHandler handler) {
 		Object child = access.get(param, Object.class);
@@ -38,5 +40,5 @@
 		try {
 			tree.prepareAccess(param);
-			tree.putSideNote(child, CompositeParam.class, param);
+			tree.putSideNote(child, OBJECT_PARAM_KEY, param);
 			return child;
 		} catch (FramsticksException e) {
@@ -164,5 +166,5 @@
 				b.append("/").append(e);
 				access.select(current.getObject());
-				tree.putSideNote(current.getObject(), CompositeParam.class, current.getParam());
+				tree.putSideNote(current.getObject(), OBJECT_PARAM_KEY, current.getParam());
 				current = new Node(current.getTree(), c, getKnownChild(tree, access, c, handler));
 				nodes.add(current);
Index: java/main/src/main/java/com/framsticks/core/PathListener.java
===================================================================
--- java/main/src/main/java/com/framsticks/core/PathListener.java	(revision 100)
+++ 	(revision )
@@ -1,10 +1,0 @@
-package com.framsticks.core;
-
-import com.framsticks.params.Param;
-
-/**
- * @author Piotr Sniegowski
- */
-public interface PathListener {
-    void onChange(Param param);
-}
Index: java/main/src/main/java/com/framsticks/core/SideNoteKey.java
===================================================================
--- java/main/src/main/java/com/framsticks/core/SideNoteKey.java	(revision 101)
+++ java/main/src/main/java/com/framsticks/core/SideNoteKey.java	(revision 101)
@@ -0,0 +1,28 @@
+package com.framsticks.core;
+
+import com.framsticks.util.FramsticksException;
+
+public class SideNoteKey<T> {
+
+	protected final Class<T> type;
+
+	/**
+	 * @param type
+	 */
+	public SideNoteKey(Class<T> type) {
+		this.type = type;
+	}
+
+	public static <T2> SideNoteKey<T2> make(Class<T2> type) {
+		return new SideNoteKey<T2>(type);
+	}
+
+	public T newValue() {
+		try {
+			return type.newInstance();
+		} catch (InstantiationException | IllegalAccessException e) {
+			throw new FramsticksException().msg("failed to create side note value").arg("type", type).cause(e);
+		}
+	}
+
+}
Index: java/main/src/main/java/com/framsticks/core/Tree.java
===================================================================
--- java/main/src/main/java/com/framsticks/core/Tree.java	(revision 100)
+++ java/main/src/main/java/com/framsticks/core/Tree.java	(revision 101)
@@ -3,4 +3,5 @@
 import javax.annotation.Nonnull;
 
+import com.framsticks.communication.queries.NeedFileAcceptor;
 import com.framsticks.params.Access;
 import com.framsticks.params.CompositeParam;
@@ -15,5 +16,4 @@
 import com.framsticks.util.dispatching.Future;
 import com.framsticks.util.dispatching.Joinable;
-import com.framsticks.util.dispatching.JoinableDispatcher;
 
 public interface Tree extends Dispatcher<Tree>, Joinable, ExceptionResultHandler {
@@ -54,7 +54,7 @@
 	public ExceptionResultHandler getExceptionHandler();
 
-	public void setDispatcher(JoinableDispatcher<Tree> dispatcher);
+	public void setDispatcher(Dispatcher<Tree> dispatcher);
 
-	public JoinableDispatcher<Tree> getDispatcher();
+	public Dispatcher<Tree> getDispatcher();
 
 	public <A> void addListener(Path path, EventParam param, EventListener<A> listener, Class<A> argumentType, Future<Void> future);
@@ -64,9 +64,13 @@
 	public Registry getRegistry();
 
-	public void putSideNote(Object object, Object key, Object value);
+	public <T> void putSideNote(Object object, SideNoteKey<T> key, T value);
 
-	public boolean removeSideNote(Object object, Object key);
+	public boolean removeSideNote(Object object, SideNoteKey<?> key);
 
-	public <T> T getSideNote(Object object, Object key, Class<T> valueType);
+	public <T> T getSideNote(Object object, SideNoteKey<T> key);
+
+	public void addNeedFileAcceptor(int priority, NeedFileAcceptor acceptor);
+
+	public void removeNeedFileAcceptor(NeedFileAcceptor acceptor);
 
 }
Index: java/main/src/main/java/com/framsticks/core/TreeOperations.java
===================================================================
--- java/main/src/main/java/com/framsticks/core/TreeOperations.java	(revision 100)
+++ java/main/src/main/java/com/framsticks/core/TreeOperations.java	(revision 101)
@@ -1,4 +1,3 @@
 package com.framsticks.core;
-
 
 import java.util.HashSet;
@@ -22,5 +21,5 @@
 import com.framsticks.params.PropertiesAccess;
 import com.framsticks.params.UniqueListAccess;
-import com.framsticks.params.Util;
+import com.framsticks.params.ParamsUtil;
 import com.framsticks.params.types.EventParam;
 import com.framsticks.params.types.ObjectParam;
@@ -43,7 +42,8 @@
 	}
 
-	public static final Object FETCHED_MARK = new Object();
-
-	public static @Nonnull FramsClass processFetchedInfo(Tree tree, File file) {
+	public static final SideNoteKey<Boolean> FETCHED_MARK = SideNoteKey.make(Boolean.class);
+
+	public static @Nonnull
+	FramsClass processFetchedInfo(Tree tree, File file) {
 		assert tree.isActive();
 		FramsClass framsClass = Loaders.loadFramsClass(file.getContent());
@@ -80,5 +80,4 @@
 		assert path.isTheSame(files.get(0).getPath());
 
-
 		try {
 			log.debug("process fetched values: {}", path);
@@ -98,10 +97,9 @@
 					if (path.getTop().getParam() instanceof ObjectParam) {
 						assert results.size() == 1;
-						Util.takeAllNonNullValues(bindAccess(result), parsingAccess.select(results.get(0)));
+						ParamsUtil.takeAllNonNullValues(bindAccess(result), parsingAccess.select(results.get(0)));
 						mark(result.getTree(), result.getTopObject(), FETCHED_MARK, true);
 						future.pass(result);
 						return;
 					}
-
 
 					final ListAccess listAccess = (ListAccess) access;
@@ -139,5 +137,5 @@
 
 						targetAccess.select(childTo);
-						Util.takeAllNonNullValues(targetAccess, parsingAccess);
+						ParamsUtil.takeAllNonNullValues(targetAccess, parsingAccess);
 						if (newOne) {
 							listAccess.set(id, childTo);
@@ -188,7 +186,7 @@
 	}
 
-
-	public static @Nonnull Access bindAccessFromSideNote(Tree tree, Object object) {
-		CompositeParam param = tree.getSideNote(object, CompositeParam.class, CompositeParam.class);
+	public static @Nonnull
+	Access bindAccessFromSideNote(Tree tree, Object object) {
+		CompositeParam param = tree.getSideNote(object, Path.OBJECT_PARAM_KEY);
 		if (param == null) {
 			throw new FramsticksException().msg("failed to bind access from side node").arg("tree", tree).arg("object", object).arg("type", object.getClass());
@@ -197,10 +195,12 @@
 	}
 
-	public static @Nonnull Access bindAccess(Tree tree, String path) {
+	public static @Nonnull
+	Access bindAccess(Tree tree, String path) {
 		log.debug("bind access for textual: {} in {}", path, tree);
 		return bindAccess(Path.to(tree, path));
 	}
 
-	public static @Nonnull Access bindAccess(Node node) {
+	public static @Nonnull
+	Access bindAccess(Node node) {
 		Tree tree = node.getTree();
 		assert tree.isActive();
@@ -209,5 +209,5 @@
 		try {
 			Access access = tree.prepareAccess(node.getParam());
-			tree.putSideNote(node.getObject(), CompositeParam.class, node.getParam());
+			tree.putSideNote(node.getObject(), Path.OBJECT_PARAM_KEY, node.getParam());
 
 			return access.select(node.getObject());
@@ -218,5 +218,6 @@
 	}
 
-	public static @Nonnull Access bindAccess(Path path) {
+	public static @Nonnull
+	Access bindAccess(Path path) {
 		assert path.getTree().isActive();
 		path.assureResolved();
@@ -280,5 +281,4 @@
 		});
 	}
-
 
 	/**
@@ -323,5 +323,5 @@
 	public static Object createAccessee(Tree tree, CompositeParam param) {
 		Object object = tree.prepareAccess(param).createAccessee();
-		tree.putSideNote(object, CompositeParam.class, param);
+		tree.putSideNote(object, Path.OBJECT_PARAM_KEY, param);
 		return object;
 	}
@@ -329,15 +329,15 @@
 	public static Object createAccessee(Tree tree, Access access) {
 		Object object = access.createAccessee();
-		tree.putSideNote(object, CompositeParam.class, access.buildParam(new ParamBuilder()).finish(CompositeParam.class));
+		tree.putSideNote(object, Path.OBJECT_PARAM_KEY, access.buildParam(new ParamBuilder()).finish(CompositeParam.class));
 		return object;
 	}
 
-	public static boolean isMarked(Tree tree, Object object, Object mark, boolean defValue) {
-		assert tree.isActive();
-		Boolean v = tree.getSideNote(object, mark, Boolean.class);
+	public static boolean isMarked(Tree tree, Object object, SideNoteKey<Boolean> mark, boolean defValue) {
+		assert tree.isActive();
+		Boolean v = tree.getSideNote(object, mark);
 		return (v != null ? v : defValue);
 	}
 
-	public static void mark(Tree tree, Object object, Object mark, boolean value) {
+	public static void mark(Tree tree, Object object, SideNoteKey<Boolean> mark, boolean value) {
 		assert tree.isActive();
 		tree.putSideNote(object, mark, value);
@@ -348,3 +348,38 @@
 	}
 
+	public static <T extends Param> T getParam(Path path, String id, Class<T> type) {
+		return getFramsClass(path).getParamEntry(id, type);
+	}
+
+	public static <T> T getOrCreateSideNote(Tree tree, Object object, SideNoteKey<T> key) {
+		T result = tree.getSideNote(object, key);
+		if (result == null) {
+			result = key.newValue();
+			tree.putSideNote(object, key, result);
+		}
+		return result;
+	}
+
+	public static <T> boolean hasSideNotes(Tree tree, Object object, SideNoteKey<T> key) {
+		return tree.getSideNote(object, key) != null;
+	}
+
+	public static <T> boolean hasSideNote(Path path, SideNoteKey<T> key) {
+		return path.getTree().getSideNote(path.getTopObject(), key) != null;
+	}
+
+	public static <T> T getSideNote(Path path, SideNoteKey<T> key) {
+		assert path.isResolved();
+		return path.getTree().getSideNote(path.getTopObject(), key);
+	}
+
+	public static <T> void putSideNote(Path path, SideNoteKey<T> key, T value) {
+		path.getTree().putSideNote(path.getTopObject(), key, value);
+	}
+
+	public static boolean removeSideNote(Path path, SideNoteKey<?> key) {
+		assert path.isResolved();
+		return path.getTree().removeSideNote(path.getTopObject(), key);
+	}
+
 }
Index: java/main/src/main/java/com/framsticks/core/ValueChange.java
===================================================================
--- java/main/src/main/java/com/framsticks/core/ValueChange.java	(revision 101)
+++ java/main/src/main/java/com/framsticks/core/ValueChange.java	(revision 101)
@@ -0,0 +1,32 @@
+package com.framsticks.core;
+
+import com.framsticks.params.annotations.FramsClassAnnotation;
+import com.framsticks.params.annotations.ParamAnnotation;
+import com.framsticks.util.lang.Strings;
+
+@FramsClassAnnotation
+public class ValueChange {
+
+	@ParamAnnotation
+	public Object value;
+
+
+	/**
+	 * @param value
+	 */
+	public ValueChange(Object value) {
+		this.value = value;
+	}
+
+	/**
+	 *
+	 */
+	public ValueChange() {
+	}
+
+	@Override
+	public String toString() {
+		return Strings.toStringNullProof(value, "<null>");
+	}
+
+}
Index: java/main/src/main/java/com/framsticks/experiment/Experiment.java
===================================================================
--- java/main/src/main/java/com/framsticks/experiment/Experiment.java	(revision 101)
+++ java/main/src/main/java/com/framsticks/experiment/Experiment.java	(revision 101)
@@ -0,0 +1,37 @@
+package com.framsticks.experiment;
+
+import java.util.Map;
+
+import com.framsticks.core.ListChange;
+import com.framsticks.params.EventListener;
+import com.framsticks.params.SimpleUniqueList;
+import com.framsticks.params.annotations.AutoAppendAnnotation;
+import com.framsticks.params.annotations.FramsClassAnnotation;
+import com.framsticks.params.annotations.ParamAnnotation;
+import com.framsticks.util.dispatching.JoinableCollection;
+
+@FramsClassAnnotation
+public class Experiment extends JoinableCollection<Simulator> {
+
+	protected final SimpleUniqueList<Simulator> simulators = new SimpleUniqueList<>(Simulator.class, 's');
+
+	@ParamAnnotation
+	public Map<String, Simulator> getSimulators() {
+		return simulators.getView();
+	}
+
+	@ParamAnnotation(id = "simulators_changed")
+	public void addSimulatorsListener(EventListener<ListChange> listener) {
+		simulators.addListener(listener);
+	}
+
+	@ParamAnnotation(id = "simulators_changed")
+	public void removeSimulatorsListener(EventListener<ListChange> listener) {
+		simulators.removeListener(listener);
+	}
+
+	@AutoAppendAnnotation
+	public void addSimulator(Simulator simulator) {
+		simulators.add(simulator);
+	}
+}
Index: java/main/src/main/java/com/framsticks/experiment/Simulator.java
===================================================================
--- java/main/src/main/java/com/framsticks/experiment/Simulator.java	(revision 101)
+++ java/main/src/main/java/com/framsticks/experiment/Simulator.java	(revision 101)
@@ -0,0 +1,103 @@
+package com.framsticks.experiment;
+
+import com.framsticks.params.ParamFlags;
+import com.framsticks.params.UniqueObject;
+import com.framsticks.params.annotations.FramsClassAnnotation;
+import com.framsticks.params.annotations.ParamAnnotation;
+import com.framsticks.params.types.ProcedureParam;
+import com.framsticks.remote.RemoteTree;
+import com.framsticks.util.dispatching.AbstractJoinable;
+import com.framsticks.util.dispatching.Dispatching;
+import com.framsticks.util.dispatching.Joinable;
+import com.framsticks.util.dispatching.JoinableParent;
+import com.framsticks.util.dispatching.JoinableState;
+
+@FramsClassAnnotation
+public class Simulator extends AbstractJoinable implements JoinableParent, UniqueObject {
+	protected String uid;
+
+	protected final RemoteTree remoteTree = new RemoteTree();
+
+	/**
+	 *
+	 */
+	public Simulator() {
+		super();
+	}
+
+	@ParamAnnotation
+	public void setAddress(String address) {
+		remoteTree.setAddress(address);
+	}
+
+	@ParamAnnotation(flags = ParamFlags.USERREADONLY)
+	public String getAddress() {
+		return remoteTree.getAddress();
+	}
+
+	@Override
+	@ParamAnnotation
+	public String getName() {
+
+		return "simulator " + uid + " " + getAddress();
+	}
+
+	@Override
+	@ParamAnnotation
+	public String getUid() {
+		return uid;
+	}
+
+	@Override
+	public void setUid(String uid) {
+		this.uid = uid;
+	}
+
+	/**
+	 * @return the tree
+	 */
+	@ParamAnnotation
+	public RemoteTree getRemoteTree() {
+		return remoteTree;
+	}
+
+	@Override
+	protected void joinableStart() {
+		Dispatching.use(remoteTree, this);
+
+	}
+
+	@Override
+	protected void joinableInterrupt() {
+		Dispatching.drop(remoteTree, this);
+
+	}
+
+	@Override
+	protected void joinableFinish() {
+
+	}
+
+	@Override
+	protected void joinableJoin() throws InterruptedException {
+		Dispatching.join(remoteTree);
+	}
+
+	@ParamAnnotation(paramType = ProcedureParam.class)
+	public void init() {
+	}
+
+	@ParamAnnotation(paramType = ProcedureParam.class)
+	public void start() {
+	}
+
+	@ParamAnnotation(paramType = ProcedureParam.class)
+	public void stop() {
+	}
+
+	@Override
+	public void childChangedState(Joinable joinable, JoinableState state) {
+		proceedToState(state);
+	}
+
+}
Index: java/main/src/main/java/com/framsticks/gui/Browser.java
===================================================================
--- java/main/src/main/java/com/framsticks/gui/Browser.java	(revision 100)
+++ java/main/src/main/java/com/framsticks/gui/Browser.java	(revision 101)
@@ -1,4 +1,7 @@
 package com.framsticks.gui;
 
+import com.framsticks.communication.File;
+import com.framsticks.communication.queries.NeedFile;
+import com.framsticks.communication.queries.NeedFileAcceptor;
 import com.framsticks.core.*;
 import com.framsticks.gui.console.Console;
@@ -9,4 +12,5 @@
 import com.framsticks.params.annotations.FramsClassAnnotation;
 import com.framsticks.params.annotations.ParamAnnotation;
+import com.framsticks.parsers.FileSource;
 import com.framsticks.remote.RemoteTree;
 import com.framsticks.util.FramsticksException;
@@ -22,4 +26,5 @@
 
 import javax.swing.*;
+import javax.swing.filechooser.FileNameExtensionFilter;
 
 import org.apache.logging.log4j.Logger;
@@ -30,8 +35,16 @@
 import java.awt.datatransfer.StringSelection;
 import java.awt.event.ActionEvent;
+import java.awt.event.ActionListener;
+import java.awt.event.WindowAdapter;
+import java.awt.event.WindowEvent;
+import java.io.IOException;
 import java.util.ArrayList;
 import java.util.LinkedList;
 import java.util.List;
+import java.util.regex.Matcher;
+import java.util.regex.Pattern;
+
 import com.framsticks.util.dispatching.RunAt;
+import com.framsticks.util.lang.Strings;
 
 /**
@@ -132,10 +145,83 @@
 	}
 
+	protected static final Pattern extensionFilterPattern = Pattern.compile("\\*\\.(\\S+)");
+
 	@AutoAppendAnnotation
-	public void addTree(Tree tree) {
+	public void addTree(final Tree tree) {
 		log.debug("adding tree: {}", tree);
 		tree.setDispatcher(new SwingDispatcher<Tree>());
 		tree.setExceptionHandler(this);
 		trees.add(tree);
+
+		final NeedFileAcceptor acceptor = new NeedFileAcceptor() {
+
+			protected boolean done = false;
+
+			@Override
+			public boolean acceptNeed(final NeedFile needFile) {
+				final JFileChooser chooser = new JFileChooser();
+				final JFrame frame = new JFrame();
+
+				frame.setDefaultCloseOperation(JFrame.DO_NOTHING_ON_CLOSE);
+
+				frame.addWindowListener(new WindowAdapter() {
+					@Override
+					public void windowClosing(WindowEvent e) {
+						if (!done) {
+							needFile.getFuture().handle(new FramsticksException().msg("user closed the window"));
+						}
+						frame.setVisible(false);
+						frame.dispose();
+					}
+				});
+
+				frame.setTitle(Strings.toStringEmptyProof(needFile.getDescription(), "Choose file"));
+				chooser.setMultiSelectionEnabled(false);
+				Matcher matcher = extensionFilterPattern.matcher(needFile.getSuggestedName());
+				if (matcher.matches()) {
+					chooser.setFileFilter(new FileNameExtensionFilter(Strings.toStringEmptyProof(needFile.getDescription(), "file"), Strings.takeGroup(needFile.getSuggestedName(), matcher, 1).toString()));
+				}
+
+				frame.getContentPane().add(chooser);
+
+				chooser.addActionListener(new ActionListener() {
+
+					@Override
+					public void actionPerformed(ActionEvent event) {
+						if (event.getActionCommand().equals("CancelSelection")) {
+							needFile.getFuture().handle(new FramsticksException().msg("user cancelled choose"));
+							frame.setVisible(false);
+							frame.dispose();
+						}
+						if (event.getActionCommand().equals("ApproveSelection")) {
+							File file = null;
+							String filename = chooser.getSelectedFile().getAbsolutePath();
+							try {
+								file = new File("", new FileSource(filename));
+							} catch (IOException e) {
+								needFile.getFuture().handle(new FramsticksException().msg("failed to open choosed file").arg("filename", filename).cause(e));
+							}
+							if (file != null) {
+								done = true;
+								needFile.getFuture().pass(file);
+							}
+							frame.setVisible(false);
+							frame.dispose();
+						}
+					}
+				});
+				frame.setVisible(true);
+				return true;
+			}
+		};
+
+		tree.dispatch(new RunAt<Tree>(this) {
+			@Override
+			protected void runAt() {
+				log.debug("adding need file acceptor: {}", acceptor);
+				tree.addNeedFileAcceptor(Integer.MAX_VALUE, acceptor);
+			}
+		});
+
 	}
 
@@ -143,19 +229,19 @@
 		// final Tree i = trees.get("localhost");
 		// i.dispatch(new RunAt<Tree>(future) {
-		// 	@Override
-		// 	protected void runAt() {
-		// 		TreeOperations.tryGet(i, path, new FutureHandler<Path>(future) {
-		// 			@Override
-		// 			protected void result(final Path p) {
-		// 				future.pass(p);
-		// 				mainFrame.dispatch(new RunAt<Frame>(future) {
-		// 					@Override
-		// 					protected void runAt() {
-		// 						mainFrame.goTo(p);
-		// 					}
-		// 				});
-		// 			}
-		// 		});
-		// 	}
+		//	@Override
+		//	protected void runAt() {
+		//		TreeOperations.tryGet(i, path, new FutureHandler<Path>(future) {
+		//			@Override
+		//			protected void result(final Path p) {
+		//				future.pass(p);
+		//				mainFrame.dispatch(new RunAt<Frame>(future) {
+		//					@Override
+		//					protected void runAt() {
+		//						mainFrame.goTo(p);
+		//					}
+		//				});
+		//			}
+		//		});
+		//	}
 		// });
 	}
@@ -183,4 +269,5 @@
 						protected void runAt() {
 							final Path p = Path.to(i, "/");
+							log.debug("adding path: {}", p);
 							dispatch(new RunAt<Browser>(this) {
 								@Override
Index: java/main/src/main/java/com/framsticks/gui/Frame.java
===================================================================
--- java/main/src/main/java/com/framsticks/gui/Frame.java	(revision 100)
+++ java/main/src/main/java/com/framsticks/gui/Frame.java	(revision 101)
@@ -243,5 +243,5 @@
 			@Override
 			public void actionPerformed(ActionEvent actionEvent) {
-				interrupt();
+				interruptJoinable();
 			}
 		});
@@ -268,4 +268,5 @@
 		log.debug("trying mount: {}", path);
 		if (!tree.getAssignedRoot().isResolved()) {
+			log.debug("root not yet assigned, geting root");
 			tree.get(path, new FutureHandler<Path>(this) {
 
Index: java/main/src/main/java/com/framsticks/gui/FrameJoinable.java
===================================================================
--- java/main/src/main/java/com/framsticks/gui/FrameJoinable.java	(revision 100)
+++ java/main/src/main/java/com/framsticks/gui/FrameJoinable.java	(revision 101)
@@ -31,5 +31,5 @@
 	 */
 	public FrameJoinable() {
-		statusBar = new StatusBar();
+		statusBar = new StatusBar(this);
 	}
 
@@ -101,5 +101,5 @@
 			@Override
 			protected void runAt() {
-				finish();
+				finishJoinable();
 			}
 		});
@@ -135,5 +135,5 @@
 			public void windowClosing(WindowEvent e) {
 				log.info("received closing");
-				interrupt();
+				interruptJoinable();
 			}
 		});
Index: java/main/src/main/java/com/framsticks/gui/Gui.java
===================================================================
--- java/main/src/main/java/com/framsticks/gui/Gui.java	(revision 100)
+++ java/main/src/main/java/com/framsticks/gui/Gui.java	(revision 101)
@@ -7,6 +7,8 @@
 import javax.swing.Box;
 import javax.swing.BoxLayout;
+import javax.swing.JComponent;
 import javax.swing.JLabel;
 import javax.swing.JPanel;
+import javax.swing.border.TitledBorder;
 
 import org.apache.logging.log4j.Logger;
@@ -24,4 +26,5 @@
 import com.framsticks.params.CompositeParam;
 import com.framsticks.params.Param;
+import com.framsticks.params.ParamFlags;
 import com.framsticks.params.PrimitiveParam;
 import com.framsticks.params.types.BinaryParam;
@@ -101,8 +104,8 @@
 	}
 
-	public static <P extends Param, C extends Control> void fillWithControls(ControlOwner owner, Collection<P> params, Map<P, C> components, Class<C> controlType) {
-		JPanel panel = owner.getPanelForControls();
+	public static <P extends Param, C extends Control> void fillWithControls(ControlOwner owner, JPanel panel, Collection<P> params, Map<String, C> components, Class<C> controlType) {
+		panel.setLayout(new BoxLayout(panel, BoxLayout.PAGE_AXIS));
 		for (P param : params) {
-			if (param.isUserHidden()) {
+			if (param.hasFlag(ParamFlags.USERHIDDEN)) {
 				continue;
 			}
@@ -122,21 +125,59 @@
 			line.setLayout(new BoxLayout(line, BoxLayout.LINE_AXIS));
 			line.setAlignmentX(JPanel.LEFT_ALIGNMENT);
-			JLabel label = new JLabel(Strings.notEmpty(param.getName()) ? param.getName() : (Strings.notEmpty(param.getId()) ? param.getId() : "?"));
-			label.setToolTipText(control.getToolTipText());
-			label.setHorizontalAlignment(JLabel.RIGHT);
-			Dimension labelSize = new Dimension(150, 30);
-			label.setMaximumSize(labelSize);
-			label.setMinimumSize(labelSize);
-			label.setPreferredSize(labelSize);
-			line.add(label);
-			line.add(Box.createRigidArea(new Dimension(8, 0)));
+
 			line.add(control);
 			line.revalidate();
 			panel.add(line);
 			panel.add(Box.createRigidArea(new Dimension(0, 8)));
+			components.put(param.getId(), control);
 			//component.setAlignmentX(LEFT_ALIGNMENT);
-			components.put(param, control);
+			// components.put(param.getId(), control);
 		}
 
 	}
+
+	public static String getBestName(Param param) {
+		if (Strings.notEmpty(param.getName())) {
+			return param.getName();
+		}
+		if (Strings.notEmpty(param.getId())) {
+			return param.getId();
+		}
+		return "?";
+	}
+
+	public static void setupTitledControl(Control control, JComponent... components) {
+
+		control.setLayout(new BoxLayout(control, BoxLayout.PAGE_AXIS));
+		control.setBorder(new TitledBorder(Gui.getBestName(control.getParam())));
+		for (JComponent c : components) {
+			// control.add(Box.createRigidArea(new Dimension(0, 4)));
+			control.add(c);
+		}
+	}
+
+	public static void layoutInRow(JPanel panel, JComponent first, JComponent... components) {
+		panel.setLayout(new BoxLayout(panel, BoxLayout.LINE_AXIS));
+		panel.setAlignmentX(JPanel.LEFT_ALIGNMENT);
+
+		panel.add(first);
+		for (JComponent c : components) {
+			panel.add(Box.createRigidArea(new Dimension(8, 0)));
+			panel.add(c);
+		}
+	}
+
+	public static void addLeftToLabel(Control control, JComponent... components) {
+
+		JLabel label = new JLabel(getBestName(control.getParam()));
+		label.setToolTipText(control.getToolTipText());
+		label.setHorizontalAlignment(JLabel.LEFT);
+		Dimension labelSize = new Dimension(150, 30);
+		label.setMaximumSize(labelSize);
+		label.setMinimumSize(labelSize);
+		label.setPreferredSize(labelSize);
+
+		layoutInRow(control, label, components);
+
+	}
 }
Index: java/main/src/main/java/com/framsticks/gui/MainFrame.java
===================================================================
--- java/main/src/main/java/com/framsticks/gui/MainFrame.java	(revision 100)
+++ java/main/src/main/java/com/framsticks/gui/MainFrame.java	(revision 101)
@@ -73,150 +73,4 @@
 	}
 
-	// private void onConnectionEstablished() {
-		// assert isActive();
-
-		// addNodeActionToTreePopupMenu("Open in console", new NodeAction() {
-			// @Override
-			// public void actionPerformed(TreeNode treeNode) {
-				// assert isActive();
-			// }
-		// });
-
-
-
-
-	// 	// browser.addNodeActionToTreePopupMenu("Resolve recursively", new NodeAction() {
-	// 	// 	@Override
-	// 	// 	public void actionPerformed(TreeNode treeNode) {
-	// 	// 		final Child child = treeNode.getChild();
-	// 	// 		//server.invokeLater(new Runnable() {
-	// 		// 		@Override
-	// 		// 		public void run() {
-	// 		// 			resolveRecursively(child);
-	// 		// 		}
-	// 		// 	});
-	// 	// }
-	// 	// });
-
-
-		// // addNodeActionToTreePopupMenu("Store in file", new NodeAction() {
-		// //     @Override
-		// //     public void actionPerformed(final TreeNode treeNode) {
-		// //         // final Node node = treeNode.getNode();
-		// //         // server.invokeLater(new Runnable() {
-		// //         //     @Override
-		// //         //     public void run() {
-		// //         //         try {
-		// //         //             log.info("storing");
-		// //         //             //File file = new File();
-		// //         //             StoreStream stream = new StoreStream(file, server.getManager());
-		// //         //             stream.store(node);
-		// //         //         } catch (FileNotFoundException e) {
-		// //         //             e.printStackTrace();
-		// //         //         }
-		// //         //     }
-		// //         // });
-		// //     }
-		// // });
-
-
-	// 	// arguments.forEach("resolve", new UnaryFunctor<Boolean, String>() {
-	// 	// 	@Override
-	// 	// 	public Boolean call(final String s) {
-	// 	// 		server.getManager().resolvePath(s, new StateFunctor() {
-	// 	// 			@Override
-	// 	// 			public void call(Exception e) {
-	// 	// 				if (e != null) {
-	// 	// 					log.error("failed to resolve: {}", s);
-	// 	// 					return;
-	// 	// 				}
-	// 	// 				log.info("succeeded to resolve: {}", s);
-	// 	// 			}
-	// 	// 		});
-	// 	// 		return true;
-	// 	// 	}
-	// 	// });
-
-
-	// 	// arguments.forEach("console", new UnaryFunctor<Boolean, String>() {
-	// 	// 	@Override
-	// 	// 	public Boolean call(String s) {
-	// 	// 		showConsoleFrame().setCommandLine(s);
-	// 	// 		return true;
-	// 	// 	}
-	// 	// });
-	// }
-
-
-	// /**
-	//  * Creates panel with start, step, stop buttons.
-	//  */
-	// @Override
-	// protected JPanel createLeftTopPanel() {
-	// 	assert isActive();
-	// 	Dimension buttonDimension = new Dimension(45, 45);
-
-	// 	JPanel panel = new JPanel();
-
-	// 	stop = new JButton(ImageProvider.loadImage(ImageProvider.SIM_STOP));
-	// 	stop.addActionListener(new ActionListener() {
-
-	// 		public void actionPerformed(ActionEvent e) {
-	// 			setSimulationRunning(false);
-	// 		}
-
-	// 	});
-
-	// 	stop.setPreferredSize(buttonDimension);
-	// 	stop.setToolTipText("Simulation Stop");
-	// 	stop.setEnabled(false);
-
-	// 	step = new JButton(ImageProvider.loadImage(ImageProvider.SIM_STEP));
-	// 	/*
-	// 	step.addActionListener(new ActionListener() {
-
-	// 		public void actionPerformed(ActionEvent e) {
-	// 			connection.send(new CallQuery().setMethod("step").setPath("/simulator"));
-	// 		}
-
-	// 	});
-	// 	*/
-
-	// 	step.setPreferredSize(buttonDimension);
-	// 	step.setToolTipText("Simulation Step");
-
-	// 	start = new JButton(ImageProvider.loadImage(ImageProvider.SIM_START));
-	// 	start.addActionListener(new ActionListener() {
-
-	// 		public void actionPerformed(ActionEvent e) {
-	// 			setSimulationRunning(true);
-	// 		}
-
-	// 	});
-
-	// 	start.setPreferredSize(buttonDimension);
-	// 	start.setToolTipText("Start Simulation");
-
-	// 	JPanel buttonPanel = new JPanel();
-	// 	buttonPanel.setLayout(new BoxLayout(buttonPanel, BoxLayout.LINE_AXIS));
-	// 	buttonPanel.add(Box.createHorizontalStrut(5));
-	// 	buttonPanel.add(stop);
-	// 	buttonPanel.add(Box.createHorizontalStrut(10));
-	// 	buttonPanel.add(step);
-	// 	buttonPanel.add(Box.createHorizontalStrut(10));
-	// 	buttonPanel.add(start);
-	// 	buttonPanel.add(Box.createHorizontalStrut(5));
-
-	// 	buttonPanel.setBorder(BorderFactory.createTitledBorder(BorderFactory
-	// 			.createRaisedBevelBorder(), "Simulation Control",
-	// 			TitledBorder.CENTER, TitledBorder.DEFAULT_POSITION
-	// 			));
-
-	// 	panel.setLayout(new BoxLayout(panel, BoxLayout.LINE_AXIS));
-	// 	panel.add(Box.createHorizontalGlue());
-	// 	panel.add(buttonPanel);
-	// 	panel.add(Box.createHorizontalGlue());
-	// 	return panel;
-	// }
 
 	/**
@@ -248,14 +102,4 @@
 	}
 
-	// public ConsoleFrame showConsoleFrame() {
-	// 	assert isActive();
-	// 	/*
-	// 	ConsoleFrame consoleFrame = new ConsoleFrame(connection);
-	// 	consoleFrame.show(MainFrame.this);
-	// 	return consoleFrame;
-	// 	*/
-	// 	return null;
-	// }
-
 	/**
 	 * Closes connection with manager.
Index: java/main/src/main/java/com/framsticks/gui/ModifiablePanel.java
===================================================================
--- java/main/src/main/java/com/framsticks/gui/ModifiablePanel.java	(revision 100)
+++ java/main/src/main/java/com/framsticks/gui/ModifiablePanel.java	(revision 101)
@@ -9,4 +9,5 @@
 import java.awt.event.ActionEvent;
 import java.awt.event.ActionListener;
+import static com.framsticks.core.TreeOperations.*;
 
 /**
@@ -24,4 +25,5 @@
 	protected final JLabel label;
 	protected final JButton applyButton;
+	protected final JButton revertButton;
 	protected final JPanel centerPanel;
 
@@ -32,6 +34,4 @@
 		log.debug("create panel for type: {}", className);
 
-
-
 		JPanel pageEndPanel = new JPanel();
 		pageEndPanel.setLayout(new BoxLayout(pageEndPanel, BoxLayout.X_AXIS));
@@ -41,4 +41,15 @@
 		applyButton.setName("apply");
 
+		revertButton = new JButton("Revert");
+		revertButton.setName("revert");
+
+		pageEndPanel.add(applyButton);
+		pageEndPanel.add(Box.createHorizontalStrut(10));
+
+		pageEndPanel.add(revertButton);
+		pageEndPanel.add(Box.createHorizontalStrut(10));
+
+		pageEndPanel.setPreferredSize(new Dimension(0, 30));
+
 		applyButton.addActionListener(new ActionListener() {
 			public void actionPerformed(ActionEvent e) {
@@ -46,8 +57,10 @@
 			}
 		});
-		pageEndPanel.add(applyButton);
 
-		pageEndPanel.add(Box.createHorizontalStrut(10));
-		pageEndPanel.setPreferredSize(new Dimension(0, 30));
+		revertButton.addActionListener(new ActionListener() {
+			public void actionPerformed(ActionEvent e) {
+				revertChanges();
+			}
+		});
 
 		label = new JLabel();
@@ -67,4 +80,5 @@
 
 	protected abstract void applyChanges();
+	protected abstract void revertChanges();
 
 	protected void setupContentComponent(Component contentComponent) {
@@ -73,3 +87,10 @@
 	}
 
+	protected void refreshControlButtons() {
+		assert frame.isActive();
+		boolean hasChanges = hasSideNotes(getTree(), getCurrentObject(), treeAtFrame.getUserChangesKey());
+		applyButton.setEnabled(hasChanges);
+		revertButton.setEnabled(hasChanges);
+	}
+
 }
Index: java/main/src/main/java/com/framsticks/gui/NodeAtFrame.java
===================================================================
--- java/main/src/main/java/com/framsticks/gui/NodeAtFrame.java	(revision 100)
+++ 	(revision )
@@ -1,18 +1,0 @@
-package com.framsticks.gui;
-
-import java.util.IdentityHashMap;
-import java.util.Map;
-
-import com.framsticks.gui.controls.ValueControl;
-
-public class NodeAtFrame {
-
-	protected final Map<ValueControl, Object> localChanges = new IdentityHashMap<>();
-
-	/**
-	 *
-	 */
-	public NodeAtFrame() {
-	}
-
-}
Index: java/main/src/main/java/com/framsticks/gui/ObjectPanel.java
===================================================================
--- java/main/src/main/java/com/framsticks/gui/ObjectPanel.java	(revision 100)
+++ java/main/src/main/java/com/framsticks/gui/ObjectPanel.java	(revision 101)
@@ -5,8 +5,6 @@
 import com.framsticks.gui.controls.ControlOwner;
 import com.framsticks.gui.controls.ValueControl;
-import com.framsticks.gui.controls.ValueControlListener;
 import com.framsticks.params.Access;
 import com.framsticks.params.Param;
-import com.framsticks.params.ValueParam;
 
 import org.apache.logging.log4j.Logger;
@@ -16,9 +14,11 @@
 
 import java.util.Collection;
-import java.util.IdentityHashMap;
+import java.util.HashMap;
 import java.util.Map;
 import static com.framsticks.util.lang.Containers.filterInstanceof;
 
 import com.framsticks.util.FramsticksException;
+
+import static com.framsticks.core.TreeOperations.*;
 
 @SuppressWarnings("serial")
@@ -27,6 +27,6 @@
 	private static final Logger log = LogManager.getLogger(ObjectPanel.class);
 
-	final protected Map<Param, Control> components = new IdentityHashMap<Param, Control>();
-	final protected Map<ValueParam, ValueControl> valueControls = new IdentityHashMap<ValueParam, ValueControl>();
+	final protected Map<String, Control> controls = new HashMap<String, Control>();
+	final protected Map<String, ValueControl> valueControls = new HashMap<String, ValueControl>();
 
 	protected final JPanel contentPanel;
@@ -38,24 +38,13 @@
 		contentPanel = new JPanel();
 		scrollPane = new JScrollPane(contentPanel);
-		contentPanel.setLayout(new BoxLayout(contentPanel, BoxLayout.PAGE_AXIS));
+
 		setupContentComponent(scrollPane);
 
-		Gui.fillWithControls(this, params, components, Control.class);
+		Gui.fillWithControls(this, contentPanel, params, controls, Control.class);
 		setName(framsClass.getId());
 
-		for (final ValueControl c : filterInstanceof(components.values(), ValueControl.class)) {
-			valueControls.put(c.getParam(), c);
+		for (final ValueControl c : filterInstanceof(controls.values(), ValueControl.class)) {
+			valueControls.put(c.getParam().getId(), c);
 			c.setUserEnabled(true);
-			c.setListener(new ValueControlListener() {
-				@Override
-				public boolean onChange(Object newValue) {
-					if (currentPath == null) {
-						return true;
-					}
-					boolean result = treeAtFrame.changeValue(currentPath.assureResolved().getTopObject(), c, newValue);
-					refreshControlButtons();
-					return result;
-				}
-			});
 		}
 
@@ -68,10 +57,14 @@
 		assert frame.isActive();
 		assert currentPath != null;
-		treeAtFrame.pushLocalChanges(currentPath);
+		treeAtFrame.pushUserChangesToTree(currentPath);
+		refreshControlButtons();
 	}
 
-	protected void refreshControlButtons() {
-		assert frame.isActive();
-		applyButton.setEnabled(treeAtFrame.hasLocalChanges(currentPath.getTopObject()));
+
+	@Override
+	protected void revertChanges() {
+		assert currentPath != null;
+		removeSideNote(currentPath, treeAtFrame.getUserChangesKey());
+		pullValuesFromLocalToUser(bindAccess(currentPath));
 	}
 
@@ -81,23 +74,25 @@
 		log.debug("refreshing components");
 
-		final Map<ValueControl, Object> values = new IdentityHashMap<ValueControl, Object>();
-		for (Map.Entry<ValueParam, ValueControl> e : valueControls.entrySet()) {
-			values.put(e.getValue(), access.get(e.getKey().getId(), Object.class));
+		UserChanges userChanges = getSideNote(currentPath, treeAtFrame.getUserChangesKey());
+
+
+		for (Map.Entry<String, ValueControl> e : valueControls.entrySet()) {
+			String id = e.getKey();
+			Object value;
+			if (userChanges != null && userChanges.changes.containsKey(id)) {
+				value = userChanges.changes.get(id);
+			} else {
+				value = access.get(id, Object.class);
+			}
+
+			e.getValue().pushValueToUserInterface(value);
 		}
 
-
-		NodeAtFrame nodeAtFrame = treeAtFrame.getLocalInfo(currentPath.getTopObject());
-		if (nodeAtFrame != null) {
-			for (Map.Entry<ValueControl, Object> e : nodeAtFrame.localChanges.entrySet()) {
-				values.put(e.getKey(), e.getValue());
-			}
+		for (Map.Entry<String, Control> e : controls.entrySet()) {
+			e.getValue().refreshState();
 		}
 
-		for (Map.Entry<ValueControl, Object> e : values.entrySet()) {
-			e.getKey().pushValueToUserInterface(e.getValue());
-		}
 		refreshControlButtons();
-		ObjectPanel.this.revalidate();
-
+		// ObjectPanel.this.revalidate();
 	}
 
@@ -105,9 +100,4 @@
 	public String getTitle() {
 		return "Properties";
-	}
-
-	@Override
-	public JPanel getPanelForControls() {
-		return contentPanel;
 	}
 
@@ -122,3 +112,13 @@
 	}
 
+	@Override
+	public boolean onValueChange(ValueControl control, Object newValue) {
+		if (currentPath == null) {
+			return true;
+		}
+		boolean result = treeAtFrame.changeValue(currentPath.assureResolved().getTopObject(), control, newValue);
+		refreshControlButtons();
+		return result;
+	}
+
 }
Index: java/main/src/main/java/com/framsticks/gui/StatusBar.java
===================================================================
--- java/main/src/main/java/com/framsticks/gui/StatusBar.java	(revision 100)
+++ java/main/src/main/java/com/framsticks/gui/StatusBar.java	(revision 101)
@@ -15,8 +15,9 @@
 import com.framsticks.util.FramsticksException;
 import com.framsticks.util.dispatching.Dispatcher;
+import com.framsticks.util.dispatching.Dispatching;
 import com.framsticks.util.dispatching.ExceptionResultHandler;
 import com.framsticks.util.dispatching.RunAt;
 
-public class StatusBar implements ExceptionResultHandler, Dispatcher<StatusBar> {
+public class StatusBar implements ExceptionResultHandler {
 	private static final Logger log = LogManager.getLogger(StatusBar.class);
 
@@ -24,14 +25,31 @@
 	protected JPanel swing;
 	protected ExceptionResultHandler exceptionHandler;
+	protected final Dispatcher<?> dispatcher;
 
 	/**
 	 *
 	 */
-	public StatusBar() {
+	public StatusBar(Dispatcher<?> dispatcher) {
+		this.dispatcher = dispatcher;
+	}
+
+	@SuppressWarnings({"rawtypes", "unchecked"})
+	public void showInfo(final Object value) {
+		Dispatching.dispatchIfNotActive(dispatcher, new RunAt(this) {
+
+			@Override
+			protected void runAt() {
+				String text = value.toString();
+				log.info("info: {}", text);
+				statusBar.setText(text);
+
+			}
+		});
 	}
 
 	@Override
+	@SuppressWarnings({"rawtypes", "unchecked"})
 	public void handle(final FramsticksException exception) {
-		dispatch(new RunAt<StatusBar>(this) {
+		dispatcher.dispatch(new RunAt(this) {
 
 			@Override
@@ -58,14 +76,4 @@
 	}
 
-	@Override
-	public boolean isActive() {
-		return SwingDispatcher.getInstance().isActive();
-	}
-
-	@Override
-	public void dispatch(RunAt<? extends StatusBar> runnable) {
-		SwingDispatcher.getInstance().dispatch(runnable);
-	}
-
 	/**
 	 * @return the swing
Index: java/main/src/main/java/com/framsticks/gui/SwingDispatcher.java
===================================================================
--- java/main/src/main/java/com/framsticks/gui/SwingDispatcher.java	(revision 100)
+++ java/main/src/main/java/com/framsticks/gui/SwingDispatcher.java	(revision 101)
@@ -1,11 +1,7 @@
 package com.framsticks.gui;
 
-import java.awt.event.ActionEvent;
-import java.awt.event.ActionListener;
 
 import com.framsticks.util.dispatching.AbstractJoinable;
 import com.framsticks.util.dispatching.Dispatcher;
-import com.framsticks.util.dispatching.JoinableDispatcher;
-import com.framsticks.util.dispatching.Task;
 import com.framsticks.util.dispatching.ThrowExceptionHandler;
 
@@ -16,5 +12,5 @@
  * @author Piotr Sniegowski
  */
-public class SwingDispatcher<C> extends AbstractJoinable implements JoinableDispatcher<C> {
+public class SwingDispatcher<C> extends AbstractJoinable implements Dispatcher<C> {
 
 	@SuppressWarnings("rawtypes")
@@ -42,20 +38,4 @@
 	@Override
 	public final void dispatch(RunAt<? extends C> runnable) {
-		if (runnable instanceof Task) {
-			final Task<?> task = (Task<?>) runnable;
-			Timer timer = new Timer(0, null);
-			timer.addActionListener(new ActionListener() {
-
-				@Override
-				public void actionPerformed(ActionEvent event) {
-					task.run();
-				}
-
-			});
-			timer.setInitialDelay((int) (task.getMoment() - System.currentTimeMillis()));
-			timer.setRepeats(false);
-			timer.start();
-			return;
-		}
 		SwingUtilities.invokeLater(runnable);
 	}
@@ -73,5 +53,5 @@
 	@Override
 	protected void joinableInterrupt() {
-		finish();
+		finishJoinable();
 	}
 
Index: java/main/src/main/java/com/framsticks/gui/TreeAtFrame.java
===================================================================
--- java/main/src/main/java/com/framsticks/gui/TreeAtFrame.java	(revision 100)
+++ java/main/src/main/java/com/framsticks/gui/TreeAtFrame.java	(revision 101)
@@ -4,11 +4,13 @@
 import org.apache.logging.log4j.LogManager;
 
+import com.framsticks.core.SideNoteKey;
 import com.framsticks.core.Tree;
 import com.framsticks.core.Node;
 import com.framsticks.core.Path;
-import com.framsticks.core.TreeOperations;
+import static com.framsticks.core.TreeOperations.*;
 import com.framsticks.gui.controls.ValueControl;
 import com.framsticks.params.CompositeParam;
 import com.framsticks.params.FramsClass;
+import com.framsticks.params.PrimitiveParam;
 
 import java.util.*;
@@ -30,4 +32,6 @@
 	protected Node rootNode;
 
+	protected final SideNoteKey<UserChanges> userChangesKey = SideNoteKey.make(UserChanges.class);
+
 	public TreeAtFrame(Tree tree, Frame frame) {
 		this.frame = frame;
@@ -44,4 +48,11 @@
 	public Tree getTree() {
 		return tree;
+	}
+
+	/**
+	 * @return the userChangesKey
+	 */
+	public SideNoteKey<UserChanges> getUserChangesKey() {
+		return userChangesKey;
 	}
 
@@ -71,5 +82,5 @@
 		if (panels.isEmpty()) {
 			panel = new EmptyTreePanel(parameters);
-		} else 	if (panels.size() == 1) {
+		} else	if (panels.size() == 1) {
 			panel = panels.get(0);
 		} else {
@@ -83,49 +94,29 @@
 	}
 
-
-	public boolean hasLocalChanges(Object object) {
-		NodeAtFrame nodeAtFrame = tree.getSideNote(object, this, NodeAtFrame.class);
-		if (nodeAtFrame == null) {
-			return false;
-		}
-		return !nodeAtFrame.localChanges.isEmpty();
-	}
-
-	public NodeAtFrame assureLocalInfo(Object object) {
-		assert frame.isActive();
-		NodeAtFrame nodeAtFrame = tree.getSideNote(object, this, NodeAtFrame.class);
-
-		if (nodeAtFrame == null) {
-			nodeAtFrame = new NodeAtFrame();
-			// log.debug();
-			tree.putSideNote(object, this, nodeAtFrame);
-		}
-		return nodeAtFrame;
-	}
-
-	public NodeAtFrame getLocalInfo(Object object) {
-		return tree.getSideNote(object, this, NodeAtFrame.class);
-	}
-
 	public boolean changeValue(Object object, ValueControl component, Object newValue) {
 		log.debug("changing value of {} to '{}'", component, newValue);
 
-		assureLocalInfo(object).localChanges.put(component, newValue);
+		getOrCreateSideNote(tree, object, userChangesKey).changes.put(component.getParam().getId(), newValue);
 
 		return true;
 	}
 
-	public void pushLocalChanges(Path path) {
+
+	public void pushUserChangesToTree(final Path path) {
 		assert frame.isActive();
 		path.assureResolved();
 
-		NodeAtFrame nodeAtFrame = getLocalInfo(path.getTopObject());
-		if (nodeAtFrame == null) {
+		final UserChanges userChanges = getSideNote(path, userChangesKey);
+		if (userChanges == null) {
 			return;
 		}
-		for (Map.Entry<ValueControl, Object> e : nodeAtFrame.localChanges.entrySet()) {
-			TreeOperations.set(path, e.getKey().getParam(), e.getValue(), new FutureHandler<Integer>(frame) {
+		removeSideNote(path, userChangesKey);
+
+		for (final Map.Entry<String, Object> e : userChanges.changes.entrySet()) {
+			set(path, getParam(path, e.getKey(), PrimitiveParam.class), e.getValue(), new FutureHandler<Integer>(frame) {
 				@Override
 				protected void result(Integer flag) {
+					assert frame.isActive();
+					userChanges.changes.remove(e.getKey());
 				}
 			});
Index: java/main/src/main/java/com/framsticks/gui/TreePanel.java
===================================================================
--- java/main/src/main/java/com/framsticks/gui/TreePanel.java	(revision 100)
+++ java/main/src/main/java/com/framsticks/gui/TreePanel.java	(revision 101)
@@ -2,5 +2,6 @@
 
 import com.framsticks.core.Path;
-import com.framsticks.core.TreeOperations;
+import com.framsticks.core.Tree;
+import static com.framsticks.core.TreeOperations.*;
 import com.framsticks.gui.tree.AbstractNode;
 import com.framsticks.gui.tree.TreeNode;
@@ -28,5 +29,5 @@
 		}
 		setCurrentPath(path);
-		pullValuesFromLocalToUser(TreeOperations.bindAccess(path));
+		pullValuesFromLocalToUser(bindAccess(path));
 	}
 
@@ -79,5 +80,12 @@
 		return treeAtFrame;
 	}
-
+	public final Object getCurrentObject() {
+		assert currentPath != null;
+		return currentPath.getTopObject();
+	}
+	public final Tree getTree() {
+		assert currentPath != null;
+		return currentPath.getTree();
+	}
 
 	public final String getClassName() {
Index: java/main/src/main/java/com/framsticks/gui/UserChanges.java
===================================================================
--- java/main/src/main/java/com/framsticks/gui/UserChanges.java	(revision 101)
+++ java/main/src/main/java/com/framsticks/gui/UserChanges.java	(revision 101)
@@ -0,0 +1,14 @@
+package com.framsticks.gui;
+
+import java.util.IdentityHashMap;
+import java.util.Map;
+
+
+public class UserChanges {
+
+	protected final Map<String, Object> changes = new IdentityHashMap<>();
+
+	public UserChanges() {
+	}
+
+}
Index: java/main/src/main/java/com/framsticks/gui/controls/CheckBoxControl.java
===================================================================
--- java/main/src/main/java/com/framsticks/gui/controls/CheckBoxControl.java	(revision 100)
+++ java/main/src/main/java/com/framsticks/gui/controls/CheckBoxControl.java	(revision 101)
@@ -10,4 +10,5 @@
 import org.apache.logging.log4j.LogManager;
 
+import com.framsticks.gui.Gui;
 import com.framsticks.params.types.BooleanParam;
 
@@ -31,5 +32,5 @@
 		});
 		this.setMaximumSize(new Dimension(Integer.MAX_VALUE, Control.LINE_HEIGHT));
-		addAsOnlyChild(checkBox);
+		Gui.addLeftToLabel(this, checkBox);
 	}
 
Index: java/main/src/main/java/com/framsticks/gui/controls/Control.java
===================================================================
--- java/main/src/main/java/com/framsticks/gui/controls/Control.java	(revision 100)
+++ java/main/src/main/java/com/framsticks/gui/controls/Control.java	(revision 101)
@@ -1,12 +1,14 @@
 package com.framsticks.gui.controls;
 
-import java.awt.BorderLayout;
+// import java.awt.BorderLayout;
 
-import javax.swing.JComponent;
+
 import javax.swing.JPanel;
 
+import com.framsticks.core.Path;
 import com.framsticks.params.ParamFlags;
 import com.framsticks.params.Param;
 import com.framsticks.util.FramsticksException;
+import com.framsticks.util.Misc;
 import com.framsticks.util.dispatching.ExceptionResultHandler;
 
@@ -57,11 +59,7 @@
 
 	public final boolean isReadonly() {
-		return !userEnabled || param.hasFlag(ParamFlags.READONLY);
+		return !userEnabled || param.hasFlag(ParamFlags.READONLY) || param.hasFlag(ParamFlags.USERREADONLY);
 	}
 
-	protected void addAsOnlyChild(JComponent component) {
-		this.setLayout(new BorderLayout());
-		this.add(component, BorderLayout.CENTER);
-	}
 
 	@Override
@@ -74,3 +72,14 @@
 		owner.handle(exception);
 	}
+
+	public Path getCurrentPath() {
+		return owner.getCurrentPath();
+	}
+
+	public Path assureCurrentPath() {
+		return Misc.throwIfNull(owner.getCurrentPath());
+	}
+
+	public void refreshState() {
+	}
 }
Index: java/main/src/main/java/com/framsticks/gui/controls/ControlOwner.java
===================================================================
--- java/main/src/main/java/com/framsticks/gui/controls/ControlOwner.java	(revision 100)
+++ java/main/src/main/java/com/framsticks/gui/controls/ControlOwner.java	(revision 101)
@@ -1,5 +1,4 @@
 package com.framsticks.gui.controls;
 
-import javax.swing.JPanel;
 
 import com.framsticks.core.Path;
@@ -9,7 +8,7 @@
 public interface ControlOwner extends ExceptionResultHandler {
 
-	public JPanel getPanelForControls();
 	public Path getCurrentPath();
 	public Frame getFrame();
+	public boolean onValueChange(ValueControl control, Object newValue);
 
 }
Index: java/main/src/main/java/com/framsticks/gui/controls/EnumControl.java
===================================================================
--- java/main/src/main/java/com/framsticks/gui/controls/EnumControl.java	(revision 100)
+++ java/main/src/main/java/com/framsticks/gui/controls/EnumControl.java	(revision 101)
@@ -1,4 +1,5 @@
 package com.framsticks.gui.controls;
 
+import com.framsticks.gui.Gui;
 import com.framsticks.params.types.EnumParam;
 import com.framsticks.util.lang.Numbers;
@@ -35,5 +36,5 @@
 			}
 		});
-		addAsOnlyChild(list);
+		Gui.addLeftToLabel(this, list);
 	}
 
Index: java/main/src/main/java/com/framsticks/gui/controls/EventControl.java
===================================================================
--- java/main/src/main/java/com/framsticks/gui/controls/EventControl.java	(revision 100)
+++ java/main/src/main/java/com/framsticks/gui/controls/EventControl.java	(revision 101)
@@ -1,11 +1,27 @@
 package com.framsticks.gui.controls;
 
-import java.awt.Dimension;
 import java.awt.event.ActionEvent;
 import java.awt.event.ActionListener;
-
-import javax.swing.JButton;
-
+import java.text.SimpleDateFormat;
+import java.util.ArrayList;
+import java.util.Date;
+import java.util.List;
+
+
+
+import com.framsticks.core.Path;
+import com.framsticks.core.SideNoteKey;
+import com.framsticks.gui.Frame;
+import com.framsticks.gui.Gui;
+import com.framsticks.gui.table.AbstractTableModel;
+import com.framsticks.params.EventListener;
 import com.framsticks.params.types.EventParam;
+import com.framsticks.util.FramsticksUnsupportedOperationException;
+import com.framsticks.util.dispatching.Dispatching;
+import com.framsticks.util.dispatching.FutureHandler;
+import com.framsticks.util.dispatching.RunAt;
+import com.framsticks.util.lang.Pair;
+
+import static com.framsticks.core.TreeOperations.*;
 
 /**
@@ -13,42 +29,172 @@
  */
 @SuppressWarnings("serial")
-public class EventControl extends Control {
+public class EventControl extends HistoryControl {
 	// private static final Logger log = LogManager.getLogger(EventControl.class.getName());
 
-	protected final JButton button;
-	boolean subscribed = true;
+	@SuppressWarnings("rawtypes")
+	protected final SideNoteKey<EventListener> listenerKey = SideNoteKey.make(EventListener.class);
+
+	public static class History {
+		protected final List<Pair<Date, Object>> entries = new ArrayList<>();
+	};
+
+	protected final SideNoteKey<History> historyKey = SideNoteKey.make(History.class);
+	protected TableModel tableModel;
 
 	public EventControl(final EventParam eventParam) {
 		super(eventParam);
 
-		button = new JButton("subscribe");
-		this.add(button);
-		this.setMaximumSize(new Dimension(Integer.MAX_VALUE, Control.LINE_HEIGHT));
-
-		button.addActionListener(new ActionListener() {
+		mainButton.setText("Subscribe");
+		mainButton.setName("subscription");
+
+		tableModel = new TableModel();
+		resultsTable.setModel(tableModel);
+
+		mainButton.addActionListener(new ActionListener() {
 			@Override
 			public void actionPerformed(ActionEvent e) {
-				if (subscribed) {
-					//panel.getCurrentTreeNode().unsubscribe(eventParam);
-				} else {
-					//panel.getCurrentTreeNode().subscribe(eventParam);
+				final Path path = assureCurrentPath();
+				EventListener<?> listener = getSideNote(path, listenerKey);
+				if (listener != null) {
+					removeSideNote(path, listenerKey);
+					refreshState();
+					path.getTree().removeListener(path, getParam(), listener, FutureHandler.doNothing(Void.class, owner.getFrame()));
+					return;
 				}
+
+				final EventListener<Object> newListener = new EventListener<Object>() {
+					@Override
+					public void action(final Object argument) {
+						/** actions can be invoked from anywhere */
+						Dispatching.dispatchIfNotActive(owner.getFrame(), new RunAt<Frame>(owner.getFrame()) {
+
+							@Override
+							protected void runAt() {
+								getOrCreateSideNote(path.getTree(), path.getTopObject(), historyKey).entries.add(Pair.make(new Date(), argument));
+								refreshTable();
+							}
+						});
+						// owner.getFrame().getStatusBar().showInfo("event " + param + " happened: " + argument);
+					}
+				};
+
+				path.getTree().addListener(path, getParam(), newListener, Object.class, new FutureHandler<Void>(owner.getFrame()) {
+
+					@Override
+					protected void result(Void result) {
+						putSideNote(path, listenerKey, newListener);
+						refreshState();
+					}
+				});
+
 			}
 		});
 
+		updateFoldState();
+		Gui.setupTitledControl(this, controlRow, resultsScrollPane);
 	}
 
 	@Override
 	protected void updateEnabled(boolean enabled) {
-		button.setEnabled(enabled);
-	}
-
-	/*
-	@Override
-	public void refresh() {
-		subscribed = panel.getCurrentTreeNode().hasSubscribed((EventParam)param);
-		button.setText(subscribed ? "unsubscribe" : "subscribe");
-		this.revalidate();
-	}
-	*/
+		mainButton.setEnabled(enabled);
+	}
+
+	public boolean isListening() {
+		return hasSideNote(getCurrentPath(), listenerKey);
+	}
+
+	protected void refreshButtonState() {
+		mainButton.setText(isListening() ? "Don't listen" : "Listen");
+	}
+
+	@Override
+	protected void refreshTable() {
+		History history = getSideNote(assureCurrentPath(), historyKey);
+		tableModel.entries = history != null ? history.entries : null;
+		tableModel.refreshAll();
+	}
+
+	@Override
+	protected void clearTable() {
+		History history = getSideNote(assureCurrentPath(), historyKey);
+		if (history != null) {
+			history.entries.clear();
+		}
+		refreshTable();
+	}
+
+	public void refreshState() {
+		refreshButtonState();
+		refreshTable();
+	}
+
+	@Override
+	public EventParam getParam() {
+		return (EventParam) param;
+	}
+
+
+	public class TableModel extends AbstractTableModel {
+
+		List<Pair<Date, Object>> entries;
+		SimpleDateFormat format = new SimpleDateFormat("HH:mm:ss:SSS");
+
+		@Override
+		public Class<?> getColumnClass(int columnIndex) {
+			return String.class;
+		}
+
+		@Override
+		public int getColumnCount() {
+			return 2;
+		}
+
+		@Override
+		public String getColumnName(int columnIndex) {
+			return columnIndex == 0 ? "Occured at" : "Argument";
+		}
+
+		@Override
+		public int getRowCount() {
+			if (entries == null) {
+				return 0;
+			}
+			if (isFolded()) {
+				return entries.isEmpty() ? 0 : 1;
+			}
+			return entries.size();
+		}
+
+		@Override
+		public Object getValueAt(int rowIndex, int columnIndex) {
+			if (entries == null) {
+				return null;
+			}
+			Pair<Date, Object> entry;
+			if (isFolded()) {
+				if (rowIndex > 0) {
+					return null;
+				}
+				if (entries.isEmpty()) {
+					return null;
+				}
+				entry = entries.get(entries.size() - 1);
+			} else {
+				entry = entries.get(rowIndex);
+			}
+			return columnIndex == 0 ? format.format(entry.first) : entry.second;
+		}
+
+		@Override
+		public boolean isCellEditable(int rowIndex, int columnIndex) {
+			return false;
+		}
+
+		@Override
+		public void setValueAt(Object value, int rowIndex, int columnIndex) {
+			throw new FramsticksUnsupportedOperationException().msg("setting value in event history");
+		}
+
+
+	}
 }
Index: java/main/src/main/java/com/framsticks/gui/controls/HistoryControl.java
===================================================================
--- java/main/src/main/java/com/framsticks/gui/controls/HistoryControl.java	(revision 101)
+++ java/main/src/main/java/com/framsticks/gui/controls/HistoryControl.java	(revision 101)
@@ -0,0 +1,99 @@
+package com.framsticks.gui.controls;
+
+import java.awt.Dimension;
+import java.awt.event.ActionEvent;
+import java.awt.event.ActionListener;
+
+import javax.swing.JButton;
+import javax.swing.JCheckBox;
+import javax.swing.JPanel;
+import javax.swing.JScrollPane;
+import javax.swing.JTable;
+
+import com.framsticks.gui.Gui;
+import com.framsticks.params.Param;
+import com.framsticks.util.swing.TooltipConstructor;
+
+@SuppressWarnings("serial")
+public abstract class HistoryControl extends Control {
+
+	protected final JPanel controlRow;
+	protected final JButton mainButton;
+	protected final JCheckBox foldingCheckbox;
+	protected final JButton clearButton;
+
+	protected final JScrollPane resultsScrollPane;
+	protected final JTable resultsTable;
+
+	public HistoryControl(Param param) {
+		super(param);
+
+		this.setToolTipText(new TooltipConstructor()
+			.append("name", param.getName())
+			.append("id", param.getId())
+			.append("help", param.getHelp())
+			.build());
+
+		controlRow = new JPanel();
+		controlRow.setName("control_row");
+
+		mainButton = new JButton();
+
+		mainButton.setMaximumSize(new Dimension(Integer.MAX_VALUE, LINE_HEIGHT));
+
+		clearButton = new JButton("Clear");
+		clearButton.setName("clear");
+
+		foldingCheckbox = new JCheckBox("Fold");
+		foldingCheckbox.setName("folding");
+		foldingCheckbox.setSelected(true);
+
+		foldingCheckbox.addActionListener(new ActionListener() {
+			@Override
+			public void actionPerformed(ActionEvent e) {
+				updateFoldState();
+			}
+		});
+
+
+		clearButton.addActionListener(new ActionListener() {
+			@Override
+			public void actionPerformed(ActionEvent e) {
+				clearTable();
+			}
+		});
+
+
+
+		Gui.layoutInRow(controlRow, mainButton, foldingCheckbox, clearButton);
+
+		resultsTable = new JTable();
+		resultsScrollPane = new JScrollPane(resultsTable);
+		resultsTable.setShowGrid(false);
+
+
+	}
+
+	public boolean isFolded() {
+		return foldingCheckbox.isSelected();
+	}
+
+	protected void updateFoldState() {
+		int lines = isFolded() ? 1 : 5;
+		int height = lines * LINE_HEIGHT;
+		// resultsTable.getTableHeader().setVisible(!isFolded());
+		resultsScrollPane.setColumnHeaderView(!isFolded() ? resultsTable.getTableHeader() : null);
+		resultsScrollPane.setMaximumSize(new Dimension(Integer.MAX_VALUE, height));
+		resultsScrollPane.setPreferredSize(new Dimension(0, height));
+		if (owner != null) {
+			refreshTable();
+		}
+		this.revalidate();
+		// foldingButton.setText(isFolded ? "Unfold" : "Fold");
+	}
+
+	protected abstract void refreshTable();
+
+	protected abstract void clearTable();
+
+}
Index: java/main/src/main/java/com/framsticks/gui/controls/ProcedureControl.java
===================================================================
--- java/main/src/main/java/com/framsticks/gui/controls/ProcedureControl.java	(revision 100)
+++ java/main/src/main/java/com/framsticks/gui/controls/ProcedureControl.java	(revision 101)
@@ -5,13 +5,10 @@
 import com.framsticks.gui.Gui;
 import com.framsticks.params.Param;
-import com.framsticks.params.ValueParam;
 import com.framsticks.params.types.ProcedureParam;
 import com.framsticks.util.dispatching.ExceptionResultHandler;
 import com.framsticks.util.dispatching.FutureHandler;
 import com.framsticks.util.dispatching.ThrowExceptionHandler;
-import com.framsticks.util.swing.TooltipConstructor;
 
 import javax.swing.*;
-import javax.swing.border.BevelBorder;
 
 import org.apache.logging.log4j.Logger;
@@ -20,5 +17,5 @@
 import java.awt.event.ActionEvent;
 import java.awt.event.ActionListener;
-import java.util.IdentityHashMap;
+import java.util.HashMap;
 import java.util.LinkedList;
 import java.util.List;
@@ -26,36 +23,30 @@
 
 @SuppressWarnings("serial")
-public class ProcedureControl extends Control implements ControlOwner {
+public class ProcedureControl extends HistoryControl implements ControlOwner {
 
 	private static final Logger log = LogManager.getLogger(ProcedureControl.class);
 
-	protected final JButton procedureButton;
+	protected final JPanel argumentsPanel;
 
-	final protected Map<ValueParam, ValueControl> components = new IdentityHashMap<>();
+	final protected Map<String, ValueControl> components = new HashMap<>();
 
 	public ProcedureControl(ProcedureParam procedureParam) {
 		super(procedureParam);
 
-		this.setToolTipText(new TooltipConstructor()
-			.append("name", procedureParam.getName())
-			.append("id", procedureParam.getId())
-			.append("help", procedureParam.getHelp())
-			.build());
 
-		procedureButton = new JButton("Call");
-		procedureButton.setName("call");
+		mainButton.setText("Call");
+		mainButton.setName("call");
 
-		this.setLayout(new BoxLayout(this, BoxLayout.PAGE_AXIS));
 
-		Gui.fillWithControls(this, procedureParam.getArgumentsType(), components, ValueControl.class);
+		argumentsPanel = new JPanel();
+		argumentsPanel.setName("arguments");
+
+		Gui.fillWithControls(this, argumentsPanel, procedureParam.getArgumentsType(), components, ValueControl.class);
 
 		if (components.size() != procedureParam.getArgumentsType().size()) {
-			procedureButton.setEnabled(false);
-		}
-		if (!components.isEmpty()) {
-			this.setBorder(new BevelBorder(BevelBorder.RAISED));
+			mainButton.setEnabled(false);
 		}
 
-		procedureButton.addActionListener(new ActionListener() {
+		mainButton.addActionListener(new ActionListener() {
 			@Override
 			public void actionPerformed(ActionEvent e) {
@@ -65,5 +56,5 @@
 				final List<Object> arguments = new LinkedList<Object>();
 				for (Param arg : getParam().getArgumentsType()) {
-					Object value = components.get(arg).getCurrentValue();
+					Object value = components.get(arg.getId()).getCurrentValue();
 					arguments.add(value);
 					log.debug("argument {}: {}", arg, value);
@@ -74,7 +65,10 @@
 			}
 		});
-		this.add(procedureButton);
+
+		updateFoldState();
+		Gui.setupTitledControl(this, argumentsPanel, controlRow, resultsScrollPane);
 
 	}
+
 
 	public static void callProcedure(final Path path, final ProcedureParam param, Object[] arguments) {
@@ -93,9 +87,4 @@
 
 	@Override
-	public JPanel getPanelForControls() {
-		return this;
-	}
-
-	@Override
 	public ProcedureParam getParam() {
 		return (ProcedureParam) param;
@@ -104,5 +93,5 @@
 	@Override
 	protected void updateEnabled(boolean enabled) {
-		procedureButton.setEnabled(enabled);
+		mainButton.setEnabled(enabled);
 		for (ValueControl vc : components.values()) {
 			vc.setUserEnabled(enabled);
@@ -120,3 +109,20 @@
 	}
 
+	@Override
+	public boolean onValueChange(ValueControl control, Object newValue) {
+		return true;
+	}
+
+	@Override
+	protected void refreshTable() {
+		// TODO Auto-generated method stub
+
+	}
+
+	@Override
+	protected void clearTable() {
+		// TODO Auto-generated method stub
+
+	}
+
 }
Index: java/main/src/main/java/com/framsticks/gui/controls/SliderControl.java
===================================================================
--- java/main/src/main/java/com/framsticks/gui/controls/SliderControl.java	(revision 100)
+++ java/main/src/main/java/com/framsticks/gui/controls/SliderControl.java	(revision 101)
@@ -18,4 +18,5 @@
 import org.apache.logging.log4j.LogManager;
 
+import com.framsticks.gui.Gui;
 import com.framsticks.params.types.DecimalParam;
 import com.framsticks.params.types.FloatParam;
@@ -98,10 +99,5 @@
 		slider.setPaintTicks(true);
 
-		this.setLayout(new BoxLayout(this, BoxLayout.LINE_AXIS));
-		this.setAlignmentX(Box.CENTER_ALIGNMENT);
-		this.setAlignmentY(Box.CENTER_ALIGNMENT);
-
-		JPanel sliderPanel = new JPanel();
-		// sliderPanel.setLayout(new BoxLayout(sliderPanel, BoxLayout.LINE_AXIS));
+
 
 
@@ -140,12 +136,11 @@
 		}));
 
-		JPanel sVPanel = new JPanel();
-		sVPanel.setLayout(new BoxLayout(sVPanel, BoxLayout.LINE_AXIS));
-		sVPanel.add(text);
-		Layout.copyComponentDimensions(sVPanel, text);
-
-		JPanel sPanel = new JPanel();
-		sPanel.setLayout(new BoxLayout(sPanel, BoxLayout.LINE_AXIS));
-
+		JPanel valuePanel = new JPanel();
+		valuePanel.setLayout(new BoxLayout(valuePanel, BoxLayout.LINE_AXIS));
+		valuePanel.add(text);
+		Layout.copyComponentDimensions(valuePanel, text);
+
+
+		JPanel sliderPanel = new JPanel();
 		sliderPanel.setLayout(new BorderLayout());
 		sliderPanel.add(slider, BorderLayout.CENTER);
@@ -153,8 +148,10 @@
 		sliderPanel.setMinimumSize(new Dimension(0, 60));
 
-		sPanel.add(sVPanel);
-		sPanel.add(sliderPanel);
-
-		this.add(sPanel);
+
+		this.setLayout(new BoxLayout(this, BoxLayout.LINE_AXIS));
+		this.setAlignmentX(Box.CENTER_ALIGNMENT);
+		this.setAlignmentY(Box.CENTER_ALIGNMENT);
+
+		Gui.addLeftToLabel(this, valuePanel, sliderPanel);
 	}
 
Index: java/main/src/main/java/com/framsticks/gui/controls/TextAreaControl.java
===================================================================
--- java/main/src/main/java/com/framsticks/gui/controls/TextAreaControl.java	(revision 100)
+++ java/main/src/main/java/com/framsticks/gui/controls/TextAreaControl.java	(revision 101)
@@ -1,4 +1,5 @@
 package com.framsticks.gui.controls;
 
+import com.framsticks.gui.Gui;
 import com.framsticks.params.PrimitiveParam;
 
@@ -27,5 +28,6 @@
 		textArea.setMaximumSize(new Dimension(Integer.MAX_VALUE, maxSize));
 
-		this.revalidate();
+		Gui.setupTitledControl(this, textScrollPane);
+		// this.revalidate();
 	}
 
Index: java/main/src/main/java/com/framsticks/gui/controls/TextFieldControl.java
===================================================================
--- java/main/src/main/java/com/framsticks/gui/controls/TextFieldControl.java	(revision 100)
+++ java/main/src/main/java/com/framsticks/gui/controls/TextFieldControl.java	(revision 101)
@@ -1,4 +1,5 @@
 package com.framsticks.gui.controls;
 
+import com.framsticks.gui.Gui;
 import com.framsticks.params.PrimitiveParam;
 import com.framsticks.util.lang.Strings;
@@ -33,5 +34,5 @@
 		addDefaultDocumentListener(textField);
 
-		addAsOnlyChild(textField);
+		Gui.addLeftToLabel(this, textField);
 	}
 
Index: java/main/src/main/java/com/framsticks/gui/controls/ValueControl.java
===================================================================
--- java/main/src/main/java/com/framsticks/gui/controls/ValueControl.java	(revision 100)
+++ java/main/src/main/java/com/framsticks/gui/controls/ValueControl.java	(revision 101)
@@ -12,4 +12,5 @@
 import com.framsticks.util.lang.FlagsUtil;
 import com.framsticks.util.swing.TooltipConstructor;
+// import static com.framsticks.core.TreeOperations.*;
 
 /**
@@ -20,9 +21,4 @@
 	private static final Logger log =
 		LogManager.getLogger(ValueControl.class);
-
-	/**
-	 *
-	 */
-	protected ValueControlListener listener;
 
 	public ValueControl(PrimitiveParam<?> primitiveParam) {
@@ -64,10 +60,6 @@
 	public abstract Object pullValueFromUserInterface();
 
-	public void setListener(ValueControlListener listener) {
-		this.listener = listener;
-	}
-
 	protected Object filterValueThroughConstraints(Object candidate) {
-		Object oldValue = pullValueFromUserInterface();
+		Object oldValue = pullValueFromUserInterface();//bindAccess(owner.getCurrentPath()).get(getParam(), Object.class);
 		try {
 			ReassignResult<?> res = getParam().reassign(candidate, oldValue);
@@ -92,8 +84,5 @@
 	protected boolean notifyOfChange() {
 		if (!programmaticChange) {
-			if (listener == null) {
-				return true;
-			}
-			return listener.onChange(getCurrentValue());
+			return owner.onValueChange(this, getCurrentValue());
 		}
 		return true;
Index: java/main/src/main/java/com/framsticks/gui/controls/ValueControlListener.java
===================================================================
--- java/main/src/main/java/com/framsticks/gui/controls/ValueControlListener.java	(revision 100)
+++ 	(revision )
@@ -1,8 +1,0 @@
-package com.framsticks.gui.controls;
-
-/**
- * @author Piotr Sniegowski
- */
-public interface ValueControlListener {
-	public boolean onChange(Object newValue);
-}
Index: java/main/src/main/java/com/framsticks/gui/table/AbstractTableModel.java
===================================================================
--- java/main/src/main/java/com/framsticks/gui/table/AbstractTableModel.java	(revision 101)
+++ java/main/src/main/java/com/framsticks/gui/table/AbstractTableModel.java	(revision 101)
@@ -0,0 +1,32 @@
+package com.framsticks.gui.table;
+
+import java.util.LinkedList;
+import java.util.List;
+
+import javax.swing.event.TableModelEvent;
+import javax.swing.event.TableModelListener;
+
+public abstract class AbstractTableModel implements javax.swing.table.TableModel {
+
+	protected final List<TableModelListener> listeners = new LinkedList<>();
+
+	@Override
+	public void addTableModelListener(TableModelListener listener) {
+		listeners.add(listener);
+	}
+
+	@Override
+	public void removeTableModelListener(TableModelListener listener) {
+		listeners.remove(listener);
+	}
+
+	protected void notifyAll(TableModelEvent event) {
+		for (TableModelListener l : listeners) {
+			l.tableChanged(event);
+		}
+	}
+
+	public void refreshAll() {
+		notifyAll(new TableModelEvent(this));
+	}
+}
Index: java/main/src/main/java/com/framsticks/gui/table/ListPanel.java
===================================================================
--- java/main/src/main/java/com/framsticks/gui/table/ListPanel.java	(revision 100)
+++ java/main/src/main/java/com/framsticks/gui/table/ListPanel.java	(revision 101)
@@ -54,4 +54,5 @@
 		table = new JTable(tableModel);
 		tableModel.setupTable();
+		table.setShowGrid(false);
 
 		scrollPane = new JScrollPane(table);
@@ -72,6 +73,11 @@
 
 	@Override
+	protected void revertChanges() {
+	}
+
+	@Override
 	public void pullValuesFromLocalToUser(Access access) {
 		tableModel.attachSource(Casting.throwCast(ListAccess.class, access));
+		refreshControlButtons();
 	}
 
@@ -87,3 +93,4 @@
 		return table;
 	}
+
 }
Index: java/main/src/main/java/com/framsticks/gui/table/PrimitiveColumn.java
===================================================================
--- java/main/src/main/java/com/framsticks/gui/table/PrimitiveColumn.java	(revision 100)
+++ java/main/src/main/java/com/framsticks/gui/table/PrimitiveColumn.java	(revision 101)
@@ -2,5 +2,5 @@
 
 import com.framsticks.params.PrimitiveParam;
-import com.framsticks.util.UnsupportedOperationException;
+import com.framsticks.util.FramsticksUnsupportedOperationException;
 
 public class PrimitiveColumn extends Column {
@@ -31,5 +31,5 @@
 	@Override
 	public int setValueAt(int rowIndex, Object value) {
-		throw new UnsupportedOperationException().msg("setting value in table");
+		throw new FramsticksUnsupportedOperationException().msg("setting value in table");
 	}
 
Index: java/main/src/main/java/com/framsticks/gui/table/TableModel.java
===================================================================
--- java/main/src/main/java/com/framsticks/gui/table/TableModel.java	(revision 100)
+++ java/main/src/main/java/com/framsticks/gui/table/TableModel.java	(revision 101)
@@ -3,5 +3,4 @@
 import java.util.ArrayList;
 import java.util.Collections;
-import java.util.LinkedList;
 import java.util.List;
 
@@ -10,6 +9,4 @@
 import javax.swing.JTable;
 import javax.swing.UIManager;
-import javax.swing.event.TableModelEvent;
-import javax.swing.event.TableModelListener;
 
 import org.apache.logging.log4j.Logger;
@@ -23,9 +20,8 @@
 import com.framsticks.util.lang.Casting;
 
-public class TableModel implements javax.swing.table.TableModel {
+public class TableModel extends AbstractTableModel {
 
 	private static final Logger log = LogManager.getLogger(TableModel.class);
 
-	protected List<TableModelListener> listeners = new LinkedList<>();
 	protected ListAccess access;
 	protected Access elementAccess;
@@ -52,9 +48,4 @@
 	}
 
-	protected void refreshAll() {
-		for (TableModelListener l : listeners) {
-			l.tableChanged(new TableModelEvent(this));
-		}
-	}
 
 	/**
@@ -74,8 +65,4 @@
 	}
 
-	@Override
-	public void addTableModelListener(TableModelListener listener) {
-		listeners.add(listener);
-	}
 
 	@Override
@@ -114,8 +101,4 @@
 	}
 
-	@Override
-	public void removeTableModelListener(TableModelListener listener) {
-		listeners.remove(listener);
-	}
 
 
Index: java/main/src/main/java/com/framsticks/gui/tree/TreeModel.java
===================================================================
--- java/main/src/main/java/com/framsticks/gui/tree/TreeModel.java	(revision 100)
+++ java/main/src/main/java/com/framsticks/gui/tree/TreeModel.java	(revision 101)
@@ -17,4 +17,5 @@
 import com.framsticks.core.Node;
 import com.framsticks.core.Path;
+import com.framsticks.core.SideNoteKey;
 import com.framsticks.core.TreeOperations;
 import com.framsticks.gui.Frame;
@@ -24,10 +25,10 @@
 import com.framsticks.params.ListAccess;
 import com.framsticks.params.PrimitiveParam;
-import com.framsticks.params.Util;
+import com.framsticks.params.ParamsUtil;
 import com.framsticks.params.ValueParam;
 import com.framsticks.params.types.EventParam;
 import com.framsticks.util.FramsticksException;
 import com.framsticks.util.Misc;
-import com.framsticks.util.UnsupportedOperationException;
+import com.framsticks.util.FramsticksUnsupportedOperationException;
 import com.framsticks.util.dispatching.FutureHandler;
 import com.framsticks.util.lang.Casting;
@@ -91,5 +92,5 @@
 	@Override
 	public void valueForPathChanged(TreePath path, Object value) {
-		throw new UnsupportedOperationException().msg("changing value of tree node");
+		throw new FramsticksUnsupportedOperationException().msg("changing value of tree node");
 	}
 
@@ -133,5 +134,5 @@
 	public TreeModelEvent prepareModelEventRegarding(Access access, String id, TreePath treeListPath) {
 
-		int number = Util.getNumberOfCompositeParamChild(access, access.get(id, Object.class));
+		int number = ParamsUtil.getNumberOfCompositeParamChild(access, access.get(id, Object.class));
 		if (number == -1) {
 			log.debug("encountered minor tree inconsistency in {}", treeListPath);
@@ -438,5 +439,5 @@
 
 
-	protected final Object createdTag = new Object();
+	protected final SideNoteKey<Boolean> createdTag = SideNoteKey.make(Boolean.class);
 
 
Index: java/main/src/main/java/com/framsticks/gui/tree/TreeNode.java
===================================================================
--- java/main/src/main/java/com/framsticks/gui/tree/TreeNode.java	(revision 100)
+++ java/main/src/main/java/com/framsticks/gui/tree/TreeNode.java	(revision 101)
@@ -4,5 +4,4 @@
 import java.util.Iterator;
 import java.util.LinkedList;
-import java.util.List;
 
 import org.apache.logging.log4j.LogManager;
@@ -11,4 +10,5 @@
 import com.framsticks.core.Node;
 import com.framsticks.core.Path;
+import com.framsticks.core.SideNoteKey;
 import com.framsticks.core.Tree;
 import com.framsticks.gui.Frame;
@@ -59,10 +59,10 @@
 		hashCode = System.identityHashCode(path.getTopObject());
 
-		if (getTree().getSideNote(path.getTopObject(), getTreeModel().createdTag, Object.class) == getTreeModel().createdTag) {
+		if (isMarked(path.getTree(), path.getTopObject(), getTreeModel().createdTag, false)) {
 			return;
 		}
 
 		// path.getTree().putSideNote(path.getTopObject(), Textual.class, path.getTextual());
-		path.getTree().putSideNote(path.getTopObject(), getTreeModel().createdTag, getTreeModel().createdTag);
+		mark(path.getTree(), path.getTopObject(), getTreeModel().createdTag, true);
 
 		/** Iterate over all EventParams and for matching ValueParams register listeners. */
@@ -239,5 +239,5 @@
 		}
 		Access access = bindAccessForTreeObject(child);
-		CompositeParam param = getTree().getSideNote(child, CompositeParam.class, CompositeParam.class);
+		CompositeParam param = getTree().getSideNote(child, Path.OBJECT_PARAM_KEY);
 		String name = param.getId();
 
@@ -262,32 +262,20 @@
 	}
 
-	// public static class Textual {
-	// }
 
 	public String getTextual() {
 		return textual;
-		// return getTree().getSideNote(lock(), Textual.class, String.class);
-	}
-
-	protected final Object listenersTag = new Object();
-
-	public List<EventListener<?>> getListeners() {
-		@SuppressWarnings("unchecked")
-		List<EventListener<?>> result = getTree().getSideNote(lock(), listenersTag, List.class);
-		if (result == null) {
-			result = new LinkedList<>();
-			getTree().putSideNote(lock(), listenersTag, result);
-		}
-
-		return result;
-	}
+	}
+
+	@SuppressWarnings("rawtypes")
+	protected final SideNoteKey<LinkedList> listenersTag = SideNoteKey.make(LinkedList.class);
 
 	protected <A> void tryAddListener(final Path path, final EventParam eventParam, Class<A> argumentType, final EventListener<A> listener) {
 		getTree().addListener(path, eventParam, listener, argumentType, new FutureHandler<Void>(getFrame()) {
+			@SuppressWarnings("unchecked")
 			@Override
 			protected void result(Void result) {
 				assert getFrame().isActive();
 				log.debug("registered gui listener for {} at {}", eventParam, path);
-				getListeners().add(listener);
+				getOrCreateSideNote(getTree(), lock(), listenersTag).add(listener);
 			}
 		});
Index: java/main/src/main/java/com/framsticks/hosting/Cli.java
===================================================================
--- java/main/src/main/java/com/framsticks/hosting/Cli.java	(revision 100)
+++ java/main/src/main/java/com/framsticks/hosting/Cli.java	(revision 101)
@@ -58,5 +58,7 @@
 				client.connection.sendFile(
 						"event " + event.pathToEvent + " " + event.name,
-						ClientAtServer.printToFile("", tree.getRegistry().createAccess(argument.getClass()).select(argument))
+						ClientAtServer.printToFile("", tree.getRegistry().createAccess(argument.getClass()).select(argument)),
+						null,
+						client
 					);
 			}
Index: java/main/src/main/java/com/framsticks/hosting/Server.java
===================================================================
--- java/main/src/main/java/com/framsticks/hosting/Server.java	(revision 100)
+++ java/main/src/main/java/com/framsticks/hosting/Server.java	(revision 101)
@@ -6,4 +6,5 @@
 
 import com.framsticks.core.Tree;
+import com.framsticks.params.ParamFlags;
 import com.framsticks.params.annotations.AutoAppendAnnotation;
 import com.framsticks.params.annotations.FramsClassAnnotation;
@@ -17,5 +18,4 @@
 import com.framsticks.util.dispatching.JoinableState;
 import com.framsticks.util.dispatching.RunAt;
-import com.framsticks.util.dispatching.Task;
 import com.framsticks.util.dispatching.ThrowExceptionHandler;
 
@@ -24,4 +24,6 @@
 import java.net.ServerSocket;
 import java.net.Socket;
+import java.util.TimerTask;
+
 import com.framsticks.util.dispatching.Thread;
 
@@ -61,5 +63,5 @@
 	 * @param port the port to set
 	 */
-	@ParamAnnotation
+	@ParamAnnotation(flags = ParamFlags.USERREADONLY)
 	public void setPort(int port) {
 		this.port = port;
@@ -76,5 +78,5 @@
 			log.debug("exception caught during socket closing: ", e);
 		}
-		finish();
+		finishJoinable();
 	}
 
@@ -150,18 +152,25 @@
 
 	protected void tryBind(int when) {
-		acceptThread.dispatch(new Task<Accept>(ThrowExceptionHandler.getInstance(), when) {
+		Dispatching.getTimer().schedule(new TimerTask() {
+
 			@Override
-			protected void runAt() {
-				try {
-					acceptSocket.bind(new InetSocketAddress(port));
-					log.debug("started accepting on port {}", port);
-					acceptNext();
-					return;
-				} catch (IOException e) {
-					log.warn("failed to accept on port {} (repeating): ", port, e);
-				}
-				tryBind(1000);
+			public void run() {
+				acceptThread.dispatch(new RunAt<Accept>(ThrowExceptionHandler.getInstance()) {
+					@Override
+					protected void runAt() {
+						try {
+							acceptSocket.bind(new InetSocketAddress(port));
+							log.debug("started accepting on port {}", port);
+							acceptNext();
+							return;
+						} catch (IOException e) {
+							log.warn("failed to accept on port {} (repeating): ", port, e);
+						}
+						tryBind(1000);
+					}
+				});
 			}
-		});
+
+		}, when);
 	}
 
@@ -178,45 +187,3 @@
 	}
 
-	// @Override
-	// public FramsClass getInfoFromCache(String id) {
-	//	assert isActive();
-	//	if (id == null) {
-	//		return null;
-	//	}
-	//	FramsClass cached = registry.getFramsClass(id);
-	//	if (cached != null) {
-	//		return cached;
-	//	}
-	//	try {
-	//		Class<?> nativeClass = Class.forName(id);
-	//		FramsClass framsClass = FramsClass.build().forClass(nativeClass);
-
-	//		if (!framsClass.getId().equals(id)) {
-	//			log.error("no matching id");
-	//			return null;
-	//		}
-
-	//		registry.registerReflectedClass(null, id, nativeClass);
-	//		registry.putFramsClass(framsClass);
-	//		return framsClass;
-	//	} catch (ClassNotFoundException ignored) {
-	//	} catch (ConstructionException e) {
-	//		log.error("failed to use info from cache: {}", e);
-	//	}
-
-	//	return null;
-	// }
-
-	// @Override
-	// protected void fetchInfo(Path path, Future<FramsClass> future) {
-	//	assert isActive();
-
-	//	FramsClass framsClass = getInfoFromCache(path.getTop().getObject().getClass().getCanonicalName());
-	//	if (framsClass == null) {
-	//		log.error("failed to create frams class for: {}", path.getTop().getObject().getClass());
-	//		future.result(null, new Exception());
-	//		return;
-	//	}
-	//	future.result(framsClass, null);
-	// }
 }
Index: java/main/src/main/java/com/framsticks/model/ModelBuilder.java
===================================================================
--- java/main/src/main/java/com/framsticks/model/ModelBuilder.java	(revision 100)
+++ java/main/src/main/java/com/framsticks/model/ModelBuilder.java	(revision 101)
@@ -7,5 +7,5 @@
 
 import com.framsticks.model.f0.Schema;
-import com.framsticks.params.Util;
+import com.framsticks.params.ParamsUtil;
 import com.framsticks.params.annotations.AutoAppendAnnotation;
 import com.framsticks.params.annotations.FramsClassAnnotation;
@@ -35,5 +35,5 @@
 			}
 			F0Parser parser = new F0Parser(schema, stream);
-			components.addAll(Util.stripAccess(parser.parse(), ModelComponent.class));
+			components.addAll(ParamsUtil.stripAccess(parser.parse(), ModelComponent.class));
 		}
 		return build();
Index: java/main/src/main/java/com/framsticks/params/AbstractUniqueObject.java
===================================================================
--- java/main/src/main/java/com/framsticks/params/AbstractUniqueObject.java	(revision 101)
+++ java/main/src/main/java/com/framsticks/params/AbstractUniqueObject.java	(revision 101)
@@ -0,0 +1,27 @@
+package com.framsticks.params;
+
+import com.framsticks.params.annotations.FramsClassAnnotation;
+import com.framsticks.params.annotations.ParamAnnotation;
+
+@FramsClassAnnotation
+public abstract class AbstractUniqueObject implements UniqueObject {
+
+	protected String uid;
+
+	/**
+	 * @return the uid
+	 */
+	@ParamAnnotation
+	public String getUid() {
+		return uid;
+	}
+
+	/**
+	 * @param uid the uid to set
+	 */
+	@ParamAnnotation
+	public void setUid(String uid) {
+		this.uid = uid;
+	}
+
+}
Index: java/main/src/main/java/com/framsticks/params/Access.java
===================================================================
--- java/main/src/main/java/com/framsticks/params/Access.java	(revision 100)
+++ java/main/src/main/java/com/framsticks/params/Access.java	(revision 101)
@@ -44,19 +44,5 @@
 	void regRemove(EventParam param, EventListener<?> listener);
 
-	void setDefault(boolean numericOnly);
-
-	void setDefault(int i, boolean numericOnly);
-
-	void setMin();
-
-	void setMin(int i);
-
-	void setMax();
-
-	void setMax(int i);
-
 	void save(SinkInterface sink);
-
-	void load(SourceInterface stream);
 
 	/**
@@ -69,5 +55,5 @@
 	Object getSelected();
 
-	Access cloneAccess() throws ConstructionException;
+	Access cloneAccess();
 
 	Object createAccessee();
Index: java/main/src/main/java/com/framsticks/params/AccessOperations.java
===================================================================
--- java/main/src/main/java/com/framsticks/params/AccessOperations.java	(revision 101)
+++ java/main/src/main/java/com/framsticks/params/AccessOperations.java	(revision 101)
@@ -0,0 +1,181 @@
+package com.framsticks.params;
+
+import org.apache.logging.log4j.Logger;
+import org.apache.logging.log4j.LogManager;
+
+import com.framsticks.util.FramsticksException;
+import com.framsticks.util.lang.Containers;
+import com.framsticks.util.lang.Holder;
+// import com.framsticks.util.lang.Containers;
+
+import static com.framsticks.params.SetStateFlags.*;
+
+public final class AccessOperations {
+
+	private final static Logger log = LogManager.getLogger(SimpleAbstractAccess.class.getName());
+
+	/**
+	 *
+	 */
+	private AccessOperations() {
+	}
+
+	/**
+	 * Simple String key, value class.
+	 */
+	public static class Entry {
+
+		public final String key;
+		public final String value;
+
+		public Entry(String key, String value) {
+			this.key = key;
+			this.value = value;
+		}
+
+		@Override
+		public String toString() {
+			return key + " = " + value;
+		}
+	}
+
+	private static Entry readEntry(Source source) {
+
+		String line;
+		String key = null;
+		StringBuilder value = null;
+		while ((line = source.readLine()) != null) {
+			if (key == null) {
+				int colonIndex = line.indexOf(':');
+				if (colonIndex == -1) {
+					return null;
+				}
+				key = line.substring(0, colonIndex);
+				String inlineValue = line.substring(colonIndex + 1);
+
+
+				if (!inlineValue.startsWith("~")) {
+					return new Entry(key, inlineValue);
+				}
+				value = new StringBuilder();
+				value.append(inlineValue.substring(1));
+				continue;
+			}
+			if (value.length() != 0) {
+				value.append(System.getProperty("line.separator"));
+			}
+			if (line.endsWith("~") && !line.endsWith("\\~")) {
+				value.append(line.substring(0, line.length() - 1));
+				return new Entry(key, value.toString().replaceAll("\\\\~", "~"));
+			}
+			value.append(line);
+		}
+		return null;
+	}
+
+	public static void load(Access access, Source source) {
+		Entry entry;
+		while ((entry = readEntry(source)) != null) {
+			Param param = access.getParam(entry.key);
+			if (param == null) {
+				throw new FramsticksException().msg("param not found in access").arg("name", entry.key).arg("access", access);
+			}
+			if (!(param instanceof ValueParam)) {
+				throw new FramsticksException().msg("param is not a value param").arg("param", param).arg("access", access);
+			}
+			if ((param.getFlags() & ParamFlags.DONTLOAD) == 0) {
+				int retFlags = access.set((ValueParam) param, entry.value);
+				if ((retFlags & (PSET_HITMIN | PSET_HITMAX)) != 0) {
+					String which = ((retFlags & PSET_HITMIN) != 0) ? "small" : "big";
+					log.warn("value of key '{}' was too {}, adjusted", entry.key, which);
+				}
+			}
+		}
+
+	}
+
+	public interface Adjuster {
+		public Holder<Object> adjust(ValueParam param);
+		public Class<? extends ValueParam> getParamType();
+	}
+
+	public static class MinAdjuster implements Adjuster {
+
+		/**
+		 *
+		 */
+		public MinAdjuster() {
+		}
+
+		@Override
+		public Class<? extends ValueParam> getParamType() {
+			return PrimitiveParam.class;
+		}
+
+		@Override
+		public Holder<Object> adjust(ValueParam param) {
+			Object value = ((PrimitiveParam<?>) param).getMin(Object.class);
+			if (value == null) {
+				return null;
+			}
+			return Holder.make(value);
+		}
+	}
+
+	public static class MaxAdjuster implements Adjuster {
+
+		/**
+		 *
+		 */
+		public MaxAdjuster() {
+		}
+
+		@Override
+		public Class<? extends ValueParam> getParamType() {
+			return PrimitiveParam.class;
+		}
+
+		@Override
+		public Holder<Object> adjust(ValueParam param) {
+			Object value = ((PrimitiveParam<?>) param).getMax(Object.class);
+			if (value == null) {
+				return null;
+			}
+			return Holder.make(value);
+		}
+	}
+
+	public static class DefAdjuster implements Adjuster {
+
+		protected final boolean numericOnly;
+
+		/**
+		 * @param numericOnly
+		 */
+		public DefAdjuster(boolean numericOnly) {
+			this.numericOnly = numericOnly;
+		}
+
+		public Class<? extends ValueParam> getParamType() {
+			return ValueParam.class;
+		}
+
+		@Override
+		public Holder<Object> adjust(ValueParam param) {
+			if (numericOnly && !(param.isNumeric())) {
+				return null;
+			}
+			return Holder.make(param.getDef(Object.class));
+		}
+	}
+
+	public static void adjustAll(Access access, Adjuster adjuster) {
+		for (ValueParam param : Containers.filterInstanceof(access.getParams(), adjuster.getParamType())) {
+			Holder<Object> value = adjuster.adjust(param);
+			if (value != null) {
+				access.set(param, value.get());
+			}
+		}
+	}
+
+}
Index: java/main/src/main/java/com/framsticks/params/AccessProvider.java
===================================================================
--- java/main/src/main/java/com/framsticks/params/AccessProvider.java	(revision 101)
+++ java/main/src/main/java/com/framsticks/params/AccessProvider.java	(revision 101)
@@ -0,0 +1,8 @@
+package com.framsticks.params;
+
+public interface AccessProvider {
+
+	public Access getAccess(String name);
+	public void addAccess(Access access);
+
+}
Index: java/main/src/main/java/com/framsticks/params/ArrayListAccess.java
===================================================================
--- java/main/src/main/java/com/framsticks/params/ArrayListAccess.java	(revision 100)
+++ java/main/src/main/java/com/framsticks/params/ArrayListAccess.java	(revision 101)
@@ -24,5 +24,5 @@
 
 	@Override
-	public ArrayListAccess cloneAccess() throws ConstructionException {
+	public ArrayListAccess cloneAccess() {
 		return new ArrayListAccess(elementAccess.cloneAccess());
 	}
@@ -124,5 +124,5 @@
 	@Override
 	public ArrayListAccess select(Object object) {
-		list = Util.selectObjectForAccess(this, object, List.class);
+		list = ParamsUtil.selectObjectForAccess(this, object, List.class);
 		return this;
 	}
Index: java/main/src/main/java/com/framsticks/params/EventListeners.java
===================================================================
--- java/main/src/main/java/com/framsticks/params/EventListeners.java	(revision 101)
+++ java/main/src/main/java/com/framsticks/params/EventListeners.java	(revision 101)
@@ -0,0 +1,23 @@
+package com.framsticks.params;
+
+import java.util.LinkedList;
+import java.util.List;
+
+public class EventListeners<A> {
+
+	protected final List<EventListener<A>> listeners = new LinkedList<>();
+
+	public void add(EventListener<A> listener) {
+		listeners.add(listener);
+	}
+
+	public void remove(EventListener<A> listener) {
+		listeners.remove(listener);
+	}
+
+	public void actionForAll(A argument) {
+		for (EventListener<A> l : listeners) {
+			l.action(argument);
+		}
+	}
+}
Index: java/main/src/main/java/com/framsticks/params/FramsClassBuilder.java
===================================================================
--- java/main/src/main/java/com/framsticks/params/FramsClassBuilder.java	(revision 100)
+++ java/main/src/main/java/com/framsticks/params/FramsClassBuilder.java	(revision 101)
@@ -5,6 +5,4 @@
 import java.lang.reflect.Member;
 import java.lang.reflect.Method;
-import java.lang.reflect.ParameterizedType;
-import java.lang.reflect.Type;
 import java.util.ArrayList;
 import java.util.Collections;
@@ -40,114 +38,4 @@
 	}
 
-	public static ParamBuilder induceParamType(ParamBuilder builder, Type type) {
-		// if (type.equals(Void.TYPE)) {
-		// 	throw new ConstructionException().msg("void is not a valid type");
-		// }
-
-		if (type instanceof ParameterizedType) {
-			ParameterizedType p = (ParameterizedType) type;
-			Type rawType = p.getRawType();
-			Type containedType = null;
-			//TODO make implementation here
-			boolean map = false;
-			StringBuilder b = new StringBuilder();
-			if (rawType.equals(Map.class)) {
-				containedType = p.getActualTypeArguments()[1];
-				map = true;
-				b.append("l");
-			} else if (rawType.equals(List.class)) {
-				containedType = p.getActualTypeArguments()[0];
-				b.append("l");
-			} else if (rawType.equals(EventListener.class)) {
-				containedType = p.getActualTypeArguments()[0];
-				b.append("e");
-			}
-			if (!(containedType instanceof Class)) {
-				return builder;
-			}
-			b.append(" ");
-
-			Class<?> containedClass = (Class<?>) containedType;
-			FramsClassAnnotation fca = containedClass.getAnnotation(FramsClassAnnotation.class);
-			if (fca == null) {
-				throw new ConstructionException().msg("the contained class is not annotated").arg("class", containedClass);
-			}
-			b.append(getName(fca, containedClass));
-			//TODO parametrize this
-			if (map) {
-				b.append(" uid");
-			}
-
-			builder.type(b.toString());
-			return builder;
-		}
-
-		if (type instanceof Class) {
-
-			Class<?> cl = (Class<?>) type;
-
-			// TODO: future support for enum
-			// if (cl.isEnum()) {
-			//	Class<? extends Enum<?>> enumType = (Class<? extends Enum<?>>) cl;
-			//	Enum<?>[] enums = enumType.getEnumConstants();
-			//	StringBuilder b = new StringBuilder();
-
-			//	b.append("d 0 ").append(enums.length - 1).append(" 0 ");
-			//	for (Enum<?> e : enums) {
-			//		b.append("~").append(e.name());
-			//	}
-			//	return b.toString();
-			// }
-			if (cl.equals(Integer.class) || cl.equals(int.class)) {
-				builder.type("d");
-				return builder;
-			}
-			if (cl.equals(String.class)) {
-				builder.type("s");
-				return builder;
-			}
-			if (cl.equals(Double.class) || cl.equals(double.class)) {
-				builder.type("f");
-				return builder;
-			}
-			if (cl.equals(Boolean.class) || cl.equals(boolean.class)) {
-				builder.type( "d 0 1");
-				return builder;
-			}
-			if (cl.equals(Object.class)) {
-				builder.type("x");
-				return builder;
-			}
-
-
-			// builder.type("o " + (cl).getCanonicalName());
-			builder.type("o " + cl.getSimpleName());
-			builder.fillStorageType(cl);
-			return builder;
-		}
-
-		throw new ConstructionException().msg("failed to find framsticks for native type").arg("type", type);
-	}
-
-
-	public static ParamBuilder induceParamType(ParamBuilder builder, ParamCandidate candidate) {
-		Method method = candidate.getCaller();
-		if (method == null) {
-			return induceParamType(builder, candidate.getType());
-		}
-
-		if (!method.getReturnType().equals(Void.TYPE)) {
-			builder.resultType(induceParamType(Param.build(), method.getGenericReturnType()).finish(ValueParam.class));
-		}
-
-		List<ValueParam> arguments = new ArrayList<>();
-		int number = 0;
-		for (Type arg : method.getGenericParameterTypes()) {
-			arguments.add(induceParamType(Param.build(), arg).idAndName("arg" + (number++)).finish(ValueParam.class));
-		}
-		builder.argumentsType(arguments);
-
-		return builder;
-	}
 
 	public static final String GENERATE_HELP_PREFIX = "automatically generated from: ";
@@ -238,5 +126,5 @@
 			ParamBuilder builder = Param.build().id(pc.getId()).name(pc.getName()).flags(pc.getFlags());
 
-			induceParamType(builder, pc);
+			pc.induceParamType(builder);
 
 			for (ParamAnnotation pa : pc.getAnnotations()) {
@@ -365,3 +253,5 @@
 	}
 
+
+
 }
Index: java/main/src/main/java/com/framsticks/params/InvalidOperationException.java
===================================================================
--- java/main/src/main/java/com/framsticks/params/InvalidOperationException.java	(revision 100)
+++ java/main/src/main/java/com/framsticks/params/InvalidOperationException.java	(revision 101)
@@ -1,8 +1,8 @@
 package com.framsticks.params;
 
-import com.framsticks.util.UnsupportedOperationException;
+import com.framsticks.util.FramsticksUnsupportedOperationException;
 
 @SuppressWarnings("serial")
-public class InvalidOperationException extends UnsupportedOperationException {
+public class InvalidOperationException extends FramsticksUnsupportedOperationException {
 
 }
Index: java/main/src/main/java/com/framsticks/params/ListAccess.java
===================================================================
--- java/main/src/main/java/com/framsticks/params/ListAccess.java	(revision 100)
+++ java/main/src/main/java/com/framsticks/params/ListAccess.java	(revision 101)
@@ -7,5 +7,4 @@
 import com.framsticks.params.types.EventParam;
 import com.framsticks.params.types.ProcedureParam;
-import com.framsticks.util.UnimplementedException;
 
 /**
@@ -30,27 +29,4 @@
 	// }
 
-	@Override
-	public void setDefault(boolean numericOnly) {
-	}
-
-	@Override
-	public void setDefault(int i, boolean numericOnly) {
-	}
-
-	@Override
-	public void setMin() {
-	}
-
-	@Override
-	public void setMin(int i) {
-	}
-
-	@Override
-	public void setMax() {
-	}
-
-	@Override
-	public void setMax(int i) {
-	}
 
 	@Override
@@ -63,9 +39,4 @@
 			elementAccess.save(sink);
 		}
-	}
-
-	@Override
-	public void load(SourceInterface stream) {
-		throw new UnimplementedException().msg("load in list access");
 	}
 
Index: java/main/src/main/java/com/framsticks/params/ListSource.java
===================================================================
--- java/main/src/main/java/com/framsticks/params/ListSource.java	(revision 100)
+++ java/main/src/main/java/com/framsticks/params/ListSource.java	(revision 101)
@@ -5,12 +5,12 @@
 import java.util.List;
 
-public class ListSource implements SourceInterface {
+public class ListSource implements Source {
 
 	private Iterator<String> iterator = null;
-	//private final List<String> source;
+	private final List<String> list;
 
-	public ListSource(List<String> source) {
-		//this.source = source;
-		iterator = source.iterator();
+	public ListSource(List<String> list) {
+		this.list = list;
+		iterator = list.iterator();
 	}
 
@@ -38,5 +38,5 @@
 
 	@Override
-	public SourceInterface openInclude(String include) {
+	public Source openInclude(String include) {
 		return null;
 	}
@@ -59,3 +59,20 @@
 		return iterator == null;
 	}
+
+	@Override
+	public String toString() {
+		if (list.isEmpty()) {
+			return "?";
+		}
+		StringBuilder b = new StringBuilder();
+		int print = Math.min(list.size(), 3);
+		for (int i = 0; i < print; ++i) {
+			if (i != 0) {
+				b.append(" ");
+			}
+			b.append(list.get(i));
+		}
+		return b.toString();
+	}
+
 }
Index: java/main/src/main/java/com/framsticks/params/ObjectAccess.java
===================================================================
--- java/main/src/main/java/com/framsticks/params/ObjectAccess.java	(revision 101)
+++ java/main/src/main/java/com/framsticks/params/ObjectAccess.java	(revision 101)
@@ -0,0 +1,5 @@
+package com.framsticks.params;
+
+public interface ObjectAccess extends Access {
+
+}
Index: java/main/src/main/java/com/framsticks/params/Param.java
===================================================================
--- java/main/src/main/java/com/framsticks/params/Param.java	(revision 100)
+++ java/main/src/main/java/com/framsticks/params/Param.java	(revision 101)
@@ -96,8 +96,4 @@
 	}
 
-	public boolean isUserHidden() {
-		return (flags & ParamFlags.USERHIDDEN) != 0;
-	}
-
 	public static ParamBuilder build() {
 		return new ParamBuilder();
Index: java/main/src/main/java/com/framsticks/params/ParamCandidate.java
===================================================================
--- java/main/src/main/java/com/framsticks/params/ParamCandidate.java	(revision 100)
+++ java/main/src/main/java/com/framsticks/params/ParamCandidate.java	(revision 101)
@@ -10,4 +10,5 @@
 import java.lang.reflect.ParameterizedType;
 import java.lang.reflect.Type;
+import java.util.ArrayList;
 import java.util.Arrays;
 import java.util.Collection;
@@ -15,4 +16,6 @@
 import java.util.Comparator;
 import java.util.HashMap;
+import java.util.HashSet;
+import java.util.IdentityHashMap;
 import java.util.LinkedList;
 import java.util.List;
@@ -75,4 +78,5 @@
 	protected final OneTime<Method> adder = new OneTime<>("adder");
 	protected final OneTime<Method> remover = new OneTime<>("remover");
+	protected int flags = 0;
 
 	protected final List<ParamAnnotation> annotations = new LinkedList<>();
@@ -162,4 +166,17 @@
 	public List<ParamAnnotation> getAnnotations() {
 		return Collections.unmodifiableList(annotations);
+	}
+
+	protected final java.util.Set<Class<?>> dependantClasses = new HashSet<>();
+
+	// public void addDependantClass(Class<?> javaClass) {
+	// 	dependantClasses.add(javaClass);
+	// }
+
+	/**
+	 * @return the dependantClasses
+	 */
+	public java.util.Set<Class<?>> getDependantClasses() {
+		return Collections.unmodifiableSet(dependantClasses);
 	}
 
@@ -247,4 +264,5 @@
 		this.name.set(name);
 		annotations.add(paramAnnotation);
+		flags |= paramAnnotation.flags();
 		if (member instanceof Field) {
 			field.set((Field) member);
@@ -299,5 +317,5 @@
 
 	public int getFlags() {
-		int f = 0;
+		int f = flags;
 		if (isReadOnly()) {
 			f |= ParamFlags.READONLY;
@@ -341,4 +359,5 @@
 		protected final Map<String, ParamCandidate> candidates;
 		protected final List<ParamCandidate> order;
+		protected final java.util.Set<Class<?>> dependantClasses = new HashSet<>();
 
 		/**
@@ -364,7 +383,17 @@
 			return order;
 		}
-	}
+
+		public java.util.Set<Class<?>> getDependentClasses() {
+			return dependantClasses;
+		}
+	}
+
+	protected static final Map<Class<?>, Set> setsCache = Collections.synchronizedMap(new IdentityHashMap<Class<?>, Set>());
 
 	public static Set getAllCandidates(Class<?> javaClass) throws ConstructionException {
+		Set result = setsCache.get(javaClass);
+		if (result != null) {
+			return result;
+		}
 
 		List<Class<?>> javaClasses = new LinkedList<>();
@@ -374,8 +403,9 @@
 		}
 
-		Set resultSet = new Set(new HashMap<String, ParamCandidate>(), new LinkedList<ParamCandidate>());
+		result = new Set(new HashMap<String, ParamCandidate>(), new LinkedList<ParamCandidate>());
 
 		for (Class<?> j : javaClasses) {
-			Set set = new Set(resultSet.getCandidates(), new LinkedList<ParamCandidate>());
+			Set set = new Set(result.getCandidates(), new LinkedList<ParamCandidate>());
+
 			filterParamsCandidates(set, j.getDeclaredFields());
 			filterParamsCandidates(set, j.getDeclaredMethods());
@@ -383,4 +413,13 @@
 			FramsClassAnnotation fa = j.getAnnotation(FramsClassAnnotation.class);
 			if (fa != null) {
+
+				if (j != javaClass) {
+					result.dependantClasses.add(j);
+				}
+				for (Class<?> r : fa.register()) {
+					result.dependantClasses.add(r);
+				}
+
+
 				final List<String> order = Arrays.asList(fa.order());
 				Collections.sort(set.getOrder(), new Comparator<ParamCandidate>() {
@@ -396,12 +435,16 @@
 				});
 			}
-			resultSet.getOrder().addAll(0, set.getOrder());
-		}
-
-		for (ParamCandidate pc : resultSet.getOrder()) {
+			result.getOrder().addAll(0, set.getOrder());
+		}
+
+		for (ParamCandidate pc : result.getOrder()) {
 			pc.validate();
-		}
-
-		return resultSet;
+			pc.induceParamType(Param.build());
+			result.dependantClasses.addAll(pc.getDependantClasses());
+		}
+
+		setsCache.put(javaClass, result);
+
+		return result;
 	}
 
@@ -425,3 +468,116 @@
 	}
 
+	protected ParamBuilder induceParamType(ParamBuilder builder, Type type) {
+		// if (type.equals(Void.TYPE)) {
+		// 	throw new ConstructionException().msg("void is not a valid type");
+		// }
+
+		if (type instanceof ParameterizedType) {
+			ParameterizedType p = (ParameterizedType) type;
+			Type rawType = p.getRawType();
+			Type containedType = null;
+			//TODO make implementation here
+			boolean map = false;
+			StringBuilder b = new StringBuilder();
+			if (rawType.equals(Map.class)) {
+				containedType = p.getActualTypeArguments()[1];
+				map = true;
+				b.append("l");
+			} else if (rawType.equals(List.class)) {
+				containedType = p.getActualTypeArguments()[0];
+				b.append("l");
+			} else if (rawType.equals(EventListener.class)) {
+				containedType = p.getActualTypeArguments()[0];
+				b.append("e");
+			}
+			if (!(containedType instanceof Class)) {
+				return builder;
+			}
+			b.append(" ");
+
+			Class<?> containedClass = (Class<?>) containedType;
+			FramsClassAnnotation fca = containedClass.getAnnotation(FramsClassAnnotation.class);
+			if (fca == null) {
+				throw new ConstructionException().msg("the contained class is not annotated").arg("class", containedClass);
+			}
+			dependantClasses.add(containedClass);
+			b.append(FramsClassBuilder.getName(fca, containedClass));
+			//TODO parameterize this
+			if (map) {
+				b.append(" uid");
+			}
+
+			builder.type(b.toString());
+			return builder;
+		}
+
+		if (type instanceof Class) {
+
+			Class<?> cl = (Class<?>) type;
+
+			// TODO: future support for enum
+			// if (cl.isEnum()) {
+			//	Class<? extends Enum<?>> enumType = (Class<? extends Enum<?>>) cl;
+			//	Enum<?>[] enums = enumType.getEnumConstants();
+			//	StringBuilder b = new StringBuilder();
+
+			//	b.append("d 0 ").append(enums.length - 1).append(" 0 ");
+			//	for (Enum<?> e : enums) {
+			//		b.append("~").append(e.name());
+			//	}
+			//	return b.toString();
+			// }
+			if (cl.equals(Integer.class) || cl.equals(int.class)) {
+				builder.type("d");
+				return builder;
+			}
+			if (cl.equals(String.class)) {
+				builder.type("s");
+				return builder;
+			}
+			if (cl.equals(Double.class) || cl.equals(double.class)) {
+				builder.type("f");
+				return builder;
+			}
+			if (cl.equals(Boolean.class) || cl.equals(boolean.class)) {
+				builder.type( "d 0 1");
+				return builder;
+			}
+			if (cl.equals(Object.class)) {
+				builder.type("x");
+				return builder;
+			}
+
+
+			// builder.type("o " + (cl).getCanonicalName());
+			builder.type("o " + cl.getSimpleName());
+			dependantClasses.add(cl);
+			builder.fillStorageType(cl);
+			return builder;
+		}
+
+		throw new ConstructionException().msg("failed to find framsticks for native type").arg("type", type);
+	}
+
+
+	public ParamBuilder induceParamType(ParamBuilder builder) {
+		Method method = getCaller();
+		if (method == null) {
+			return induceParamType(builder, getType());
+		}
+
+		if (!method.getReturnType().equals(Void.TYPE)) {
+			builder.resultType(induceParamType(Param.build(), method.getGenericReturnType()).finish(ValueParam.class));
+		}
+
+		List<ValueParam> arguments = new ArrayList<>();
+		int number = 0;
+		for (Type arg : method.getGenericParameterTypes()) {
+			arguments.add(induceParamType(Param.build(), arg).idAndName("arg" + (number++)).finish(ValueParam.class));
+		}
+		builder.argumentsType(arguments);
+
+		return builder;
+	}
+
 };
Index: java/main/src/main/java/com/framsticks/params/ParamsUtil.java
===================================================================
--- java/main/src/main/java/com/framsticks/params/ParamsUtil.java	(revision 101)
+++ java/main/src/main/java/com/framsticks/params/ParamsUtil.java	(revision 101)
@@ -0,0 +1,90 @@
+package com.framsticks.params;
+
+import java.util.ArrayList;
+import java.util.List;
+
+import com.framsticks.util.FramsticksException;
+
+
+import static com.framsticks.util.lang.Containers.filterInstanceof;
+
+/**
+ * @author Piotr Sniegowski
+ */
+public abstract class ParamsUtil {
+
+	public static String readSourceToString(Source source) {
+		StringBuilder result = new StringBuilder();
+		String line;
+		while ((line = source.readLine()) != null) {
+			result.append(line).append(" ");
+		}
+		source.close();
+		return result.toString();
+	}
+
+	public static <T> List<T> stripAccess(List<Access> accesses, Class<T> type) {
+		List<T> result = new ArrayList<T>();
+		for (Access a : accesses) {
+			Object object = a.getSelected();
+			if (!type.isInstance(object)) {
+				throw new FramsticksException().msg("extracted object is of invalid type").arg("object", object).arg("desired", type).arg("actual", object.getClass()).arg("framsclass", a.getFramsClass());
+			}
+			result.add(type.cast(object));
+		}
+		return result;
+	}
+
+	public static int takeAllNonNullValues(Access to, Access from) {
+		int copied = 0;
+		for (ValueParam f : filterInstanceof(from.getParams(), ValueParam.class)) {
+			Object v = from.get(f, Object.class);
+			if (v == null) {
+				continue;
+			}
+			// if (to.get(f, Object.class) != null) {
+			//	continue;
+			// }
+			to.set(f, v);
+			++copied;
+		}
+		return copied;
+	}
+
+	public static int copyExistingParamsTypeSafe(Access to, Access from) {
+		int copied = 0;
+		for (ValueParam f : filterInstanceof(from.getParams(), ValueParam.class)) {
+			Param t = from.getParam(f.getId());
+			if (!(t instanceof ValueParam)) {
+				continue;
+			}
+			if (to.getClass() != f.getClass()) {
+				continue;
+			}
+			to.set((ValueParam) t, from.get(f, Object.class));
+			++copied;
+		}
+		return copied;
+	}
+
+	public static <T> T selectObjectForAccess(Access access, Object object, Class<T> type) {
+		if (object == null) {
+			return null;
+		}
+		if (!type.isInstance(object)) {
+			throw new FramsticksException().msg("trying to select object of wrong type").arg("object", object).arg("type", object.getClass()).arg("in", access);
+		}
+		return type.cast(object);
+	}
+
+	public static int getNumberOfCompositeParamChild(Access access, Object child) {
+		int count = access.getCompositeParamCount();
+		for (int i = 0; i < count; ++i) {
+			if (access.get(i, Object.class) == child) {
+				return i;
+			}
+		}
+		return -1;
+	}
+
+}
Index: java/main/src/main/java/com/framsticks/params/PropertiesAccess.java
===================================================================
--- java/main/src/main/java/com/framsticks/params/PropertiesAccess.java	(revision 100)
+++ java/main/src/main/java/com/framsticks/params/PropertiesAccess.java	(revision 101)
@@ -76,5 +76,5 @@
 	@Override
 	public PropertiesAccess select(Object object) {
-		properties = Util.selectObjectForAccess(this, object, Map.class);
+		properties = ParamsUtil.selectObjectForAccess(this, object, Map.class);
 		return this;
 	}
Index: java/main/src/main/java/com/framsticks/params/ReflectionAccess.java
===================================================================
--- java/main/src/main/java/com/framsticks/params/ReflectionAccess.java	(revision 100)
+++ java/main/src/main/java/com/framsticks/params/ReflectionAccess.java	(revision 101)
@@ -1,25 +1,14 @@
 package com.framsticks.params;
 
-import java.lang.reflect.Field;
 import java.lang.reflect.InvocationTargetException;
 import java.lang.reflect.Method;
-import java.util.ArrayList;
-import java.util.Collections;
-import java.util.Comparator;
-import java.util.HashMap;
-import java.util.IdentityHashMap;
-import java.util.List;
-import java.util.Map;
-
-import javax.annotation.concurrent.Immutable;
+
 
 import org.apache.logging.log4j.Logger;
 import org.apache.logging.log4j.LogManager;
 
-import com.framsticks.params.annotations.AutoAppendAnnotation;
 import com.framsticks.params.types.EventParam;
 import com.framsticks.params.types.ProcedureParam;
 import com.framsticks.util.FramsticksException;
-import com.framsticks.util.lang.Pair;
 
 import static com.framsticks.util.lang.Containers.*;
@@ -37,228 +26,7 @@
 
 	protected final Class<?> javaClass;
-	protected final Backend backend;
+	protected final ReflectionAccessBackend backend;
 
 	private Object object;
-
-	@Immutable
-	public static class Backend {
-
-		protected static final Map<Pair<Class<?>, FramsClass>, Backend> synchronizedCache = Collections.synchronizedMap(new HashMap<Pair<Class<?>, FramsClass>, Backend>());
-
-
-		public interface ReflectedGetter {
-			public <T> T get(Object object, Class<T> type) throws IllegalArgumentException, IllegalAccessException, InvocationTargetException;
-		}
-
-		public interface ReflectedSetter {
-			public <T> void set(Object object, T value) throws IllegalArgumentException, IllegalAccessException, InvocationTargetException;
-		}
-
-		public interface ReflectedCaller {
-			public Object call(Object object, Object[] arguments) throws IllegalArgumentException, IllegalAccessException, InvocationTargetException;
-		}
-
-		public interface ReflectedAdder{
-			public void reg(Object object, EventListener<?> listener) throws IllegalArgumentException, IllegalAccessException, InvocationTargetException;
-		}
-
-		public interface ReflectedRemover{
-			public void regRemove(Object object, EventListener<?> listener) throws IllegalArgumentException, IllegalAccessException, InvocationTargetException;
-		}
-
-		protected final Map<ValueParam, ReflectedSetter> setters = new IdentityHashMap<>();
-		protected final Map<ValueParam, ReflectedGetter> getters = new IdentityHashMap<>();
-		protected final Map<ProcedureParam, ReflectedCaller> callers = new IdentityHashMap<>();
-		protected final Map<EventParam, ReflectedAdder> adders = new IdentityHashMap<>();
-		protected final Map<EventParam, ReflectedRemover> removers = new IdentityHashMap<>();
-
-		protected final List<Method> autoAppendMethods = new ArrayList<>();
-
-		/**
-		 * @param params
-		 */
-		public Backend() {
-		}
-
-		public static Backend getOrCreateFor(Class<?> reflectedClass, FramsClass framsClass) {
-
-			Pair<Class<?>, FramsClass> id = new Pair<Class<?>, FramsClass>(reflectedClass, framsClass);
-			Backend backend = synchronizedCache.get(id);
-			if (backend != null) {
-				return backend;
-			}
-
-			log.debug("constructing backend for {}", id);
-			backend = new Backend();
-
-			Map<String, ParamCandidate> candidates = ParamCandidate.getAllCandidates(reflectedClass).getCandidates();
-
-			try {
-				for (final ProcedureParam pp : filterInstanceof(framsClass.getParamEntries(), ProcedureParam.class)) {
-					if (!candidates.containsKey(pp.getId())) {
-						log.trace("java class does implement method {}", pp);
-						continue;
-					}
-					ParamCandidate pc = candidates.get(pp.getId());
-					final Method method = pc.getCaller();
-
-					backend.callers.put(pp, new ReflectedCaller() {
-
-						@Override
-						public Object call(Object object, Object[] arguments) throws IllegalArgumentException, IllegalAccessException, InvocationTargetException {
-							return method.invoke(object, arguments);
-						}
-					});
-
-				}
-
-				for (final EventParam ep : filterInstanceof(framsClass.getParamEntries(), EventParam.class)) {
-					if (!candidates.containsKey(ep.getId())) {
-						log.trace("java class does not implement the event param {}", ep);
-						continue;
-					}
-					ParamCandidate ec = candidates.get(ep.getId());
-					final Method adder = ec.getAdder();
-					final Method remover = ec.getRemover();
-
-					backend.adders.put(ep, new ReflectedAdder() {
-
-						@Override
-						public void reg(Object object, EventListener<?> listener) throws IllegalArgumentException, IllegalAccessException, InvocationTargetException {
-							adder.invoke(object, listener);
-						}
-					});
-
-					backend.removers.put(ep, new ReflectedRemover() {
-
-						@Override
-						public void regRemove(Object object, EventListener<?> listener) throws IllegalArgumentException, IllegalAccessException, InvocationTargetException {
-							remover.invoke(object, listener);
-						}
-					});
-				}
-
-				for (final ValueParam vp : filterInstanceof(framsClass.getParamEntries(), ValueParam.class)) {
-					if (!candidates.containsKey(vp.getId())) {
-						throw new ConstructionException().msg("missing candidate for param").arg("param", vp);
-					}
-					ParamCandidate pc = candidates.get(vp.getId());
-					if (pc.isReadOnly() && !vp.hasFlag(ParamFlags.READONLY)) {
-						throw new ConstructionException().msg("readonly state conflict").arg("param", vp);
-					}
-					if (!typeMatch(pc.getRawType(), vp.getStorageType())) {
-						throw new ConstructionException().msg("types mismatch for param").arg("param", vp).arg("candidate", pc.getType()).arg("storage", vp.getStorageType());
-					}
-
-					final boolean primitive = pc.isPrimitive();
-					if (pc.getField() != null) {
-						final Field f = pc.getField();
-						backend.getters.put(vp, new ReflectedGetter() {
-							@Override
-							public <T> T get(Object object, Class<T> type) throws IllegalArgumentException, IllegalAccessException {
-								return type.cast(f.get(object));
-							}
-						});
-						if (!pc.isFinal()) {
-							backend.setters.put(vp, new ReflectedSetter() {
-								@Override
-								public <T> void set(Object object, T value) throws IllegalArgumentException, IllegalAccessException {
-									if (value == null && primitive) {
-										throw new FramsticksException().msg("setting null to primitive value");
-									}
-									f.set(object, value);
-								}
-							});
-						}
-					} else {
-						final Method g = pc.getGetter();
-
-						backend.getters.put(vp, new ReflectedGetter() {
-							@Override
-							public <T> T get(Object object, Class<T> type) throws IllegalArgumentException, IllegalAccessException, InvocationTargetException {
-								return type.cast(g.invoke(object));
-							}
-						});
-
-						if (!pc.isFinal()) {
-							final Method s = pc.getSetter();
-							backend.setters.put(vp, new ReflectedSetter() {
-								@Override
-								public <T> void set(Object object, T value) throws IllegalArgumentException, IllegalAccessException, InvocationTargetException {
-									if (value == null && primitive) {
-										throw new FramsticksException().msg("setting null to primitive value");
-									}
-									s.invoke(object, value);
-								}
-							});
-						}
-					}
-				}
-			} catch (ConstructionException e) {
-				throw e.arg("java class", reflectedClass).arg("framsClass", framsClass);
-			}
-
-			Class<?> javaClass = reflectedClass;
-			while (javaClass != null) {
-
-				for (Method m : javaClass.getDeclaredMethods()) {
-					AutoAppendAnnotation a = m.getAnnotation(AutoAppendAnnotation.class);
-					if (a == null) {
-						continue;
-					}
-					Class<?>[] args = m.getParameterTypes();
-					if (args.length != 1) {
-						throw new ConstructionException().msg("invalid number of arguments in AutoAppend marked method").arg("method", m).arg("arguments", args.length);
-					}
-					backend.autoAppendMethods.add(m);
-				}
-
-				javaClass = javaClass.getSuperclass();
-			}
-
-			Collections.sort(backend.autoAppendMethods, new Comparator<Method>() {
-
-				@Override
-				public int compare(Method m0, Method m1) {
-					Class<?> arg0 = m0.getParameterTypes()[0];
-					Class<?> arg1 = m1.getParameterTypes()[0];
-					if (arg0.isAssignableFrom(arg1)) {
-						return 1;
-					}
-					if (arg1.isAssignableFrom(arg0)) {
-						return -1;
-					}
-					return 0;
-				}
-			});
-
-			synchronizedCache.put(id, backend);
-			return backend;
-		}
-
-	}
-
-	public static boolean typeMatch(Class<?> a, Class<?> b) {
-		if (b.isPrimitive()) {
-			throw new FramsticksException().msg("failed to match type, right argument is primitive").arg("left", a).arg("right", b);
-		}
-		if (!a.isPrimitive()) {
-			return a.equals(b);
-		}
-
-		if (a.equals(int.class)) {
-			return b.equals(Integer.class);
-		}
-		if (a.equals(double.class)) {
-			return b.equals(Double.class);
-		}
-		if (a.equals(boolean.class)) {
-			return b.equals(Boolean.class);
-		}
-		throw new FramsticksException().msg("failed to match types").arg("left", a).arg("right", b);
-	}
-
-
-
 
 	public ReflectionAccess(Class<?> javaClass) throws ConstructionException {
@@ -268,8 +36,8 @@
 
 	public ReflectionAccess(Class<?> javaClass, FramsClass framsClass) throws ConstructionException {
-		this(javaClass, framsClass, Backend.getOrCreateFor(javaClass, framsClass));
-	}
-
-	protected ReflectionAccess(Class<?> javaClass, FramsClass framsClass, Backend backend) throws ConstructionException {
+		this(javaClass, framsClass, ReflectionAccessBackend.getOrCreateFor(javaClass, framsClass));
+	}
+
+	protected ReflectionAccess(Class<?> javaClass, FramsClass framsClass, ReflectionAccessBackend backend) throws ConstructionException {
 		super(framsClass);
 		this.javaClass = javaClass;
@@ -278,5 +46,5 @@
 
 	@Override
-	public ReflectionAccess cloneAccess() throws ConstructionException {
+	public ReflectionAccess cloneAccess() {
 		return new ReflectionAccess(javaClass, framsClass, backend);
 	}
@@ -290,5 +58,5 @@
 				}
 
-				return backend.getters.get(param).get(object, type);
+				return backend.getters.get(param.getId()).get(object, type);
 			} catch (IllegalAccessException | IllegalArgumentException | InvocationTargetException e) {
 				throw new FramsticksException().msg("failed to get").cause(e);
@@ -310,5 +78,5 @@
 					throw new FramsticksException().msg("no object set");
 				}
-				Backend.ReflectedSetter s = backend.setters.get(param);
+				ReflectionAccessBackend.ReflectedSetter s = backend.setters.get(param.getId());
 				if (s == null) {
 					throw new FramsticksException().msg("trying to set unsettable");
@@ -334,5 +102,5 @@
 				}
 
-				backend.adders.get(param).reg(object, listener);
+				backend.adders.get(param.getId()).reg(object, listener);
 				return;
 			} catch (IllegalAccessException | IllegalArgumentException | InvocationTargetException e) {
@@ -352,5 +120,5 @@
 				}
 
-				backend.removers.get(param).regRemove(object, listener);
+				backend.removers.get(param.getId()).regRemove(object, listener);
 				return;
 			} catch (IllegalAccessException | IllegalArgumentException | InvocationTargetException e) {
@@ -364,15 +132,10 @@
 	@Override
 	public Object call(String id, Object[] arguments) {
-		return call(framsClass.getParamEntry(id, ProcedureParam.class), arguments);
-	}
-
-	@Override
-	public Object call(ProcedureParam param, Object[] arguments) {
-		try {
-			try {
-				if (object == null) {
-					throw new FramsticksException().msg("no object set");
-				}
-				Backend.ReflectedCaller c = backend.callers.get(param);
+		try {
+			try {
+				if (object == null) {
+					throw new FramsticksException().msg("no object set");
+				}
+				ReflectionAccessBackend.ReflectedCaller c = backend.callers.get(id);
 				if (c == null) {
 					throw new FramsticksException().msg("method is not bound");
@@ -383,10 +146,11 @@
 			}
 		} catch (FramsticksException e) {
-			throw e.arg("param", param).arg("access", this);
-		}
-	}
-
-	void resetErrors() {
-		//TODO this replaces returnedObject.resetErrors();
+			throw e.arg("param", framsClass.getParam(id)).arg("access", this);
+		}
+	}
+
+	@Override
+	public Object call(ProcedureParam param, Object[] arguments) {
+		return call(param.getId(), arguments);
 	}
 
@@ -396,6 +160,4 @@
 			return;
 		}
-
-		resetErrors();
 
 		try {
@@ -416,5 +178,5 @@
 	@Override
 	public ReflectionAccess select(Object object) {
-		this.object = Util.selectObjectForAccess(this, object, javaClass);
+		this.object = ParamsUtil.selectObjectForAccess(this, object, javaClass);
 		return this;
 	}
@@ -425,22 +187,4 @@
 	}
 
-	// TODO: find a better place for it
-	public static String objectToString(Object object) {
-		StringBuilder b = new StringBuilder();
-		for (Field f : object.getClass().getFields()) {
-			b.append(f.getName());
-			b.append(":");
-			try {
-				Object value = f.get(object);
-				b.append((value != null) ? value.toString() : "<null>");
-			} catch (IllegalAccessException e) {
-				e.printStackTrace();
-			}
-			b.append("\n");
-		}
-		return b.toString();
-	}
-
-
 	@Override
 	public Object createAccessee() {
@@ -448,10 +192,7 @@
 			return javaClass.newInstance();
 		} catch (InstantiationException | IllegalAccessException e) {
-			e.printStackTrace();
-		}
-		log.fatal("failed to create reflected object of class {} for frams type {}", javaClass.getCanonicalName(), framsClass.getId());
-		return null;
-	}
-
+			throw new FramsticksException().msg("failed to create reflected object").arg("java class", javaClass).arg("frams class", framsClass).cause(e);
+		}
+	}
 
 	@Override
Index: java/main/src/main/java/com/framsticks/params/ReflectionAccessBackend.java
===================================================================
--- java/main/src/main/java/com/framsticks/params/ReflectionAccessBackend.java	(revision 101)
+++ java/main/src/main/java/com/framsticks/params/ReflectionAccessBackend.java	(revision 101)
@@ -0,0 +1,245 @@
+package com.framsticks.params;
+
+import java.lang.reflect.Field;
+import java.lang.reflect.InvocationTargetException;
+import java.lang.reflect.Method;
+import java.util.ArrayList;
+import java.util.Collections;
+import java.util.Comparator;
+import java.util.HashMap;
+import java.util.List;
+import java.util.Map;
+
+import javax.annotation.concurrent.Immutable;
+
+import com.framsticks.params.annotations.AutoAppendAnnotation;
+import com.framsticks.params.types.EventParam;
+import com.framsticks.params.types.ProcedureParam;
+import com.framsticks.util.FramsticksException;
+import com.framsticks.util.lang.Pair;
+
+
+import org.apache.logging.log4j.Logger;
+import org.apache.logging.log4j.LogManager;
+
+import static com.framsticks.util.lang.Containers.*;
+
+@Immutable
+public class ReflectionAccessBackend {
+
+	private final static Logger log = LogManager.getLogger(ReflectionAccessBackend.class.getName());
+
+	protected static final Map<Pair<Class<?>, FramsClass>, ReflectionAccessBackend> synchronizedCache = Collections.synchronizedMap(new HashMap<Pair<Class<?>, FramsClass>, ReflectionAccessBackend>());
+
+
+	public interface ReflectedGetter {
+		public <T> T get(Object object, Class<T> type) throws IllegalArgumentException, IllegalAccessException, InvocationTargetException;
+	}
+
+	public interface ReflectedSetter {
+		public <T> void set(Object object, T value) throws IllegalArgumentException, IllegalAccessException, InvocationTargetException;
+	}
+
+	public interface ReflectedCaller {
+		public Object call(Object object, Object[] arguments) throws IllegalArgumentException, IllegalAccessException, InvocationTargetException;
+	}
+
+	public interface ReflectedAdder{
+		public void reg(Object object, EventListener<?> listener) throws IllegalArgumentException, IllegalAccessException, InvocationTargetException;
+	}
+
+	public interface ReflectedRemover{
+		public void regRemove(Object object, EventListener<?> listener) throws IllegalArgumentException, IllegalAccessException, InvocationTargetException;
+	}
+
+	protected final Map<String, ReflectedSetter> setters = new HashMap<>();
+	protected final Map<String, ReflectedGetter> getters = new HashMap<>();
+	protected final Map<String, ReflectedCaller> callers = new HashMap<>();
+	protected final Map<String, ReflectedAdder> adders = new HashMap<>();
+	protected final Map<String, ReflectedRemover> removers = new HashMap<>();
+
+	protected final List<Method> autoAppendMethods = new ArrayList<>();
+
+	/**
+	 * @param params
+	 */
+	public ReflectionAccessBackend() {
+	}
+
+	public static ReflectionAccessBackend getOrCreateFor(Class<?> reflectedClass, FramsClass framsClass) {
+
+		Pair<Class<?>, FramsClass> id = new Pair<Class<?>, FramsClass>(reflectedClass, framsClass);
+		ReflectionAccessBackend backend = synchronizedCache.get(id);
+		if (backend != null) {
+			return backend;
+		}
+
+		log.debug("constructing backend for {}", id);
+		backend = new ReflectionAccessBackend();
+
+		Map<String, ParamCandidate> candidates = ParamCandidate.getAllCandidates(reflectedClass).getCandidates();
+
+		try {
+			for (final ProcedureParam pp : filterInstanceof(framsClass.getParamEntries(), ProcedureParam.class)) {
+				if (!candidates.containsKey(pp.getId())) {
+					log.trace("java class does implement method {}", pp);
+					continue;
+				}
+				ParamCandidate pc = candidates.get(pp.getId());
+				final Method method = pc.getCaller();
+
+				backend.callers.put(pp.getId(), new ReflectedCaller() {
+
+					@Override
+					public Object call(Object object, Object[] arguments) throws IllegalArgumentException, IllegalAccessException, InvocationTargetException {
+						return method.invoke(object, arguments);
+					}
+				});
+
+			}
+
+			for (final EventParam ep : filterInstanceof(framsClass.getParamEntries(), EventParam.class)) {
+				if (!candidates.containsKey(ep.getId())) {
+					log.trace("java class does not implement the event param {}", ep);
+					continue;
+				}
+				ParamCandidate ec = candidates.get(ep.getId());
+				final Method adder = ec.getAdder();
+				final Method remover = ec.getRemover();
+
+				backend.adders.put(ep.getId(), new ReflectedAdder() {
+
+					@Override
+					public void reg(Object object, EventListener<?> listener) throws IllegalArgumentException, IllegalAccessException, InvocationTargetException {
+						adder.invoke(object, listener);
+					}
+				});
+
+				backend.removers.put(ep.getId(), new ReflectedRemover() {
+
+					@Override
+					public void regRemove(Object object, EventListener<?> listener) throws IllegalArgumentException, IllegalAccessException, InvocationTargetException {
+						remover.invoke(object, listener);
+					}
+				});
+			}
+
+			for (final ValueParam vp : filterInstanceof(framsClass.getParamEntries(), ValueParam.class)) {
+				if (!candidates.containsKey(vp.getId())) {
+					throw new ConstructionException().msg("missing candidate for param").arg("param", vp);
+				}
+				ParamCandidate pc = candidates.get(vp.getId());
+				if (pc.isReadOnly() && !vp.hasFlag(ParamFlags.READONLY)) {
+					throw new ConstructionException().msg("readonly state conflict").arg("param", vp);
+				}
+				if (!typeMatch(pc.getRawType(), vp.getStorageType())) {
+					throw new ConstructionException().msg("types mismatch for param").arg("param", vp).arg("candidate", pc.getType()).arg("storage", vp.getStorageType());
+				}
+
+				final boolean primitive = pc.isPrimitive();
+				if (pc.getField() != null) {
+					final Field f = pc.getField();
+					backend.getters.put(vp.getId(), new ReflectedGetter() {
+						@Override
+						public <T> T get(Object object, Class<T> type) throws IllegalArgumentException, IllegalAccessException {
+							return type.cast(f.get(object));
+						}
+					});
+					if (!pc.isFinal()) {
+						backend.setters.put(vp.getId(), new ReflectedSetter() {
+							@Override
+							public <T> void set(Object object, T value) throws IllegalArgumentException, IllegalAccessException {
+								if (value == null && primitive) {
+									throw new FramsticksException().msg("setting null to primitive value");
+								}
+								f.set(object, value);
+							}
+						});
+					}
+				} else {
+					final Method g = pc.getGetter();
+
+					backend.getters.put(vp.getId(), new ReflectedGetter() {
+						@Override
+						public <T> T get(Object object, Class<T> type) throws IllegalArgumentException, IllegalAccessException, InvocationTargetException {
+							return type.cast(g.invoke(object));
+						}
+					});
+
+					if (!pc.isFinal()) {
+						final Method s = pc.getSetter();
+						backend.setters.put(vp.getId(), new ReflectedSetter() {
+							@Override
+							public <T> void set(Object object, T value) throws IllegalArgumentException, IllegalAccessException, InvocationTargetException {
+								if (value == null && primitive) {
+									throw new FramsticksException().msg("setting null to primitive value");
+								}
+								s.invoke(object, value);
+							}
+						});
+					}
+				}
+			}
+		} catch (ConstructionException e) {
+			throw e.arg("java class", reflectedClass).arg("framsClass", framsClass);
+		}
+
+		Class<?> javaClass = reflectedClass;
+		while (javaClass != null) {
+
+			for (Method m : javaClass.getDeclaredMethods()) {
+				AutoAppendAnnotation a = m.getAnnotation(AutoAppendAnnotation.class);
+				if (a == null) {
+					continue;
+				}
+				Class<?>[] args = m.getParameterTypes();
+				if (args.length != 1) {
+					throw new ConstructionException().msg("invalid number of arguments in AutoAppend marked method").arg("method", m).arg("arguments", args.length);
+				}
+				backend.autoAppendMethods.add(m);
+			}
+
+			javaClass = javaClass.getSuperclass();
+		}
+
+		Collections.sort(backend.autoAppendMethods, new Comparator<Method>() {
+
+			@Override
+			public int compare(Method m0, Method m1) {
+				Class<?> arg0 = m0.getParameterTypes()[0];
+				Class<?> arg1 = m1.getParameterTypes()[0];
+				if (arg0.isAssignableFrom(arg1)) {
+					return 1;
+				}
+				if (arg1.isAssignableFrom(arg0)) {
+					return -1;
+				}
+				return 0;
+			}
+		});
+
+		synchronizedCache.put(id, backend);
+		return backend;
+	}
+
+	public static boolean typeMatch(Class<?> a, Class<?> b) {
+		if (b.isPrimitive()) {
+			throw new FramsticksException().msg("failed to match type, right argument is primitive").arg("left", a).arg("right", b);
+		}
+		if (!a.isPrimitive()) {
+			return a.equals(b);
+		}
+
+		if (a.equals(int.class)) {
+			return b.equals(Integer.class);
+		}
+		if (a.equals(double.class)) {
+			return b.equals(Double.class);
+		}
+		if (a.equals(boolean.class)) {
+			return b.equals(Boolean.class);
+		}
+		throw new FramsticksException().msg("failed to match types").arg("left", a).arg("right", b);
+	}
+
+}
Index: java/main/src/main/java/com/framsticks/params/Registry.java
===================================================================
--- java/main/src/main/java/com/framsticks/params/Registry.java	(revision 100)
+++ java/main/src/main/java/com/framsticks/params/Registry.java	(revision 101)
@@ -8,4 +8,5 @@
 import com.framsticks.util.DoubleMap;
 import com.framsticks.util.FramsticksException;
+import com.framsticks.util.FramsticksUnsupportedOperationException;
 import com.framsticks.util.lang.Strings;
 
@@ -20,5 +21,5 @@
  */
 @FramsClassAnnotation
-public class Registry {
+public class Registry implements AccessProvider {
 	private static final Logger log = LogManager.getLogger(Registry.class.getName());
 
@@ -45,7 +46,10 @@
 
 	public Registry registerAndBuild(Class<?> javaClass) {
+		if (javaToFramsAssociation.containsKey(javaClass)) {
+			return this;
+		}
 		register(javaClass);
 		associate(javaClass, putFramsClass(FramsClass.build().forClass(javaClass)));
-		for (Class<?> r : javaClass.getAnnotation(FramsClassAnnotation.class).register()) {
+		for (Class<?> r : ParamCandidate.getAllCandidates(javaClass).getDependentClasses()) {
 			registerAndBuild(r);
 		}
@@ -157,3 +161,13 @@
 	}
 
+	@Override
+	public Access getAccess(String name) {
+		return createAccess(name);
+	}
+
+	@Override
+	public void addAccess(Access access) {
+		throw new FramsticksUnsupportedOperationException().msg("adding accesses to Registry");
+	}
+
 }
Index: java/main/src/main/java/com/framsticks/params/SimpleAbstractAccess.java
===================================================================
--- java/main/src/main/java/com/framsticks/params/SimpleAbstractAccess.java	(revision 100)
+++ java/main/src/main/java/com/framsticks/params/SimpleAbstractAccess.java	(revision 101)
@@ -4,10 +4,7 @@
 import static com.framsticks.util.lang.Containers.filterInstanceof;
 
-import org.apache.logging.log4j.Logger;
-import org.apache.logging.log4j.LogManager;
 
 import com.framsticks.params.types.ObjectParam;
 import com.framsticks.util.FramsticksException;
-import static com.framsticks.params.SetStateFlags.*;
 
 /**
@@ -23,7 +20,5 @@
  * @author Piotr Sniegowski
  */
-public abstract class SimpleAbstractAccess implements Access {
-
-	private final static Logger log = LogManager.getLogger(SimpleAbstractAccess.class.getName());
+public abstract class SimpleAbstractAccess implements ObjectAccess {
 
 	protected final FramsClass framsClass;
@@ -40,24 +35,4 @@
 		return framsClass;
 	}
-
-	/**
-	 * Simple String key, value class.
-	 */
-	public static class Entry {
-
-		public final String key;
-		public final String value;
-
-		public Entry(String key, String value) {
-			this.key = key;
-			this.value = value;
-		}
-
-		@Override
-		public String toString() {
-			return key + " = " + value;
-		}
-	}
-
 
 	@Override
@@ -129,51 +104,4 @@
 
 	@Override
-	public void setDefault(boolean numericOnly) {
-		for (int i = 0; i < framsClass.getParamCount(); i++) {
-			setDefault(i, numericOnly);
-		}
-	}
-
-	@Override
-	public void setDefault(int i, boolean numericOnly) {
-		ValueParam entry = framsClass.getParamEntry(i, ValueParam.class);
-		if ((entry != null)	&& (!numericOnly || entry.isNumeric())) {
-			set(i, entry.getDef(entry.getStorageType()));
-		}
-	}
-
-	@Override
-	public void setMin() {
-		for (int i = 0; i < framsClass.getParamCount(); i++) {
-			setMin(i);
-		}
-	}
-
-	@Override
-	public void setMin(int i) {
-		PrimitiveParam<?> entry = framsClass.getParamEntry(i, PrimitiveParam.class);
-		Object min = entry.getMin(entry.getStorageType());
-		if (min != null) {
-			set(i, min);
-		}
-	}
-
-	@Override
-	public void setMax() {
-		for (int i = 0; i < framsClass.getParamCount(); i++) {
-			setMax(i);
-		}
-	}
-
-	@Override
-	public void setMax(int i) {
-		PrimitiveParam<?> entry = framsClass.getParamEntry(i, PrimitiveParam.class);
-		Object max = entry.getMax(entry.getStorageType());
-		if (max != null) {
-			set(i, max);
-		}
-	}
-
-	@Override
 	public void save(SinkInterface sink) {
 		assert framsClass != null;
@@ -189,62 +117,4 @@
 		}
 		sink.breakLine();
-	}
-
-	private static Entry readEntry(SourceInterface source) {
-
-		String line;
-		String key = null;
-		StringBuilder value = null;
-		while ((line = source.readLine()) != null) {
-			if (key == null) {
-				int colonIndex = line.indexOf(':');
-				if (colonIndex == -1) {
-					return null;
-				}
-				key = line.substring(0, colonIndex);
-				String inlineValue = line.substring(colonIndex + 1);
-
-
-				if (!inlineValue.startsWith("~")) {
-					return new Entry(key, inlineValue);
-				}
-				value = new StringBuilder();
-				value.append(inlineValue.substring(1));
-				continue;
-			}
-			if (value.length() != 0) {
-				value.append(System.getProperty("line.separator"));
-			}
-			if (line.endsWith("~") && !line.endsWith("\\~")) {
-				value.append(line.substring(0, line.length() - 1));
-				return new Entry(key, value.toString().replaceAll("\\\\~", "~"));
-			}
-			value.append(line);
-		}
-		return null;
-	}
-
-	@Override
-	public void load(SourceInterface source) {
-		//TODO not clearing values, because get from manager gives only fields, not children
-		//this.clearValues();
-
-		Entry entry;
-		while ((entry = readEntry(source)) != null) {
-			Param param = getParam(entry.key);
-			if (param == null) {
-				throw new FramsticksException().msg("param not found in access").arg("name", entry.key).arg("access", this);
-			}
-			if (!(param instanceof ValueParam)) {
-				throw new FramsticksException().msg("param is not a value param").arg("param", param).arg("access", this);
-			}
-			if ((param.getFlags() & ParamFlags.DONTLOAD) == 0) {
-				int retFlags = this.set((ValueParam) param, entry.value);
-				if ((retFlags & (PSET_HITMIN | PSET_HITMAX)) != 0) {
-					String which = ((retFlags & PSET_HITMIN) != 0) ? "small" : "big";
-					log.warn("value of key '{}' was too {}, adjusted", entry.key, which);
-				}
-			}
-		}
 	}
 
@@ -266,23 +136,4 @@
 	}
 
-	/*
-	protected <T extends Comparable<T>> int setAndCut(Param param, Object value, Class<T> type) {
-		int flags = 0;
-		T val = type.cast(value);
-		T min = param.getMin(type);
-		T max = param.getMax(type);
-		if (min != null && val.compareTo(min) < 0) {
-			val = min;
-			flags |= Flags.PSET_HITMIN;
-		}
-		if (max != null && val.compareTo(max) > 0) {
-			val = max;
-			flags |= Flags.PSET_HITMAX;
-		}
-		internalSet(param, val);
-		return flags;
-	}*/
-
-
 	@Override
 	public ParamBuilder buildParam(ParamBuilder builder) {
Index: java/main/src/main/java/com/framsticks/params/SimpleSource.java
===================================================================
--- java/main/src/main/java/com/framsticks/params/SimpleSource.java	(revision 100)
+++ 	(revision )
@@ -1,57 +1,0 @@
-package com.framsticks.params;
-
-/**
- * @author Piotr Sniegowski
- */
-public class SimpleSource implements SourceInterface {
-
-	String content;
-	int cursor;
-
-	public SimpleSource(String content) {
-		this.content = content;
-		cursor = 0;
-	}
-
-	@Override
-	public String getFilename() {
-		return "<String>";
-	}
-
-	@Override
-	public String readLine() {
-		assert !isClosed();
-		if (cursor >= content.length()) {
-			return null;
-		}
-		int nextEndLine = content.indexOf('\n', cursor);
-		if (nextEndLine == -1) {
-			nextEndLine = content.length();
-		}
-		String result = content.substring(cursor, nextEndLine);
-		cursor = nextEndLine + 1;
-		return result;
-	}
-
-	@Override
-	public String demangleInclude(String include) {
-		return null;
-	}
-
-	@Override
-	public SourceInterface openInclude(String include) {
-		return null;
-	}
-
-	@Override
-	public void close() {
-		content = null;
-	}
-
-	//TODO no magic numbers
-	@Override
-	public boolean isClosed() {
-		return content != null;
-	}
-
-}
Index: java/main/src/main/java/com/framsticks/params/SimpleUniqueList.java
===================================================================
--- java/main/src/main/java/com/framsticks/params/SimpleUniqueList.java	(revision 101)
+++ java/main/src/main/java/com/framsticks/params/SimpleUniqueList.java	(revision 101)
@@ -0,0 +1,71 @@
+package com.framsticks.params;
+
+import java.util.Collections;
+import java.util.Iterator;
+import java.util.Map;
+
+import com.framsticks.core.ListChange;
+
+public class SimpleUniqueList<T extends UniqueObject> implements Iterable<T> {
+
+	protected final Map<String, T> children;
+	protected final Class<T> type;
+	protected final char prefix;
+	protected final EventListeners<ListChange> listeners = new EventListeners<>();
+
+	protected int counter = 0;
+
+	/**
+	 * @param type
+	 */
+	public SimpleUniqueList(Class<T> type, char prefix) {
+		this.type = type;
+		this.prefix = prefix;
+		children = UniqueListAccess.createMap(type, this);
+	}
+
+	// public static <T2> SimpleUniqueList<T2> make(Class<T2> type) {
+	//	return new SimpleUniqueList<T2>(type);
+	// }
+
+	public String generateId() {
+		return prefix + Integer.toString(counter++);
+	}
+
+	@Override
+	public Iterator<T> iterator() {
+		return children.values().iterator();
+	}
+
+	public void addListener(EventListener<ListChange> listener) {
+		listeners.add(listener);
+	}
+
+	public void removeListener(EventListener<ListChange> listener) {
+		listeners.remove(listener);
+	}
+
+	public void fireChildrenChange(T child, ListChange.Action action) {
+		ListChange change = new ListChange(action, UniqueListAccess.getNumberInMap(children, child), child.getUid());
+		listeners.actionForAll(change);
+	}
+
+	public void add(T child) {
+		child.setUid(generateId());
+		children.put(child.getUid(), child);
+		fireChildrenChange(child, ListChange.Action.Add);
+	}
+
+	public void remove(T child) {
+		fireChildrenChange(child, ListChange.Action.Remove);
+		children.remove(child.getUid());
+	}
+
+	public void modify(T child) {
+		fireChildrenChange(child, ListChange.Action.Modify);
+	}
+
+	public Map<String, T> getView() {
+		return Collections.unmodifiableMap(children);
+	}
+}
Index: java/main/src/main/java/com/framsticks/params/Source.java
===================================================================
--- java/main/src/main/java/com/framsticks/params/Source.java	(revision 101)
+++ java/main/src/main/java/com/framsticks/params/Source.java	(revision 101)
@@ -0,0 +1,10 @@
+package com.framsticks.params;
+
+public interface Source {
+	String getFilename();
+	String readLine();
+	String demangleInclude(String include);
+	Source openInclude(String include);
+	void close();
+	boolean isClosed();
+}
Index: java/main/src/main/java/com/framsticks/params/SourceInterface.java
===================================================================
--- java/main/src/main/java/com/framsticks/params/SourceInterface.java	(revision 100)
+++ 	(revision )
@@ -1,10 +1,0 @@
-package com.framsticks.params;
-
-public interface SourceInterface {
-	String getFilename();
-	String readLine();
-	String demangleInclude(String include);
-	SourceInterface openInclude(String include);
-	void close();
-	boolean isClosed();
-}
Index: java/main/src/main/java/com/framsticks/params/UniqueListAccess.java
===================================================================
--- java/main/src/main/java/com/framsticks/params/UniqueListAccess.java	(revision 100)
+++ java/main/src/main/java/com/framsticks/params/UniqueListAccess.java	(revision 101)
@@ -4,5 +4,5 @@
 import com.framsticks.util.FramsticksException;
 import com.framsticks.util.UnimplementedException;
-import com.framsticks.util.UnsupportedOperationException;
+import com.framsticks.util.FramsticksUnsupportedOperationException;
 import com.framsticks.util.lang.Casting;
 import com.framsticks.util.lang.Numbers;
@@ -205,5 +205,5 @@
 	@Override
 	public <T> int set(int i, T value) {
-		throw new UnsupportedOperationException().msg("accesing unique list through index");
+		throw new FramsticksUnsupportedOperationException().msg("accesing unique list through index");
 	}
 
@@ -251,5 +251,5 @@
 	@Override
 	public UniqueListAccess select(Object object) {
-		map = Util.selectObjectForAccess(this, object, Map.class);
+		map = ParamsUtil.selectObjectForAccess(this, object, Map.class);
 		return this;
 	}
Index: java/main/src/main/java/com/framsticks/params/UniqueObject.java
===================================================================
--- java/main/src/main/java/com/framsticks/params/UniqueObject.java	(revision 101)
+++ java/main/src/main/java/com/framsticks/params/UniqueObject.java	(revision 101)
@@ -0,0 +1,8 @@
+package com.framsticks.params;
+
+public interface UniqueObject {
+
+	public String getUid();
+
+	public void setUid(String uid);
+}
Index: java/main/src/main/java/com/framsticks/params/Util.java
===================================================================
--- java/main/src/main/java/com/framsticks/params/Util.java	(revision 100)
+++ 	(revision )
@@ -1,90 +1,0 @@
-package com.framsticks.params;
-
-import java.util.ArrayList;
-import java.util.List;
-
-import com.framsticks.util.FramsticksException;
-
-
-import static com.framsticks.util.lang.Containers.filterInstanceof;
-
-/**
- * @author Piotr Sniegowski
- */
-public abstract class Util {
-
-	public static String readSourceToString(SourceInterface source) {
-		StringBuilder result = new StringBuilder();
-		String line;
-		while ((line = source.readLine()) != null) {
-			result.append(line).append(" ");
-		}
-		source.close();
-		return result.toString();
-	}
-
-	public static <T> List<T> stripAccess(List<Access> accesses, Class<T> type) {
-		List<T> result = new ArrayList<T>();
-		for (Access a : accesses) {
-			Object object = a.getSelected();
-			if (!type.isInstance(object)) {
-				throw new FramsticksException().msg("extracted object is of invalid type").arg("object", object).arg("desired", type).arg("actual", object.getClass()).arg("framsclass", a.getFramsClass());
-			}
-			result.add(type.cast(object));
-		}
-		return result;
-	}
-
-	public static int takeAllNonNullValues(Access to, Access from) {
-		int copied = 0;
-		for (ValueParam f : filterInstanceof(from.getParams(), ValueParam.class)) {
-			Object v = from.get(f, Object.class);
-			if (v == null) {
-				continue;
-			}
-			// if (to.get(f, Object.class) != null) {
-			//	continue;
-			// }
-			to.set(f, v);
-			++copied;
-		}
-		return copied;
-	}
-
-	public static int copyExistingParamsTypeSafe(Access to, Access from) {
-		int copied = 0;
-		for (ValueParam f : filterInstanceof(from.getParams(), ValueParam.class)) {
-			Param t = from.getParam(f.getId());
-			if (!(t instanceof ValueParam)) {
-				continue;
-			}
-			if (to.getClass() != f.getClass()) {
-				continue;
-			}
-			to.set((ValueParam) t, from.get(f, Object.class));
-			++copied;
-		}
-		return copied;
-	}
-
-	public static <T> T selectObjectForAccess(Access access, Object object, Class<T> type) {
-		if (object == null) {
-			return null;
-		}
-		if (!type.isInstance(object)) {
-			throw new FramsticksException().msg("trying to select object of wrong type").arg("object", object).arg("type", object.getClass()).arg("in", access);
-		}
-		return type.cast(object);
-	}
-
-	public static int getNumberOfCompositeParamChild(Access access, Object child) {
-		int count = access.getCompositeParamCount();
-		for (int i = 0; i < count; ++i) {
-			if (access.get(i, Object.class) == child) {
-				return i;
-			}
-		}
-		return -1;
-	}
-
-}
Index: java/main/src/main/java/com/framsticks/params/annotations/ParamAnnotation.java
===================================================================
--- java/main/src/main/java/com/framsticks/params/annotations/ParamAnnotation.java	(revision 100)
+++ java/main/src/main/java/com/framsticks/params/annotations/ParamAnnotation.java	(revision 101)
@@ -22,5 +22,5 @@
 
 	String help() default "";
-	// int flags() default 0;
+	int flags() default 0;
 	int extra() default 0;
 	// String group() default "";
Index: java/main/src/main/java/com/framsticks/parsers/AccessStash.java
===================================================================
--- java/main/src/main/java/com/framsticks/parsers/AccessStash.java	(revision 101)
+++ java/main/src/main/java/com/framsticks/parsers/AccessStash.java	(revision 101)
@@ -0,0 +1,24 @@
+package com.framsticks.parsers;
+
+import java.util.HashMap;
+import java.util.Map;
+
+import com.framsticks.params.Access;
+import com.framsticks.params.AccessProvider;
+
+public class AccessStash implements AccessProvider {
+
+	protected final Map<String, Access> accesses = new HashMap<>();
+
+	@Override
+	public Access getAccess(String name) {
+		return accesses.get(name);
+	}
+
+	@Override
+	public void addAccess(Access access) {
+		/**TODO: by id or by name? rather by id, because from file is always lowercase*/
+		accesses.put(access.getId(), access);
+	}
+
+}
Index: java/main/src/main/java/com/framsticks/parsers/F0Parser.java
===================================================================
--- java/main/src/main/java/com/framsticks/parsers/F0Parser.java	(revision 100)
+++ java/main/src/main/java/com/framsticks/parsers/F0Parser.java	(revision 101)
@@ -12,5 +12,4 @@
 import com.framsticks.model.f0.Schema;
 
-import static com.framsticks.params.SimpleAbstractAccess.*;
 
 import com.framsticks.params.Param;
@@ -26,4 +25,5 @@
 import com.framsticks.params.FramsClass;
 import com.framsticks.params.Access;
+import com.framsticks.params.AccessOperations.Entry;
 import static com.framsticks.params.ParamFlags.*;
 import static com.framsticks.params.SetStateFlags.*;
Index: java/main/src/main/java/com/framsticks/parsers/FileSource.java
===================================================================
--- java/main/src/main/java/com/framsticks/parsers/FileSource.java	(revision 100)
+++ java/main/src/main/java/com/framsticks/parsers/FileSource.java	(revision 101)
@@ -1,5 +1,5 @@
 package com.framsticks.parsers;
 
-import com.framsticks.params.SourceInterface;
+import com.framsticks.params.Source;
 import com.framsticks.util.io.Encoding;
 
@@ -7,5 +7,5 @@
 
 
-public class FileSource implements SourceInterface {
+public class FileSource implements Source {
 
 	private BufferedReader reader;
@@ -60,5 +60,5 @@
 
 	@Override
-	public SourceInterface openInclude(String include)
+	public Source openInclude(String include)
 	{
 		try
@@ -89,3 +89,8 @@
 	}
 
+	@Override
+	public String toString() {
+		return filename;
+	}
+
 }
Index: java/main/src/main/java/com/framsticks/parsers/Loaders.java
===================================================================
--- java/main/src/main/java/com/framsticks/parsers/Loaders.java	(revision 100)
+++ java/main/src/main/java/com/framsticks/parsers/Loaders.java	(revision 101)
@@ -13,5 +13,5 @@
 	// private static final Logger log = LogManager.getLogger(Loaders.class.getName());
 
-	public static @Nonnull FramsClass loadFramsClass(SourceInterface source) throws ConstructionException {
+	public static @Nonnull FramsClass loadFramsClass(Source source) throws ConstructionException {
 		final MultiParamLoader loader = new MultiParamLoader();
 		loader.setNewSource(source);
Index: java/main/src/main/java/com/framsticks/parsers/MultiParamLoader.java
===================================================================
--- java/main/src/main/java/com/framsticks/parsers/MultiParamLoader.java	(revision 100)
+++ java/main/src/main/java/com/framsticks/parsers/MultiParamLoader.java	(revision 101)
@@ -68,5 +68,5 @@
 	 * File from which data should be read.
 	 */
-	private SourceInterface currentSource;
+	private Source currentSource;
 
 	protected String currentLine;
@@ -83,10 +83,10 @@
 	 * actual reader.
 	 */
-	private Map<String, SourceInterface> fileMap = new HashMap<String, SourceInterface>();
+	private Map<String, Source> fileMap = new HashMap<String, Source>();
 
 	/**
 	 * List of known classes.
 	 */
-	private Map<String, Access> knownParamInterfaces = new HashMap<String, Access>();
+	protected AccessProvider accessProvider = new AccessStash();
 
 	/**
@@ -100,4 +100,18 @@
 	public String getCurrentLine() {
 		return currentLine;
+	}
+
+	/**
+	 * @return the accessProvider
+	 */
+	public AccessProvider getAccessProvider() {
+		return accessProvider;
+	}
+
+	/**
+	 * @param accessProvider the accessProvider to set
+	 */
+	public void setAccessProvider(AccessProvider accessProvider) {
+		this.accessProvider = accessProvider;
 	}
 
@@ -184,5 +198,5 @@
 			}
 			log.trace("loading into {}", lastAccess);
-			lastAccess.load(currentSource);
+			AccessOperations.load(lastAccess, currentSource);
 
 			if (changeStatus(Status.AfterObject)) {
@@ -194,5 +208,5 @@
 
 			// found unknown object
-			emptyParam.load(currentSource);
+			AccessOperations.load(emptyParam, currentSource);
 			if (changeStatus(Status.AfterObject)) {
 				return LoopAction.Break;
@@ -262,5 +276,5 @@
 		if (line.charAt(line.length() - 1) == ':') {
 			String typeName = line.substring(0, line.length() - 1);
-			lastAccess = knownParamInterfaces.get(typeName);
+			lastAccess = accessProvider.getAccess(typeName);
 
 			if (lastAccess != null) {
@@ -300,6 +314,5 @@
 	 */
 	public void addAccess(Access access) {
-		/**TODO: by id or by name? rather by id, because from file is always lowercase*/
-		knownParamInterfaces.put(access.getId(), access);
+		accessProvider.addAccess(access);
 	}
 
@@ -324,5 +337,5 @@
 	 */
 
-	public boolean setNewSource(SourceInterface source) {
+	public boolean setNewSource(Source source) {
 		log.debug("switching current source to {}...", source.getFilename());
 
@@ -351,5 +364,5 @@
 		log.info("including file {}...", includeFilename);
 
-		SourceInterface newSource = currentSource.openInclude(includeFilename);
+		Source newSource = currentSource.openInclude(includeFilename);
 		if (newSource == null) {
 			return;
@@ -426,5 +439,5 @@
 	}
 
-	public static List<Object> loadAll(SourceInterface source, Access access) {
+	public static List<Object> loadAll(Source source, Access access) {
 		final List<Object> result = new LinkedList<>();
 
Index: java/main/src/main/java/com/framsticks/parsers/XmlLoader.java
===================================================================
--- java/main/src/main/java/com/framsticks/parsers/XmlLoader.java	(revision 100)
+++ java/main/src/main/java/com/framsticks/parsers/XmlLoader.java	(revision 101)
@@ -20,4 +20,6 @@
 
 import com.framsticks.params.Access;
+import com.framsticks.params.ParamFlags;
+import com.framsticks.params.PrimitiveParam;
 import com.framsticks.params.Registry;
 import com.framsticks.util.AutoBuilder;
@@ -107,5 +109,9 @@
 		for (int i = 0; i < attributes.getLength(); ++i) {
 			Node attributeNode = attributes.item(i);
-			access.set(mangleAttribute(attributeNode.getNodeName()), attributeNode.getNodeValue());
+			PrimitiveParam<?> param = access.getFramsClass().getParamEntry(mangleAttribute(attributeNode.getNodeName()), PrimitiveParam.class);
+			if (param.hasFlag(ParamFlags.READONLY)) {
+				throw new FramsticksException().msg("cannot configure readonly param").arg("param", param).arg("in", access);
+			}
+			access.set(param, attributeNode.getNodeValue());
 		}
 
Index: java/main/src/main/java/com/framsticks/remote/NodeListener.java
===================================================================
--- java/main/src/main/java/com/framsticks/remote/NodeListener.java	(revision 100)
+++ 	(revision )
@@ -1,13 +1,0 @@
-package com.framsticks.remote;
-
-import com.framsticks.core.ListChange;
-import com.framsticks.core.Path;
-
-/**
- * @author Piotr Sniegowski
- */
-public interface NodeListener {
-    void onUpgrade(Path path);
-    void onChange(Path path);
-    void onChildChange(Path path, ListChange.Action action);
-}
Index: java/main/src/main/java/com/framsticks/remote/RemoteTree.java
===================================================================
--- java/main/src/main/java/com/framsticks/remote/RemoteTree.java	(revision 100)
+++ java/main/src/main/java/com/framsticks/remote/RemoteTree.java	(revision 101)
@@ -7,8 +7,9 @@
 import com.framsticks.communication.queries.SetRequest;
 import com.framsticks.core.AbstractTree;
+import com.framsticks.core.ListChange;
 import com.framsticks.core.Path;
+import com.framsticks.core.ValueChange;
 import com.framsticks.params.*;
 import com.framsticks.params.EventListener;
-import com.framsticks.params.annotations.AutoAppendAnnotation;
 import com.framsticks.params.annotations.FramsClassAnnotation;
 import com.framsticks.params.annotations.ParamAnnotation;
@@ -17,9 +18,7 @@
 import com.framsticks.parsers.Loaders;
 import com.framsticks.parsers.MultiParamLoader;
-import com.framsticks.core.Tree;
 import com.framsticks.util.*;
 import com.framsticks.util.dispatching.AtOnceDispatcher;
 import com.framsticks.util.dispatching.Dispatching;
-import com.framsticks.util.dispatching.Dispatching.DispatcherWaiter;
 import com.framsticks.util.dispatching.DispatchingFuture;
 import com.framsticks.util.dispatching.ExceptionResultHandler;
@@ -52,20 +51,19 @@
 
 	public RemoteTree() {
+		bufferedDispatcher.setBuffer(true);
+		registry.registerAndBuild(ListChange.class);
+		registry.registerAndBuild(ValueChange.class);
 	}
 
 	@ParamAnnotation
 	public void setAddress(String address) {
-		setConnection(Connection.to(new ClientSideManagedConnection(), new Address(address)));
-	}
-
-	@ParamAnnotation
+		this.connection = Connection.to(new ClientSideManagedConnection(), new Address(address));
+		this.connection.setExceptionHandler(this);
+		this.connection.setNeedFileAcceptor(this);
+	}
+
+	@ParamAnnotation(flags = ParamFlags.USERREADONLY)
 	public String getAddress() {
 		return connection == null ? "<disconnected>" : connection.getAddress().toString();
-	}
-
-	@AutoAppendAnnotation
-	public void setConnection(final ClientSideManagedConnection connection) {
-		this.connection = connection;
-		this.connection.setExceptionHandler(this);
 	}
 
@@ -219,18 +217,17 @@
 		super.joinableStart();
 
-		dispatch(new RunAt<RemoteTree>(ThrowExceptionHandler.getInstance()) {
+		bufferedDispatcher.getTargetDispatcher().dispatch(new RunAt<RemoteTree>(ThrowExceptionHandler.getInstance()) {
 			@Override
 			protected void runAt() {
-				final DispatcherWaiter<Tree, Tree> waiter = new DispatcherWaiter<Tree, Tree>(RemoteTree.this);
-
-				connection.send(new InfoRequest().path("/"), waiter, new ClientSideResponseFuture(this) {
+
+				connection.send(new InfoRequest().path("/"), bufferedDispatcher.getTargetDispatcher(), new ClientSideResponseFuture(this) {
 					@Override
 					protected void processOk(Response response) {
 						FramsClass framsClass = processFetchedInfo(RemoteTree.this, response.getFiles().get(0));
 						assignRootParam(Param.build().name("Tree").id(RemoteTree.this.getName()).type("o " + framsClass.getId()).finish(CompositeParam.class));
+						bufferedDispatcher.setBuffer(false);
 					}
 				});
 
-				waiter.waitFor();
 			}
 		});
@@ -295,15 +292,9 @@
 					protected void runAt() {
 						assert isActive();
-						if (argumentType.equals(Object.class)) {
+						if (argumentType.equals(File.class)) {
 							listener.action(Casting.tryCast(argumentType, file));
 							return;
 						}
-						Access access = registry.createAccess(argumentType);
-						Object argument = access.createAccessee();
-						access.select(argument);
-						if (!argumentType.isInstance(argument)) {
-							throw new FramsticksException().msg("created argument is of wrond type").arg("expected", argumentType).arg("created", argument.getClass());
-						}
-						A typedArgument = argumentType.cast(argument);
+						// Access access = registry.createAccess(argumentType);
 
 						// log.info("executing event with argument {}", argumentType);
@@ -311,8 +302,16 @@
 						loader.setNewSource(file.getContent());
 						loader.addBreakCondition(MultiParamLoader.Status.AfterObject);
-						loader.addAccess(access);
+						loader.setAccessProvider(registry);
+						// loader.addAccess(access);
 						loader.go();
-
-						listener.action(typedArgument);
+						Object argument = loader.getLastAccess().getSelected();
+						// Object argument = access.getSelected();
+						if (argument == null) {
+							listener.action(null);
+						}
+						if (!argumentType.isInstance(argument)) {
+							throw new FramsticksException().msg("created argument is of wrong type").arg("expected", argumentType).arg("created", argument.getClass());
+						}
+						listener.action(argumentType.cast(argument));
 					}
 				});
Index: java/main/src/main/java/com/framsticks/running/ExternalProcess.java
===================================================================
--- java/main/src/main/java/com/framsticks/running/ExternalProcess.java	(revision 100)
+++ java/main/src/main/java/com/framsticks/running/ExternalProcess.java	(revision 101)
@@ -8,5 +8,4 @@
 import java.io.PrintWriter;
 import java.util.ArrayList;
-import java.util.LinkedList;
 import java.util.List;
 
@@ -15,4 +14,8 @@
 import org.apache.logging.log4j.LogManager;
 
+import com.framsticks.core.ValueChange;
+import com.framsticks.params.EventListener;
+import com.framsticks.params.EventListeners;
+import com.framsticks.params.ParamFlags;
 import com.framsticks.params.annotations.AutoAppendAnnotation;
 import com.framsticks.params.annotations.FramsClassAnnotation;
@@ -42,11 +45,20 @@
 	protected BufferedReader output;
 	protected Integer exitCode;
-
-	protected final List<OutputListener> listeners = new LinkedList<>();
+	protected String echoInput;
+
+	protected final EventListeners<ValueChange> listeners = new EventListeners<>();
 
 	@AutoAppendAnnotation
-	public void addListener(OutputListener listener) {
+	@ParamAnnotation(id = "line_output")
+	public void addOutputListener(EventListener<ValueChange> listener) {
 		synchronized (listeners) {
 			listeners.add(listener);
+		}
+	}
+
+	@ParamAnnotation(id = "line_output")
+	public void removeOutputListener(EventListener<ValueChange> listener) {
+		synchronized (listeners) {
+			listeners.remove(listener);
 		}
 	}
@@ -65,5 +77,5 @@
 	 * @return the command
 	 */
-	@ParamAnnotation
+	@ParamAnnotation(flags = ParamFlags.USERREADONLY)
 	public String getCommand() {
 		return arguments.get(0);
@@ -80,4 +92,5 @@
 	protected void readerTask() {
 
+		log.debug("reading output from " + this);
 		String line;
 		try {
@@ -86,7 +99,5 @@
 					log.trace("read line: {}", line);
 					synchronized (listeners) {
-						for (OutputListener l : listeners) {
-							l.onLineRead(line);
-						}
+						listeners.actionForAll(new ValueChange(line));
 					}
 				}
@@ -104,9 +115,9 @@
 			log.error("exception caught in process {}", this, e);
 		}
-		interrupt();
+		interruptJoinable();
 		// finish();
 	}
 
-	@ParamAnnotation
+	@ParamAnnotation(flags = ParamFlags.USERREADONLY)
 	public void setDirectory(String directory) {
 		builder.directory(new File(directory));
@@ -115,5 +126,5 @@
 	@ParamAnnotation
 	public String getDirectory() {
-		return builder.directory().getName();
+		return builder.directory() != null ? builder.directory().getName() : ".";
 	}
 
@@ -140,4 +151,9 @@
 		});
 		Dispatching.use(readerThread, this);
+
+		if (echoInput != null) {
+			input.println(echoInput);
+			input.flush();
+		}
 	}
 
@@ -152,4 +168,20 @@
 	public PrintWriter getInput() {
 		return input;
+	}
+
+	/**
+	 * @return the echoInput
+	 */
+	@ParamAnnotation(flags = ParamFlags.USERREADONLY)
+	public String getEchoInput() {
+		return echoInput;
+	}
+
+	/**
+	 * @param echoInput the echoInput to set
+	 */
+	@ParamAnnotation
+	public void setEchoInput(String echoInput) {
+		this.echoInput = echoInput;
 	}
 
@@ -162,5 +194,5 @@
 
 	@Override
-	@ParamAnnotation
+	@ParamAnnotation(flags = ParamFlags.USERREADONLY)
 	public String getName() {
 		return readerThread.getName();
Index: java/main/src/main/java/com/framsticks/running/LoggingOutputListener.java
===================================================================
--- java/main/src/main/java/com/framsticks/running/LoggingOutputListener.java	(revision 100)
+++ java/main/src/main/java/com/framsticks/running/LoggingOutputListener.java	(revision 101)
@@ -4,8 +4,10 @@
 import org.apache.logging.log4j.LogManager;
 
+import com.framsticks.core.ValueChange;
+import com.framsticks.params.EventListener;
 import com.framsticks.params.annotations.FramsClassAnnotation;
 
 @FramsClassAnnotation
-public class LoggingOutputListener implements OutputListener {
+public class LoggingOutputListener implements EventListener<ValueChange> {
 	private static final Logger log =
 		LogManager.getLogger(LoggingOutputListener.class);
@@ -13,6 +15,6 @@
 
 	@Override
-	public void onLineRead(String line) {
-		log.info(line);
+	public void action(ValueChange change) {
+		log.info(change.toString());
 	}
 
Index: java/main/src/main/java/com/framsticks/running/OutputListener.java
===================================================================
--- java/main/src/main/java/com/framsticks/running/OutputListener.java	(revision 100)
+++ 	(revision )
@@ -1,5 +1,0 @@
-package com.framsticks.running;
-
-public interface OutputListener {
-	void onLineRead(String line);
-}
Index: java/main/src/main/java/com/framsticks/test/ChangeEvent.java
===================================================================
--- java/main/src/main/java/com/framsticks/test/ChangeEvent.java	(revision 100)
+++ 	(revision )
@@ -1,17 +1,0 @@
-package com.framsticks.test;
-
-import com.framsticks.params.annotations.FramsClassAnnotation;
-import com.framsticks.params.annotations.ParamAnnotation;
-
-@FramsClassAnnotation
-public class ChangeEvent {
-
-	@ParamAnnotation
-	public String history;
-
-	@Override
-	public String toString() {
-		return "[test change event: " + history + "]";
-	}
-
-}
Index: java/main/src/main/java/com/framsticks/test/PrimeExperiment.java
===================================================================
--- java/main/src/main/java/com/framsticks/test/PrimeExperiment.java	(revision 101)
+++ java/main/src/main/java/com/framsticks/test/PrimeExperiment.java	(revision 101)
@@ -0,0 +1,9 @@
+package com.framsticks.test;
+
+import com.framsticks.experiment.Experiment;
+import com.framsticks.params.annotations.FramsClassAnnotation;
+
+@FramsClassAnnotation
+public class PrimeExperiment extends Experiment {
+
+}
Index: java/main/src/main/java/com/framsticks/test/TestChangeEvent.java
===================================================================
--- java/main/src/main/java/com/framsticks/test/TestChangeEvent.java	(revision 101)
+++ java/main/src/main/java/com/framsticks/test/TestChangeEvent.java	(revision 101)
@@ -0,0 +1,17 @@
+package com.framsticks.test;
+
+import com.framsticks.params.annotations.FramsClassAnnotation;
+import com.framsticks.params.annotations.ParamAnnotation;
+
+@FramsClassAnnotation
+public class TestChangeEvent {
+
+	@ParamAnnotation
+	public String history;
+
+	@Override
+	public String toString() {
+		return "[test change event: " + history + "]";
+	}
+
+}
Index: java/main/src/main/java/com/framsticks/test/TestChild.java
===================================================================
--- java/main/src/main/java/com/framsticks/test/TestChild.java	(revision 100)
+++ java/main/src/main/java/com/framsticks/test/TestChild.java	(revision 101)
@@ -1,14 +1,13 @@
 package com.framsticks.test;
 
-import com.framsticks.core.ListChange;
+import com.framsticks.params.AbstractUniqueObject;
 import com.framsticks.params.annotations.FramsClassAnnotation;
 import com.framsticks.params.annotations.ParamAnnotation;
 
 @FramsClassAnnotation
-public class TestChild {
+public class TestChild extends AbstractUniqueObject {
 
 	protected String name = "child";
 
-	protected String uid;
 	protected final TestClass parent;
 	/**
@@ -17,5 +16,4 @@
 	public TestChild(TestClass parent) {
 		this.parent = parent;
-		this.uid = "c" + (parent.counter++);
 	}
 
@@ -34,13 +32,5 @@
 	public void setName(String name) {
 		this.name = name;
-		parent.fireChildrenChange(this, ListChange.Action.Modify);
-	}
-
-	/**
-	 * @return the uid
-	 */
-	@ParamAnnotation
-	public String getUid() {
-		return uid;
+		parent.children.modify(this);
 	}
 
Index: java/main/src/main/java/com/framsticks/test/TestClass.java
===================================================================
--- java/main/src/main/java/com/framsticks/test/TestClass.java	(revision 100)
+++ java/main/src/main/java/com/framsticks/test/TestClass.java	(revision 101)
@@ -1,7 +1,4 @@
 package com.framsticks.test;
 
-import java.util.Collections;
-import java.util.LinkedList;
-import java.util.List;
 import java.util.Map;
 
@@ -11,5 +8,6 @@
 import com.framsticks.core.ListChange;
 import com.framsticks.params.EventListener;
-import com.framsticks.params.UniqueListAccess;
+import com.framsticks.params.EventListeners;
+import com.framsticks.params.SimpleUniqueList;
 import com.framsticks.params.annotations.FramsClassAnnotation;
 import com.framsticks.params.annotations.ParamAnnotation;
@@ -26,9 +24,4 @@
 		"createChild",
 		"children_changed"
-	},
-	register = {
-		ChangeEvent.class,
-		TestChild.class,
-		ListChange.class
 	}
 )
@@ -40,9 +33,7 @@
 	protected String name = "test";
 	protected String history = "initial|";
-	protected final List<EventListener<ChangeEvent>> historyListeners = new LinkedList<>();
-	protected final List<EventListener<ListChange>> childrenListeners = new LinkedList<>();
+	protected final EventListeners<TestChangeEvent> historyListeners = new EventListeners<>();
 
-	protected final Map<String, TestChild> children = UniqueListAccess.createMap(TestChild.class, this);
-	protected int counter = 0;
+	protected final SimpleUniqueList<TestChild> children = new SimpleUniqueList<>(TestChild.class, 'c');
 
 	/**
@@ -89,5 +80,5 @@
 	@ParamAnnotation
 	public Map<String, TestChild> getChildren() {
-		return Collections.unmodifiableMap(children);
+		return children.getView();
 	}
 
@@ -111,30 +102,21 @@
 		TestChild child = new TestChild(this);
 		child.name = name;
-		children.put(child.getUid(), child);
-		fireChildrenChange(child, ListChange.Action.Add);
+		children.add(child);
 	}
 
 	protected void fireHistoryChange() {
-		for (EventListener<ChangeEvent> l : historyListeners) {
-			ChangeEvent event = new ChangeEvent();
-			event.history = history;
-			l.action(event);
-		}
+		TestChangeEvent event = new TestChangeEvent();
+		event.history = history;
+		historyListeners.actionForAll(event);
 	}
 
-	protected void fireChildrenChange(TestChild child, ListChange.Action action) {
-		ListChange change = new ListChange(action, UniqueListAccess.getNumberInMap(children, child), child.getUid());
-		for (EventListener<ListChange> l : childrenListeners) {
-			l.action(change);
-		}
-	}
 
 	@ParamAnnotation(id = "history_changed")
-	public void addHistoryListener(EventListener<ChangeEvent> listener) {
+	public void addHistoryListener(EventListener<TestChangeEvent> listener) {
 		historyListeners.add(listener);
 	}
 
 	@ParamAnnotation(id = "history_changed")
-	public void removeHistoryListener(EventListener<ChangeEvent> listener) {
+	public void removeHistoryListener(EventListener<TestChangeEvent> listener) {
 		historyListeners.remove(listener);
 	}
@@ -142,10 +124,10 @@
 	@ParamAnnotation(id = "children_changed")
 	public void addChildrenListener(EventListener<ListChange> listener) {
-		childrenListeners.add(listener);
+		children.addListener(listener);
 	}
 
 	@ParamAnnotation(id = "children_changed")
 	public void removeChildrenListener(EventListener<ListChange> listener) {
-		childrenListeners.remove(listener);
+		children.removeListener(listener);
 	}
 
Index: java/main/src/main/java/com/framsticks/util/FramsticksUnsupportedOperationException.java
===================================================================
--- java/main/src/main/java/com/framsticks/util/FramsticksUnsupportedOperationException.java	(revision 101)
+++ java/main/src/main/java/com/framsticks/util/FramsticksUnsupportedOperationException.java	(revision 101)
@@ -0,0 +1,13 @@
+package com.framsticks.util;
+
+/**
+ * @author Piotr Sniegowski
+ */
+public class FramsticksUnsupportedOperationException extends FramsticksException {
+
+	/**
+	 *
+	 */
+	private static final long serialVersionUID = -8006030446614381094L;
+
+}
Index: java/main/src/main/java/com/framsticks/util/PeriodicTask.java
===================================================================
--- java/main/src/main/java/com/framsticks/util/PeriodicTask.java	(revision 100)
+++ 	(revision )
@@ -1,33 +1,0 @@
-package com.framsticks.util;
-
-import com.framsticks.util.dispatching.Dispatcher;
-import com.framsticks.util.dispatching.ExceptionResultHandler;
-import com.framsticks.util.dispatching.Task;
-import com.framsticks.util.dispatching.RunAt;
-
-/**
- * @author Piotr Sniegowski
- */
-public abstract class PeriodicTask<C> extends RunAt<C> {
-
-	protected final long period;
-	protected Dispatcher<? super C> dispatcher;
-
-	public PeriodicTask(ExceptionResultHandler handler, Dispatcher<? super C> dispatcher, long period) {
-		super(handler);
-		this.period = period;
-		this.dispatcher = dispatcher;
-		dispatcher.dispatch(this);
-	}
-
-
-	public void again() {
-		dispatcher.dispatch(new Task<C>(handler, period) {
-			@Override
-			protected void runAt() {
-				PeriodicTask.this.runAt();
-			}
-		});
-	}
-
-}
Index: java/main/src/main/java/com/framsticks/util/UnaryFunctor.java
===================================================================
--- java/main/src/main/java/com/framsticks/util/UnaryFunctor.java	(revision 100)
+++ 	(revision )
@@ -1,8 +1,0 @@
-package com.framsticks.util;
-
-/**
- * @author Piotr Sniegowski
- */
-public interface UnaryFunctor <Ret, Arg> {
-	Ret call(Arg arg);
-}
Index: java/main/src/main/java/com/framsticks/util/UnaryListenersSet.java
===================================================================
--- java/main/src/main/java/com/framsticks/util/UnaryListenersSet.java	(revision 100)
+++ 	(revision )
@@ -1,27 +1,0 @@
-package com.framsticks.util;
-
-
-import java.util.HashSet;
-import java.util.Iterator;
-import java.util.Set;
-
-/**
- * @author Piotr Sniegowski
- */
-public class UnaryListenersSet<T> {
-	Set<UnaryFunctor<Boolean, T>> listeners = new HashSet<UnaryFunctor<Boolean, T>>();
-
-	public void add(UnaryFunctor<Boolean, T> listener) {
-		listeners.add(listener);
-	}
-
-	public void call(T argument) {
-		Iterator<UnaryFunctor<Boolean, T>> iterator = listeners.iterator();
-		while (iterator.hasNext()) {
-			if (iterator.next().call(argument)) {
-				iterator.remove();
-			}
-		}
-	}
-
-}
Index: java/main/src/main/java/com/framsticks/util/UnsupportedOperationException.java
===================================================================
--- java/main/src/main/java/com/framsticks/util/UnsupportedOperationException.java	(revision 100)
+++ 	(revision )
@@ -1,13 +1,0 @@
-package com.framsticks.util;
-
-/**
- * @author Piotr Sniegowski
- */
-public class UnsupportedOperationException extends FramsticksException {
-
-	/**
-	 *
-	 */
-	private static final long serialVersionUID = -8006030446614381094L;
-
-}
Index: java/main/src/main/java/com/framsticks/util/dispatching/AbstractJoinable.java
===================================================================
--- java/main/src/main/java/com/framsticks/util/dispatching/AbstractJoinable.java	(revision 100)
+++ java/main/src/main/java/com/framsticks/util/dispatching/AbstractJoinable.java	(revision 101)
@@ -39,5 +39,5 @@
 		synchronized (joinablesRegistry) {
 			for (AbstractJoinable j : joinablesRegistry) {
-				b.append("\n").append(j.getState()).append(" : ").append(j);
+				b.append("\n").append(j.getState()).append(" : ").append(j).append(" - ").append(j.getClass());
 			}
 		}
@@ -73,5 +73,5 @@
 
 
-	public final boolean start() {
+	protected final boolean startJoinable() {
 		if (changeState(JoinableState.RUNNING)) {
 			joinableStart();
@@ -81,5 +81,5 @@
 	}
 
-	public final boolean interrupt() {
+	protected final boolean interruptJoinable() {
 		if (changeState(JoinableState.FINISHING)) {
 			joinableInterrupt();
@@ -89,5 +89,5 @@
 	}
 
-	protected final boolean finish() {
+	protected final boolean finishJoinable() {
 		if (changeState(JoinableState.JOINABLE)) {
 			joinableFinish();
@@ -118,9 +118,9 @@
 		switch (state) {
 			case RUNNING:
-				return start();
+				return startJoinable();
 			case FINISHING:
-				return interrupt();
+				return interruptJoinable();
 			case JOINABLE:
-				return finish();
+				return finishJoinable();
 			default: return false;
 		}
@@ -149,5 +149,5 @@
 			assert monitor == null;
 			monitor = owner.getMonitor();
-			return this.start();
+			return this.startJoinable();
 		}
 		return false;
@@ -170,5 +170,5 @@
 				@Override
 				protected void runAt() {
-					interrupt();
+					interruptJoinable();
 				}
 			});
Index: java/main/src/main/java/com/framsticks/util/dispatching/AtOnceDispatcher.java
===================================================================
--- java/main/src/main/java/com/framsticks/util/dispatching/AtOnceDispatcher.java	(revision 100)
+++ java/main/src/main/java/com/framsticks/util/dispatching/AtOnceDispatcher.java	(revision 101)
@@ -5,5 +5,5 @@
  * @author Piotr Sniegowski
  */
-public class AtOnceDispatcher<C> extends AbstractJoinable implements JoinableDispatcher<C> {
+public class AtOnceDispatcher<C> extends AbstractJoinable implements Dispatcher<C> {
 
 	@SuppressWarnings("rawtypes")
@@ -37,5 +37,5 @@
 	@Override
 	protected void joinableInterrupt() {
-		finish();
+		finishJoinable();
 	}
 
Index: java/main/src/main/java/com/framsticks/util/dispatching/BufferedDispatcher.java
===================================================================
--- java/main/src/main/java/com/framsticks/util/dispatching/BufferedDispatcher.java	(revision 101)
+++ java/main/src/main/java/com/framsticks/util/dispatching/BufferedDispatcher.java	(revision 101)
@@ -0,0 +1,58 @@
+package com.framsticks.util.dispatching;
+
+
+public class BufferedDispatcher<C> {
+
+	protected final RunnableQueue<C> queue = new RunnableQueue<>();
+
+	protected Dispatcher<C> targetDispatcher;
+
+	protected boolean buffer = false;
+
+	/**
+	 * @return the targetDispatcher
+	 */
+	public synchronized Dispatcher<C> getTargetDispatcher() {
+		return targetDispatcher;
+	}
+
+	/**
+	 * @param targetDispatcher the targetDispatcher to set
+	 */
+	public synchronized void setTargetDispatcher(Dispatcher<C> targetDispatcher) {
+		this.targetDispatcher = targetDispatcher;
+	}
+
+	public synchronized boolean isActive() {
+		if (targetDispatcher == null) {
+			return false;
+			// throw new FramsticksException().msg("no dispatcher is set for tree yet").arg("tree", this);
+		}
+		return targetDispatcher.isActive();
+	}
+
+	public synchronized void dispatch(final RunAt<? extends C> runnable) {
+		if (targetDispatcher != null && !buffer) {
+			targetDispatcher.dispatch(runnable);
+			return;
+		}
+		queue.push(runnable);
+	}
+
+	public synchronized void setBuffer(final boolean buffer) {
+		if (this.buffer == buffer) {
+			return;
+		}
+		if (buffer) {
+			this.buffer = true;
+			return;
+		}
+		this.buffer = false;
+		while (!queue.isEmpty()) {
+			targetDispatcher.dispatch(queue.pollFirst());
+		}
+
+	}
+
+
+}
Index: java/main/src/main/java/com/framsticks/util/dispatching/Dispatcher.java
===================================================================
--- java/main/src/main/java/com/framsticks/util/dispatching/Dispatcher.java	(revision 100)
+++ java/main/src/main/java/com/framsticks/util/dispatching/Dispatcher.java	(revision 101)
@@ -4,5 +4,5 @@
  * @author Piotr Sniegowski
  */
-public interface Dispatcher<C> {
+public interface Dispatcher<C> extends Joinable {
 	public boolean isActive();
 	public void dispatch(RunAt<? extends C> runnable);
Index: java/main/src/main/java/com/framsticks/util/dispatching/Dispatching.java
===================================================================
--- java/main/src/main/java/com/framsticks/util/dispatching/Dispatching.java	(revision 100)
+++ java/main/src/main/java/com/framsticks/util/dispatching/Dispatching.java	(revision 101)
@@ -1,3 +1,5 @@
 package com.framsticks.util.dispatching;
+
+import java.util.Timer;
 
 import org.apache.logging.log4j.Logger;
@@ -11,4 +13,10 @@
 public abstract class Dispatching {
 	private static final Logger log = LogManager.getLogger(Dispatching.class);
+
+	protected static final Timer timer = new Timer();
+
+	public static Timer getTimer() {
+		return timer;
+	}
 
 	public static boolean isThreadSafe() {
@@ -183,41 +191,41 @@
 	}
 
-	public static class DispatcherWaiter<C, T extends Dispatcher<C> & Joinable> implements Dispatcher<C> {
-		// protected boolean done = false;
-		protected final T dispatcher;
-		protected RunAt<? extends C> runnable;
-
-		/**
-		 * @param joinable
-		 */
-		public DispatcherWaiter(T dispatcher) {
-			this.dispatcher = dispatcher;
-		}
-
-		public synchronized void waitFor() {
-			while ((runnable == null) && (dispatcher.getState().ordinal() <= JoinableState.RUNNING.ordinal())) {
-				try {
-					this.wait();
-				} catch (InterruptedException e) {
-				}
-			}
-			if (runnable != null) {
-				runnable.run();
-			}
-
-		}
-
-		@Override
-		public boolean isActive() {
-			return dispatcher.isActive();
-		}
-
-		@Override
-		public synchronized void dispatch(RunAt<? extends C> runnable) {
-			this.runnable = runnable;
-			this.notifyAll();
-		}
-
-	}
+	// public static class DispatcherWaiter<C, T extends Dispatcher<C> & Joinable> implements Dispatcher<C> {
+	// 	// protected boolean done = false;
+	// 	protected final T dispatcher;
+	// 	protected RunAt<? extends C> runnable;
+
+	// 	/**
+	// 	 * @param joinable
+	// 	 */
+	// 	public DispatcherWaiter(T dispatcher) {
+	// 		this.dispatcher = dispatcher;
+	// 	}
+
+	// 	public synchronized void waitFor() {
+	// 		while ((runnable == null) && (dispatcher.getState().ordinal() <= JoinableState.RUNNING.ordinal())) {
+	// 			try {
+	// 				this.wait();
+	// 			} catch (InterruptedException e) {
+	// 			}
+	// 		}
+	// 		if (runnable != null) {
+	// 			runnable.run();
+	// 		}
+
+	// 	}
+
+	// 	@Override
+	// 	public boolean isActive() {
+	// 		return dispatcher.isActive();
+	// 	}
+
+	// 	@Override
+	// 	public synchronized void dispatch(RunAt<? extends C> runnable) {
+	// 		this.runnable = runnable;
+	// 		this.notifyAll();
+	// 	}
+
+	// }
 
 	public static class Waiter {
Index: java/main/src/main/java/com/framsticks/util/dispatching/JoinableCollection.java
===================================================================
--- java/main/src/main/java/com/framsticks/util/dispatching/JoinableCollection.java	(revision 100)
+++ java/main/src/main/java/com/framsticks/util/dispatching/JoinableCollection.java	(revision 101)
@@ -8,6 +8,8 @@
 import java.util.Set;
 
+import com.framsticks.params.ParamFlags;
 import com.framsticks.params.annotations.AutoAppendAnnotation;
 import com.framsticks.params.annotations.FramsClassAnnotation;
+import com.framsticks.params.annotations.ParamAnnotation;
 import com.framsticks.util.FramsticksException;
 import com.framsticks.util.Misc;
@@ -72,5 +74,5 @@
 	protected void joinableInterrupt() {
 		if (joinables.isEmpty()) {
-			finish();
+			finishJoinable();
 			return;
 		}
@@ -137,5 +139,5 @@
 	public T get(String name) {
 		for (T j : joinables) {
-			if (j.getName().equals(name)) {
+			if (name.equals(j.getName())) {
 				return j;
 			}
@@ -153,6 +155,12 @@
 
 	@Override
+	@ParamAnnotation
 	public String getName() {
 		return observableName;
+	}
+
+	@ParamAnnotation(flags = ParamFlags.USERREADONLY)
+	public final void setName(String name) {
+		observableName = name;
 	}
 
@@ -185,3 +193,5 @@
 	}
 
+
+
 }
Index: java/main/src/main/java/com/framsticks/util/dispatching/JoinableDispatcher.java
===================================================================
--- java/main/src/main/java/com/framsticks/util/dispatching/JoinableDispatcher.java	(revision 100)
+++ 	(revision )
@@ -1,5 +1,0 @@
-package com.framsticks.util.dispatching;
-
-public interface JoinableDispatcher<C> extends Dispatcher<C>, Joinable {
-
-}
Index: java/main/src/main/java/com/framsticks/util/dispatching/RunnableQueue.java
===================================================================
--- java/main/src/main/java/com/framsticks/util/dispatching/RunnableQueue.java	(revision 101)
+++ java/main/src/main/java/com/framsticks/util/dispatching/RunnableQueue.java	(revision 101)
@@ -0,0 +1,21 @@
+package com.framsticks.util.dispatching;
+
+import java.util.LinkedList;
+
+public class RunnableQueue<C> {
+
+	protected final LinkedList<RunAt<? extends C>> queue = new LinkedList<>();
+
+	public boolean isEmpty() {
+		return queue.isEmpty();
+	}
+
+	public RunAt<? extends C> pollFirst() {
+		return queue.pollFirst();
+	}
+
+	public void push(RunAt<? extends C> runnable) {
+		queue.add(runnable);
+	}
+
+}
Index: java/main/src/main/java/com/framsticks/util/dispatching/Task.java
===================================================================
--- java/main/src/main/java/com/framsticks/util/dispatching/Task.java	(revision 100)
+++ 	(revision )
@@ -1,24 +1,0 @@
-package com.framsticks.util.dispatching;
-
-/**
- * @author Piotr Sniegowski
- */
-public abstract class Task<C> extends RunAt<C> {
-
-	protected final long moment;
-
-	public Task(ExceptionResultHandler handler) {
-		super(handler);
-		moment = System.currentTimeMillis();
-	}
-
-	public Task(ExceptionResultHandler handler, long moment) {
-		super(handler);
-		this.moment = System.currentTimeMillis() + moment;
-	}
-
-	public final long getMoment() {
-		return moment;
-	}
-
-}
Index: java/main/src/main/java/com/framsticks/util/dispatching/Thread.java
===================================================================
--- java/main/src/main/java/com/framsticks/util/dispatching/Thread.java	(revision 100)
+++ java/main/src/main/java/com/framsticks/util/dispatching/Thread.java	(revision 101)
@@ -3,7 +3,4 @@
 import org.apache.logging.log4j.Logger;
 import org.apache.logging.log4j.LogManager;
-
-import java.util.LinkedList;
-import java.util.ListIterator;
 
 
@@ -14,5 +11,5 @@
  * @author Piotr Sniegowski
  */
-public class Thread<C> extends AbstractJoinable implements JoinableDispatcher<C> {
+public class Thread<C> extends AbstractJoinable implements Dispatcher<C> {
 
 	private static final Logger log = LogManager.getLogger(Thread.class);
@@ -20,8 +17,9 @@
 	protected final java.lang.Thread thread;
 
-	private final LinkedList<Task<? extends C>> queue = new LinkedList<>();
+	protected final Object condition = new Object();
+	private RunnableQueue<C> queue = new RunnableQueue<>();
 
 	public Thread() {
-		thread = new java.lang.Thread(new java.lang.Runnable() {
+		thread = new java.lang.Thread(new Runnable() {
 			@Override
 			public void run() {
@@ -30,5 +28,4 @@
 		});
 	}
-
 
 	public Thread(java.lang.Thread thread) {
@@ -51,9 +48,9 @@
 		ExceptionHandler exceptionHandler = getMonitor().getTaskExceptionHandler();
 		while (!java.lang.Thread.interrupted()) {
-			Task<? extends C> task;
-			synchronized (queue) {
+			RunAt<? extends C> runnable;
+			synchronized (condition) {
 				if (queue.isEmpty()) {
 					try {
-						queue.wait();
+						condition.wait();
 					} catch (InterruptedException ignored) {
 						break;
@@ -61,72 +58,38 @@
 					continue;
 				}
-				task = queue.peekFirst();
-				assert task != null;
-				if (task.moment > System.currentTimeMillis()) {
-					try {
-						queue.wait(task.moment - System.currentTimeMillis());
-					} catch (InterruptedException ignored) {
-						continue;
+				runnable = queue.pollFirst();
+			}
+			if (runnable != null) {
+				try {
+					runnable.run();
+				} catch (Exception e) {
+					if (exceptionHandler != null) {
+						if (exceptionHandler.handle(this, e)) {
+							continue;
+						}
 					}
-					continue;
+					log.error("error in thread: ", e);
 				}
-				queue.pollFirst();
-			}
-			try {
-				task.run();
-			} catch (Exception e) {
-				if (exceptionHandler != null) {
-					if (exceptionHandler.handle(this, e)) {
-						continue;
-					}
-				}
-				log.error("error in thread: ", e);
 			}
 		}
 		log.debug("finishing thread {}", this);
-		finish();
+		finishJoinable();
 	}
 
-	protected void enqueueTask(Task<? extends C> task) {
-		synchronized (queue) {
-			ListIterator<Task<? extends C>> i = queue.listIterator();
-			while (i.hasNext()) {
-				Task<? extends C> t = i.next();
-				if (t.getMoment() > task.getMoment()) {
-					i.previous();
-					i.add(task);
-					task = null;
-					break;
-				}
-			}
-			if (task != null) {
-				queue.add(task);
-			}
 
-			/*
-			Iterator<Task> j = queue.iterator();
-			Task prev = null;
-			while (j.hasNext()) {
-				Task next = j.next();
-				assert (prev == null) || prev.getMoment() <= next.getMoment();
-				prev = next;
-			}
-			*/
-			queue.notify();
+	@Override
+	public void dispatch(RunAt<? extends C> runnable) {
+		synchronized (condition) {
+			queue.push(runnable);
+			condition.notifyAll();
 		}
 	}
 
-	@Override
-	public void dispatch(final RunAt<? extends C> runnable) {
-		if (!(runnable instanceof Task)) {
-			enqueueTask(new Task<C>(runnable) {
-				@Override
-				protected void runAt() {
-					runnable.run();
-				}
-			});
-			return;
+	public RunnableQueue<C> switchQueue(RunnableQueue<C> queue) {
+		synchronized (condition) {
+			RunnableQueue<C> result = this.queue;
+			this.queue = queue;
+			return result;
 		}
-		enqueueTask((Task<? extends C>) runnable);
 	}
 
Index: java/main/src/main/java/com/framsticks/util/lang/Holder.java
===================================================================
--- java/main/src/main/java/com/framsticks/util/lang/Holder.java	(revision 100)
+++ java/main/src/main/java/com/framsticks/util/lang/Holder.java	(revision 101)
@@ -33,3 +33,7 @@
 	}
 
+	public static <T2> Holder<T2> make(T2 value) {
+		return new Holder<T2>(value);
+	}
+
 }
Index: java/main/src/main/java/com/framsticks/util/lang/Strings.java
===================================================================
--- java/main/src/main/java/com/framsticks/util/lang/Strings.java	(revision 100)
+++ java/main/src/main/java/com/framsticks/util/lang/Strings.java	(revision 101)
@@ -1,3 +1,5 @@
 package com.framsticks.util.lang;
+
+import java.util.regex.Matcher;
 
 import com.framsticks.util.FramsticksException;
@@ -78,3 +80,8 @@
 	}
 
+	public static CharSequence takeGroup(CharSequence input, Matcher matcher, int group) {
+		// return (matcher.start(group) == matcher.end(group)) ? null : input.subSequence(matcher.start(group), matcher.end(group));
+		return input.subSequence(matcher.start(group), matcher.end(group));
+	}
+
 }
Index: java/main/src/main/resources/bash-gui-runner.xml
===================================================================
--- java/main/src/main/resources/bash-gui-runner.xml	(revision 101)
+++ java/main/src/main/resources/bash-gui-runner.xml	(revision 101)
@@ -0,0 +1,21 @@
+<?xml version="1.0" encoding="UTF-8"?>
+<Framsticks>
+	<import class="com.framsticks.gui.Browser" />
+	<import class="com.framsticks.core.LocalTree" />
+	<import class="com.framsticks.running.LoggingOutputListener" />
+	<import class="com.framsticks.running.FramsServer" />
+	<import class="com.framsticks.running.ExternalProcess" />
+	<Browser>
+		<LocalTree>
+			<ExternalProcess name="bash" command="bash" echoInput="while true ; do date ; sleep 1 ; done">
+				<LoggingOutputListener />
+			</ExternalProcess>
+		</LocalTree>
+		<LocalTree>
+			<FramsServer name="frams" port="9100">
+				<LoggingOutputListener />
+			</FramsServer>
+		</LocalTree>
+	</Browser>
+</Framsticks>
+
Index: java/main/src/main/resources/common-columns.xml
===================================================================
--- java/main/src/main/resources/common-columns.xml	(revision 101)
+++ java/main/src/main/resources/common-columns.xml	(revision 101)
@@ -0,0 +1,5 @@
+<?xml version="1.0" encoding="UTF-8"?>
+<AutoInjector>
+	<ColumnsConfig className="Genotype" columnsNames="uid name mutate" />
+	<ColumnsConfig className="Part" columnsNames="x y z" />
+</AutoInjector>
Index: java/main/src/main/resources/framsticks.xml
===================================================================
--- java/main/src/main/resources/framsticks.xml	(revision 101)
+++ java/main/src/main/resources/framsticks.xml	(revision 101)
@@ -0,0 +1,31 @@
+<?xml version="1.0" encoding="UTF-8"?>
+<Framsticks>
+	<import class="com.framsticks.gui.Browser" />
+	<import class="com.framsticks.remote.RemoteTree" />
+	<import class="com.framsticks.model.ModelPackage" />
+	<import class="com.framsticks.model.ModelBuilder" />
+	<import class="com.framsticks.model.f0.SchemaBuilder" />
+	<import class="com.framsticks.core.LocalTree" />
+	<import class="com.framsticks.hosting.Server" />
+	<import class="com.framsticks.test.TestClass" />
+	<import class="com.framsticks.gui.table.ColumnsConfig" />
+	<Browser>
+		<RemoteTree name="localhost:9009" address="localhost:9009">
+			<ModelPackage />
+		</RemoteTree>
+		<!-- <LocalTree name="model"> -->
+		<!-- 	<ModelBuilder resource="/examples/f0_example.txt" /> -->
+		<!-- </LocalTree> -->
+		<!-- <LocalTree name="f0schema"> -->
+		<!-- 	<SchemaBuilder /> -->
+		<!-- </LocalTree> -->
+		<RemoteTree name="remote" address="localhost:9007" />
+		<include resource="/common-columns.xml" />
+		<!-- <ColumnsConfig className="Genotype" columnsNames="uid name" /> -->
+	</Browser>
+	<Server port="9007">
+		<LocalTree name="test">
+			<TestClass />
+		</LocalTree>
+	</Server>
+</Framsticks>
Index: java/main/src/main/resources/log4j2.xml
===================================================================
--- java/main/src/main/resources/log4j2.xml	(revision 100)
+++ java/main/src/main/resources/log4j2.xml	(revision 101)
@@ -11,7 +11,8 @@
 	<loggers>
 		<!-- <logger name="com.framsticks.hosting.Cli" level="debug" /> -->
-		<!-- <logger name="com.framsticks.gui.tree.TreeModel" level="debug" /> -->
+		<!-- <logger name="com.framsticks.gui.Frame" level="debug" /> -->
 		<!-- <logger name="com.framsticks.params.SimpleAbstractAccess" level="debug" /> -->
-		<!-- <logger name="com.framsticks.gui.Frame" level="debug" /> -->
+		<!-- <logger name="com.framsticks.running.ExternalProcess" level="trace" /> -->
+		<!-- <logger name="com.framsticks.util.dispatching.AbstractJoinable" level="debug" /> -->
 		<root level="info">
 			<appender-ref ref="Console"/>
Index: java/main/src/main/resources/managed-console.xml
===================================================================
--- java/main/src/main/resources/managed-console.xml	(revision 101)
+++ java/main/src/main/resources/managed-console.xml	(revision 101)
@@ -0,0 +1,9 @@
+<?xml version="1.0" encoding="UTF-8"?>
+<Framsticks>
+	<import class="com.framsticks.gui.console.ManagedConsole" />
+	<import class="com.framsticks.remote.RemoteTree" />
+	<ManagedConsole>
+		<RemoteTree name="remote" address="localhost:9009" />
+	</ManagedConsole>
+</Framsticks>
+
Index: java/main/src/main/resources/prime-gui.xml
===================================================================
--- java/main/src/main/resources/prime-gui.xml	(revision 101)
+++ java/main/src/main/resources/prime-gui.xml	(revision 101)
@@ -0,0 +1,11 @@
+<?xml version="1.0" encoding="UTF-8"?>
+<Framsticks>
+	<include resource="/prime.xml" />
+
+	<import class="com.framsticks.gui.Browser" />
+	<import class="com.framsticks.remote.RemoteTree" />
+	<Browser>
+		<RemoteTree name="prime-experiment" address="localhost:9007" />
+		<RemoteTree name="worker" address="localhost:9100" />
+	</Browser>
+</Framsticks>
Index: java/main/src/main/resources/prime.xml
===================================================================
--- java/main/src/main/resources/prime.xml	(revision 101)
+++ java/main/src/main/resources/prime.xml	(revision 101)
@@ -0,0 +1,28 @@
+<?xml version="1.0" encoding="UTF-8"?>
+<Framsticks>
+	<import class="com.framsticks.hosting.Server" />
+	<import class="com.framsticks.test.PrimeExperiment" />
+	<import class="com.framsticks.experiment.Simulator" />
+	<import class="com.framsticks.core.LocalTree" />
+	<import class="com.framsticks.running.LoggingOutputListener" />
+	<import class="com.framsticks.running.FramsServer" />
+	<Server port="9007">
+		<LocalTree name="test">
+			<PrimeExperiment name="prime">
+				<Simulator address="localhost:9100"/>
+				<Simulator address="localhost:9101"/>
+				<Simulator address="localhost:9102"/>
+			</PrimeExperiment>
+		</LocalTree>
+	</Server>
+	<FramsServer name="frams" port="9100">
+		<LoggingOutputListener />
+	</FramsServer>
+	<FramsServer name="frams" port="9101">
+		<LoggingOutputListener />
+	</FramsServer>
+	<FramsServer name="frams" port="9102">
+		<LoggingOutputListener />
+	</FramsServer>
+</Framsticks>
+
Index: java/main/src/main/resources/raw-console.xml
===================================================================
--- java/main/src/main/resources/raw-console.xml	(revision 101)
+++ java/main/src/main/resources/raw-console.xml	(revision 101)
@@ -0,0 +1,8 @@
+<?xml version="1.0" encoding="UTF-8"?>
+<Framsticks>
+	<import class="com.framsticks.gui.console.DirectConsole" />
+	<import class="com.framsticks.communication.ClientSideRawConnection" />
+	<DirectConsole>
+		<ClientSideRawConnection address="localhost:9009" />
+	</DirectConsole>
+</Framsticks>
Index: java/main/src/test/java/com/framsticks/communication/RequestTest.java
===================================================================
--- java/main/src/test/java/com/framsticks/communication/RequestTest.java	(revision 100)
+++ java/main/src/test/java/com/framsticks/communication/RequestTest.java	(revision 101)
@@ -28,4 +28,5 @@
 
 		assertThat(Request.takeRequestId(true, " 2 simple second")).isEqualTo(Pair.make(2, "simple second"));
+		assertThat(Request.takeRequestId(true, " -3 simple")).isEqualTo(Pair.make(-3, "simple"));
 	}
 
Index: java/main/src/test/java/com/framsticks/core/ListChangeTest.java
===================================================================
--- java/main/src/test/java/com/framsticks/core/ListChangeTest.java	(revision 100)
+++ java/main/src/test/java/com/framsticks/core/ListChangeTest.java	(revision 101)
@@ -7,4 +7,5 @@
 
 import com.framsticks.core.ListChange.Action;
+import com.framsticks.params.AccessOperations;
 import com.framsticks.params.FramsClass;
 import com.framsticks.params.ListSink;
@@ -60,5 +61,5 @@
 	@Test(dependsOnMethods = "createReflectionAccess")
 	public void load() throws Exception {
-		access.select(listChange).load(ListSource.createFrom("type:2", "pos:0", "id:test"));
+		AccessOperations.load(access.select(listChange), ListSource.createFrom("type:2", "pos:0", "id:test"));
 
 		assertThat(listChange.action).isEqualTo(Action.Modify);
Index: java/main/src/test/java/com/framsticks/core/XmlBasedTest.java
===================================================================
--- java/main/src/test/java/com/framsticks/core/XmlBasedTest.java	(revision 100)
+++ java/main/src/test/java/com/framsticks/core/XmlBasedTest.java	(revision 101)
@@ -26,5 +26,7 @@
 	}
 
-	protected abstract String getConfigurationName();
+	protected String getConfigurationName() {
+		return this.getClass().getSimpleName() + ".xml";
+	}
 
 
Index: java/main/src/test/java/com/framsticks/gui/ProcedureBrowserTest.java
===================================================================
--- java/main/src/test/java/com/framsticks/gui/ProcedureBrowserTest.java	(revision 100)
+++ java/main/src/test/java/com/framsticks/gui/ProcedureBrowserTest.java	(revision 101)
@@ -17,5 +17,5 @@
 import com.framsticks.params.types.StringParam;
 import com.framsticks.parsers.XmlLoader;
-import com.framsticks.test.ChangeEvent;
+import com.framsticks.test.TestChangeEvent;
 import com.framsticks.test.TestClass;
 import com.framsticks.util.dispatching.FutureHandler;
@@ -82,8 +82,8 @@
 		waitForIdle();
 
-		final EventListener<ChangeEvent> listener = new EventListener<ChangeEvent>() {
+		final EventListener<TestChangeEvent> listener = new EventListener<TestChangeEvent>() {
 
 			@Override
-			public void action(ChangeEvent argument) {
+			public void action(TestChangeEvent argument) {
 				assertThat(argument.history).isEqualTo("");
 			}
@@ -96,5 +96,5 @@
 				assertThat(access.get("history", String.class)).isEqualTo("initial|Żółw|");
 
-				tree.addListener(Path.to(tree, "/"), access.getFramsClass().getParamEntry("history_changed", EventParam.class), listener, ChangeEvent.class, FutureHandler.doNothing(Void.class, failOnException));
+				tree.addListener(Path.to(tree, "/"), access.getFramsClass().getParamEntry("history_changed", EventParam.class), listener, TestChangeEvent.class, FutureHandler.doNothing(Void.class, failOnException));
 			}
 		});
Index: java/main/src/test/java/com/framsticks/gui/console/TrackConsoleTest.java
===================================================================
--- java/main/src/test/java/com/framsticks/gui/console/TrackConsoleTest.java	(revision 100)
+++ java/main/src/test/java/com/framsticks/gui/console/TrackConsoleTest.java	(revision 101)
@@ -3,7 +3,4 @@
 import org.testng.annotations.Test;
 
-import com.framsticks.communication.Address;
-import com.framsticks.communication.ClientSideManagedConnection;
-import com.framsticks.communication.Connection;
 import com.framsticks.core.Path;
 import com.framsticks.gui.GuiTest;
@@ -28,5 +25,5 @@
 		tree = new RemoteTree();
 
-		tree.setConnection(Connection.to(new ClientSideManagedConnection(), new Address("localhost:9009")));
+		tree.setAddress("localhost:9009");
 
 		console = new TrackConsole().setConnection(tree.getConnection());
Index: java/main/src/test/java/com/framsticks/hosting/ServerTest.java
===================================================================
--- java/main/src/test/java/com/framsticks/hosting/ServerTest.java	(revision 100)
+++ java/main/src/test/java/com/framsticks/hosting/ServerTest.java	(revision 101)
@@ -15,5 +15,5 @@
 import com.framsticks.remote.RemoteTree;
 
-import com.framsticks.test.ChangeEvent;
+import com.framsticks.test.TestChangeEvent;
 import com.framsticks.test.TestClass;
 import com.framsticks.core.Tree;
@@ -45,5 +45,5 @@
 	protected LocalTree hosted;
 	protected TestClass hostedObject;
-	protected EventListener<ChangeEvent> listener;
+	protected EventListener<TestChangeEvent> listener;
 	protected EventListener<ListChange> childListener;
 
@@ -52,8 +52,4 @@
 
 
-	@Override
-	protected String getConfigurationName() {
-		return "ServerTest.xml";
-	}
 
 	@Test
@@ -127,8 +123,8 @@
 	public void registerListener() {
 		final Waiter waiter = produceWaiter(1.0);
-		listener = new EventListener<ChangeEvent>() {
-
-			@Override
-			public void action(ChangeEvent argument) {
+		listener = new EventListener<TestChangeEvent>() {
+
+			@Override
+			public void action(TestChangeEvent argument) {
 				listenerArguments.add(argument.history);
 			}
@@ -142,5 +138,5 @@
 		});
 
-		addListener(remotePath, remoteTestFramsClass.getParamEntry("history_changed", EventParam.class), listener, ChangeEvent.class, produceWaiter(1.0).passInFuture(Void.class));
+		addListener(remotePath, remoteTestFramsClass.getParamEntry("history_changed", EventParam.class), listener, TestChangeEvent.class, produceWaiter(1.0).passInFuture(Void.class));
 	}
 
Index: java/main/src/test/java/com/framsticks/model/ModelPackageTest.java
===================================================================
--- java/main/src/test/java/com/framsticks/model/ModelPackageTest.java	(revision 100)
+++ java/main/src/test/java/com/framsticks/model/ModelPackageTest.java	(revision 101)
@@ -8,5 +8,5 @@
 import com.framsticks.params.FramsClass;
 import com.framsticks.params.ReflectionAccess;
-import com.framsticks.params.SourceInterface;
+import com.framsticks.params.Source;
 import com.framsticks.params.ValueParam;
 import com.framsticks.parsers.FileSource;
@@ -25,5 +25,5 @@
 		assertThat(stream).describedAs("stream " + filename).isNotNull();
 
-		SourceInterface source = new FileSource(stream, filename);
+		Source source = new FileSource(stream, filename);
 
 		assertThat(source.isClosed()).isFalse();
Index: java/main/src/test/java/com/framsticks/params/FramsClassBuilderTest.java
===================================================================
--- java/main/src/test/java/com/framsticks/params/FramsClassBuilderTest.java	(revision 100)
+++ java/main/src/test/java/com/framsticks/params/FramsClassBuilderTest.java	(revision 101)
@@ -10,5 +10,5 @@
 import com.framsticks.params.types.StringParam;
 import com.framsticks.parsers.Savers;
-import com.framsticks.test.ChangeEvent;
+import com.framsticks.test.TestChangeEvent;
 import com.framsticks.test.TestClass;
 import com.framsticks.test.TestConfiguration;
@@ -69,5 +69,5 @@
 					"id:history_changed",
 					"name:HistoryListener",
-					"type:e ChangeEvent",
+					"type:e TestChangeEvent",
 					"",
 					"prop:",
@@ -128,8 +128,8 @@
 		final Holder<String> called = new Holder<>();
 
-		final EventListener<ChangeEvent> listener = new EventListener<ChangeEvent>() {
+		final EventListener<TestChangeEvent> listener = new EventListener<TestChangeEvent>() {
 
 			@Override
-			public void action(ChangeEvent argument) {
+			public void action(TestChangeEvent argument) {
 				called.set(argument.history);
 			}
Index: java/main/src/test/java/com/framsticks/params/ParamBuilderTest.java
===================================================================
--- java/main/src/test/java/com/framsticks/params/ParamBuilderTest.java	(revision 100)
+++ java/main/src/test/java/com/framsticks/params/ParamBuilderTest.java	(revision 101)
@@ -30,5 +30,5 @@
 	public void loadParam() throws Exception {
 		access = new ReflectionAccess(ParamBuilder.class, builderFramsClass);
-		SourceInterface source = ListSource.createFrom("prop:", "id:v", "name:variable", "type:f", "help:just a testing variable", "");
+		Source source = ListSource.createFrom("prop:", "id:v", "name:variable", "type:f", "help:just a testing variable", "");
 
 		MultiParamLoader loader = new MultiParamLoader();
Index: java/main/src/test/java/com/framsticks/parsers/F0ParserTest.java
===================================================================
--- java/main/src/test/java/com/framsticks/parsers/F0ParserTest.java	(revision 100)
+++ java/main/src/test/java/com/framsticks/parsers/F0ParserTest.java	(revision 101)
@@ -73,5 +73,5 @@
 	@Test(dependsOnMethods = {"readF0"})
 	public void stripAccess() {
-		components = Util.stripAccess(accesses, ModelComponent.class);
+		components = ParamsUtil.stripAccess(accesses, ModelComponent.class);
 
 		assertThat(components.get(1)).isInstanceOf(Part.class);
Index: java/main/src/test/java/com/framsticks/running/ExternalProcessTest.java
===================================================================
--- java/main/src/test/java/com/framsticks/running/ExternalProcessTest.java	(revision 100)
+++ java/main/src/test/java/com/framsticks/running/ExternalProcessTest.java	(revision 101)
@@ -9,4 +9,6 @@
 import org.testng.annotations.Test;
 
+import com.framsticks.core.ValueChange;
+import com.framsticks.params.EventListener;
 import com.framsticks.test.TestConfiguration;
 import com.framsticks.util.dispatching.Monitor;
@@ -25,8 +27,8 @@
 		final List<String> output = new LinkedList<>();
 
-		process.addListener(new OutputListener() {
+		process.addOutputListener(new EventListener<ValueChange>() {
 			@Override
-			public void onLineRead(String line) {
-				output.add(line);
+			public void action(ValueChange change) {
+				output.add(change.value.toString());
 			}
 		});
Index: java/main/src/test/java/com/framsticks/test/PrimeExperimentTest.java
===================================================================
--- java/main/src/test/java/com/framsticks/test/PrimeExperimentTest.java	(revision 101)
+++ java/main/src/test/java/com/framsticks/test/PrimeExperimentTest.java	(revision 101)
@@ -0,0 +1,20 @@
+package com.framsticks.test;
+
+import static org.fest.assertions.Assertions.*;
+
+import org.testng.annotations.Test;
+
+import com.framsticks.core.XmlBasedTest;
+
+@Test
+public class PrimeExperimentTest extends XmlBasedTest {
+
+	protected PrimeExperiment experiment;
+
+	@Test
+	public void start() {
+		assertThat(framsticks).isNotNull();
+		assertThat(framsticks.get("prime")).isInstanceOf(PrimeExperiment.class);
+		experiment = (PrimeExperiment) framsticks.get("prime");
+	}
+}
Index: java/main/src/test/resources/configs/PrimeExperimentTest.xml
===================================================================
--- java/main/src/test/resources/configs/PrimeExperimentTest.xml	(revision 101)
+++ java/main/src/test/resources/configs/PrimeExperimentTest.xml	(revision 101)
@@ -0,0 +1,7 @@
+<?xml version="1.0" encoding="UTF-8"?>
+<Framsticks>
+	<import class="com.framsticks.test.PrimeExperiment" />
+	<import class="com.framsticks.experiment.Simulator" />
+	<PrimeExperiment name="prime">
+	</PrimeExperiment>
+</Framsticks>
Index: java/main/src/test/resources/configs/ServerTest.xml
===================================================================
--- java/main/src/test/resources/configs/ServerTest.xml	(revision 100)
+++ java/main/src/test/resources/configs/ServerTest.xml	(revision 101)
@@ -6,5 +6,5 @@
 	<import class="com.framsticks.test.TestClass" />
 	<import class="com.framsticks.running.LoggingOutputListener" />
-	<Server name="server" port="9007">
+	<Server port="9007">
 		<LocalTree name="test">
 			<TestClass name="a test name" />
