Index: /java/main/pom.xml
===================================================================
--- /java/main/pom.xml	(revision 86)
+++ /java/main/pom.xml	(revision 87)
@@ -18,11 +18,15 @@
 		<project.build.sourceEncoding>UTF-8</project.build.sourceEncoding>
 	</properties>
+
+	<dependencyManagement>
+		<dependencies>
+			<dependency>
+				<groupId>com.ibm.icu</groupId>
+				<artifactId>icu4j</artifactId>
+				<version>3.4.4</version>
+			</dependency>
+		</dependencies>
+	</dependencyManagement>
 	<dependencies>
-		<dependency>
-			<groupId>org.javatuples</groupId>
-			<artifactId>javatuples</artifactId>
-			<version>1.2</version>
-			<scope>compile</scope>
-		</dependency>
 		<dependency>
 			<groupId>com.google.code.findbugs</groupId>
@@ -80,4 +84,18 @@
 		</dependency>
 
+		<!-- the logic of @Immutable checking built in FindBugs is not very comprehensive -->
+		<dependency>
+			<groupId>org.mutabilitydetector</groupId>
+			<artifactId>MutabilityDetector4FindBugs</artifactId>
+			<version>0.2.3</version>
+			<scope>test</scope>
+		</dependency>
+
+		<dependency>
+			<groupId>org.mutabilitydetector</groupId>
+			<artifactId>MutabilityDetector</artifactId>
+			<version>0.9</version>
+			<scope>test</scope>
+		</dependency>
 		<!-- <dependency> -->
 		<!-- 	<groupId>junit</groupId> -->
Index: /java/main/src/main/java/com/framsticks/core/Instance.java
===================================================================
--- /java/main/src/main/java/com/framsticks/core/Instance.java	(revision 86)
+++ /java/main/src/main/java/com/framsticks/core/Instance.java	(revision 87)
@@ -33,5 +33,5 @@
 	protected void run() {
 		super.run();
-		root = new Node((CompositeParam) Param.build().name("Instance").id(name).type("o").finish(), null);
+		root = new Node(Param.build().name("Instance").id(name).type("o"), null);
 		com.framsticks.model.Package.register(registry);
 	}
@@ -267,5 +267,5 @@
 		if ("/".equals(file.getPath())) {
 			if (root.getParam().getContainedTypeName() == null) {
-				root = new Node((CompositeParam)Param.build().name("Instance").id(name).type("o " + framsClass.getId()).finish(), root.getObject());
+				root = new Node(Param.build().name("Instance").id(name).type("o " + framsClass.getId()), root.getObject());
 			}
 		}
@@ -306,4 +306,5 @@
 
 					String id = listAccess.computeIdentifierFor(accessInterface.getSelected());
+					//TODO listAccessParam
 					Param param = Param.build().type("o " + accessInterface.getId()).id(id).finish();
 					Object child = accessInterface.getSelected();
@@ -339,5 +340,5 @@
 
 	public Path getPath(String textual) {
-		return new Path(this, textual);
+		return Path.build().resolve(this, textual).finish();
 	}
 
Index: /java/main/src/main/java/com/framsticks/core/ListChange.java
===================================================================
--- /java/main/src/main/java/com/framsticks/core/ListChange.java	(revision 86)
+++ /java/main/src/main/java/com/framsticks/core/ListChange.java	(revision 87)
@@ -1,16 +1,12 @@
 package com.framsticks.core;
 
-import com.framsticks.params.FramsClass;
-import com.framsticks.params.Param;
-import com.framsticks.params.types.DecimalParam;
-import com.framsticks.params.types.EnumParam;
-import com.framsticks.params.types.StringParam;
+import com.framsticks.params.annotations.FramsClassAnnotation;
+import com.framsticks.params.annotations.ParamAnnotation;
 import com.framsticks.util.lang.Strings;
-
-import java.util.Arrays;
 
 /**
  * @author Piotr Sniegowski
  */
+@FramsClassAnnotation
 public class ListChange {
 
@@ -31,18 +27,29 @@
 		Remove,
 		Modify
+		// Add(0),
+		// Remove(1),
+		// Modify(2);
+
+		// public final int value;
+
+		// /**
+		//  * @param value
+		//  */
+		// Action(int value) {
+		// 	this.value = value;
+		// }
+
 	}
 
-	private Action action;
-	private Integer position;
-	private String identifier;
+	public Action action = Action.Add;
+	@ParamAnnotation(id = "pos")
+	public Integer position;
+	@ParamAnnotation(id = "id")
+	public String identifier;
 
+	@ParamAnnotation
 	public Integer getType() { return action.ordinal(); }
+	@ParamAnnotation
 	public void setType(Integer type) { action = Action.values()[type]; }
-
-	public Integer getPos() { return position; }
-	public void setPos(Integer pos) { position = pos; }
-
-	public String getId() { return identifier; }
-	public void setId(String id) { identifier = id; }
 
 	public String getBestIdentifier() {
@@ -53,11 +60,4 @@
 	}
 
-	public static FramsClass getFramsClass() {
-		return new FramsClass("ListChange", "ListChange", null)
-				.append(Param.build().id("type").name("type").type(new EnumParam(Arrays.asList("Add", "Remove", "Modify"))))
-				.append(Param.build().id("id").name("identifier").type(StringParam.class))
-				.append(Param.build().id("pos").name("position").type(DecimalParam.class));
-	}
-
 	@Override
 	public String toString() {
Index: /java/main/src/main/java/com/framsticks/core/Node.java
===================================================================
--- /java/main/src/main/java/com/framsticks/core/Node.java	(revision 86)
+++ /java/main/src/main/java/com/framsticks/core/Node.java	(revision 87)
@@ -1,33 +1,42 @@
 package com.framsticks.core;
 
+import javax.annotation.Nonnull;
+import javax.annotation.concurrent.Immutable;
+
 import com.framsticks.params.CompositeParam;
+import com.framsticks.params.ParamBuilder;
 
 /**
  * @author Piotr Sniegowski
  */
+@Immutable
 public class Node {
 
-    protected final CompositeParam param;
-    protected final Object object;
-    //protected final Map<String, Node> children = new HashMap<String, Node>();
+	protected final CompositeParam param;
+	protected final Object object;
+	//protected final Map<String, Node> children = new HashMap<String, Node>();
 
-    public Node() {
-        param = null;
-        object = null;
-    }
+	public Node() {
+		param = null;
+		object = null;
+	}
 
-    public Node(CompositeParam param, Object object) {
-        assert param != null;
-        this.param = param;
-        this.object = object;
-    }
+	public Node(@Nonnull CompositeParam param, Object object) {
+		assert param != null;
+		this.param = param;
+		this.object = object;
+	}
 
-    public final CompositeParam getParam() {
-        return param;
-    }
+	public Node(@Nonnull ParamBuilder builder, Object object) {
+		this((CompositeParam) builder.finish(), object);
+	}
 
-    public final Object getObject() {
-        return object;
-    }
+	public final CompositeParam getParam() {
+		return param;
+	}
+
+	public final Object getObject() {
+		return object;
+	}
 
 }
Index: /java/main/src/main/java/com/framsticks/core/Path.java
===================================================================
--- /java/main/src/main/java/com/framsticks/core/Path.java	(revision 86)
+++ /java/main/src/main/java/com/framsticks/core/Path.java	(revision 87)
@@ -11,5 +11,4 @@
 
 import javax.annotation.concurrent.Immutable;
-import javax.annotation.Nonnull;
 
 import org.apache.commons.collections.ListUtils;
@@ -19,12 +18,12 @@
  */
 @Immutable
-public class Path {
+public final class Path {
 	// private final static Logger log = Logger.getLogger(Path.class.getName());
 
-	final LinkedList<Node> nodes = new LinkedList<Node>();
-	String textual;
-	Instance instance;
-
-	protected Object getKnownChild(AccessInterface access, CompositeParam param) {
+	final Instance instance;
+	final String textual;
+	final LinkedList<Node> nodes;
+
+	protected static Object getKnownChild(Instance instance, AccessInterface access, CompositeParam param) {
 		Object child = access.get(param, Object.class);
 		if (child == null) {
@@ -34,67 +33,18 @@
 	}
 
-	// public Path constructPath(Instance instance, String textual) {
-	//	assert instance.isActive();
-
-	// }
-
-	Path(Instance instance, String textual) {
-		assert instance.isActive();
+	/**
+	 * @param instance
+	 * @param textual
+	 * @param nodes
+	 */
+	Path(Instance instance, String textual, LinkedList<Node> nodes) {
 		this.instance = instance;
-
-		nodes.add(instance.root);
-		Node current = instance.root;
-
-		StringBuilder b = new StringBuilder();
-		Iterator<String> i = Instance.splitPath(textual);
-		while (i.hasNext() && current.getObject() != null) {
-			AccessInterface access = instance.registry.prepareAccess(current.getParam());
-			if (access == null) {
-				break;
-			}
-			String e = i.next();
-			Param p = access.getParam(e);
-			if (!(p instanceof CompositeParam)) {
-				//entries.add(new Entry());
-				break;
-			}
-			CompositeParam c = (CompositeParam)p;
-			b.append("/").append(e);
-			access.select(current.getObject());
-			current = new Node(c, getKnownChild(access, c));
-			nodes.add(current);
-		}
-		this.textual = (size() == 1) ? "/" : b.toString();
-	}
-
-	public Path(@Nonnull Instance instance, List<Node> nodes, Node node) {
-		this.instance = instance;
-		StringBuilder b = new StringBuilder();
-		boolean add = false;
-		for (Node n : nodes) {
-			this.nodes.add(n);
-			if (add) {
-				b.append("/").append(n.getParam().getId());
-			}
-			add = true;
-			if (n == node) {
-				break;
-			}
-		}
-		this.textual = (size() == 1) ? "/" : b.toString();
-	}
-
-	protected Path() {
-
+		this.textual = textual;
+		this.nodes = nodes;
 	}
 
 	public Path appendNode(Node node) {
 		assert isResolved();
-		Path result = new Path();
-		result.textual = textual + ((size() == 1) ? "" : "/") + node.getParam().getId();
-		result.instance = instance;
-		result.nodes.addAll(nodes);
-		result.nodes.add(node);
-		return result;
+		return new PathBuilder().instance(instance).textual(textual + ((size() == 1) ? "" : "/") + node.getParam().getId()).add(nodes).add(node).finish();
 	}
 
@@ -104,11 +54,105 @@
 	}
 
+	public static class PathBuilder {
+
+		Instance instance;
+		String textual;
+		final LinkedList<Node> nodes = new LinkedList<Node>();
+
+		public Path finish() {
+			assert instance != null;
+			assert textual != null;
+			return new Path(instance, textual, nodes);
+		}
+
+		public PathBuilder()
+		{
+		}
+
+		public PathBuilder instance(Instance instance) {
+			this.instance = instance;
+			return this;
+		}
+
+		public PathBuilder textual(String textual) {
+			this.textual = textual;
+			return this;
+		}
+
+		public PathBuilder add(List<Node> nodes) {
+			this.nodes.addAll(nodes);
+			return this;
+		}
+
+		public PathBuilder add(Node node) {
+			this.nodes.add(node);
+			return this;
+		}
+
+		public PathBuilder setLast(Object object) {
+			Node n = nodes.pollLast();
+			nodes.add(new Node(n.getParam(), object));
+			return this;
+		}
+
+		public PathBuilder buildUpTo(List<Node> nodes, Node node) {
+			StringBuilder b = new StringBuilder();
+			boolean add = false;
+			for (Node n : nodes) {
+				this.nodes.add(n);
+				if (add) {
+					b.append("/").append(n.getParam().getId());
+				}
+				add = true;
+				if (n == node) {
+					break;
+				}
+			}
+			this.textual = (nodes.size() == 1) ? "/" : b.toString();
+			return this;
+		}
+
+		public PathBuilder resolve(Instance instance, String textual) {
+
+			assert nodes.isEmpty();
+			assert instance.isActive();
+			this.instance = instance;
+
+			nodes.add(instance.root);
+			Node current = instance.root;
+
+			StringBuilder b = new StringBuilder();
+			Iterator<String> i = Instance.splitPath(textual);
+			while (i.hasNext() && current.getObject() != null) {
+				AccessInterface access = instance.registry.prepareAccess(current.getParam());
+				if (access == null) {
+					break;
+				}
+				String e = i.next();
+				Param p = access.getParam(e);
+				if (!(p instanceof CompositeParam)) {
+					//entries.add(new Entry());
+					break;
+				}
+				CompositeParam c = (CompositeParam)p;
+				b.append("/").append(e);
+				access.select(current.getObject());
+				current = new Node(c, getKnownChild(instance, access, c));
+				nodes.add(current);
+			}
+			this.textual = (nodes.size() == 1) ? "/" : b.toString();
+
+			return this;
+		}
+	}
+
+	public static PathBuilder build() {
+		return new PathBuilder();
+	}
+
 	public Path appendResolution(Object object) {
 		assert !isResolved();
-		Path result = new Path();
-		result.textual = textual;
-		result.instance = instance;
-		result.nodes.addAll(nodes);
-		result.nodes.add(new Node(result.nodes.pollLast().getParam(), object));
+		Path result = new PathBuilder().textual(textual).instance(instance).add(nodes).setLast(object).finish();
+		assert size() == result.size();
 		return result;
 	}
@@ -169,7 +213,7 @@
 		assert !isResolved();
 		if (size() == 1) {
-			return new Path(instance, "/");//appendResolution(instance.root.object);
-		}
-		Object child = getKnownChild(instance.bindAccess(getUnder()), getTop().getParam());
+			return Path.build().resolve(instance, "/").finish();//appendResolution(instance.root.object);
+		}
+		Object child = getKnownChild(instance, instance.bindAccess(getUnder()), getTop().getParam());
 		if (child == null) {
 			return this;
@@ -201,7 +245,7 @@
 	}
 
-	public void setInstance(Instance instance) {
-		this.instance = instance;
-	}
+	// public void setInstance(Instance instance) {
+	//	this.instance = instance;
+	// }
 
 	@SuppressWarnings("unchecked")
Index: /java/main/src/main/java/com/framsticks/examples/GenotypeBrowser.java
===================================================================
--- /java/main/src/main/java/com/framsticks/examples/GenotypeBrowser.java	(revision 86)
+++ /java/main/src/main/java/com/framsticks/examples/GenotypeBrowser.java	(revision 87)
@@ -42,9 +42,11 @@
 		Package.register(this.getRegistry());
 
-		registry.putInfoIntoCache(new FramsClass("ModelBuilderRoot", "ModelBuilderRoot", null)
-				.append(Param.build().type("o Model").id("model").name("model"))
-				.append(Param.build().type("s 1").id("genotype").name("genotype"))
+		registry.putInfoIntoCache(FramsClass.build()
+				.idAndName("ModelBuilderRoot")
+				.param(Param.build().id("model").type("o Model").name("model"))
+				.param(Param.build().id("genotype").type("s 1").name("genotype"))
+				.finish()
 		);
-		root = new Node((CompositeParam)Param.build().type("o ModelBuilderRoot").id(name).name("Instance").finish(), PropertiesAccess.createPropertiesMap());
+		root = new Node(Param.build().type("o ModelBuilderRoot").id(name).name("Instance"), PropertiesAccess.createPropertiesMap());
 	}
 
Index: /java/main/src/main/java/com/framsticks/gui/EndpointAtFrame.java
===================================================================
--- /java/main/src/main/java/com/framsticks/gui/EndpointAtFrame.java	(revision 86)
+++ /java/main/src/main/java/com/framsticks/gui/EndpointAtFrame.java	(revision 87)
@@ -121,8 +121,6 @@
 					return result;
 				}
-				Path p = new Path(path.getInstance(), nodes, n);
+				Path p = Path.build().instance(path.getInstance()).buildUpTo(nodes, n).finish();
 
-				//TODO breaks immutable
-				path.setInstance(path.getInstance());
 
 				log.debug("forced resolution: creating treenode for " + p);
Index: /java/main/src/main/java/com/framsticks/gui/Gui.java
===================================================================
--- /java/main/src/main/java/com/framsticks/gui/Gui.java	(revision 86)
+++ /java/main/src/main/java/com/framsticks/gui/Gui.java	(revision 87)
@@ -42,5 +42,5 @@
 	}
 
-	public static Control createComponentForText(PrimitiveParam valueParam) {
+	public static Control createComponentForText(PrimitiveParam<?> valueParam) {
 		if (valueParam.getMin(Object.class) != null) {
 			return new TextAreaControl(valueParam);
Index: /java/main/src/main/java/com/framsticks/gui/controls/TextAreaControl.java
===================================================================
--- /java/main/src/main/java/com/framsticks/gui/controls/TextAreaControl.java	(revision 86)
+++ /java/main/src/main/java/com/framsticks/gui/controls/TextAreaControl.java	(revision 87)
@@ -14,5 +14,5 @@
 	protected JScrollPane textScrollPane;
 
-	public TextAreaControl(PrimitiveParam valueParam) {
+	public TextAreaControl(PrimitiveParam<?> valueParam) {
 		super(valueParam);
 		textArea = new JTextArea();
Index: /java/main/src/main/java/com/framsticks/gui/controls/TextControl.java
===================================================================
--- /java/main/src/main/java/com/framsticks/gui/controls/TextControl.java	(revision 86)
+++ /java/main/src/main/java/com/framsticks/gui/controls/TextControl.java	(revision 87)
@@ -10,5 +10,5 @@
 public abstract class TextControl extends ValueControl {
 
-	public TextControl(PrimitiveParam valueParam) {
+	public TextControl(PrimitiveParam<?> valueParam) {
 		super(valueParam);
 	}
Index: /java/main/src/main/java/com/framsticks/gui/controls/TextFieldControl.java
===================================================================
--- /java/main/src/main/java/com/framsticks/gui/controls/TextFieldControl.java	(revision 86)
+++ /java/main/src/main/java/com/framsticks/gui/controls/TextFieldControl.java	(revision 87)
@@ -23,5 +23,5 @@
 	}
 
-	public TextFieldControl(PrimitiveParam valueParam) {
+	public TextFieldControl(PrimitiveParam<?> valueParam) {
 		super(valueParam);
 		textField = new JTextField();
Index: /java/main/src/main/java/com/framsticks/gui/controls/ValueControl.java
===================================================================
--- /java/main/src/main/java/com/framsticks/gui/controls/ValueControl.java	(revision 86)
+++ /java/main/src/main/java/com/framsticks/gui/controls/ValueControl.java	(revision 87)
@@ -22,5 +22,5 @@
 	protected ValueControlListener listener;
 
-	public ValueControl(PrimitiveParam primitiveParam) {
+	public ValueControl(PrimitiveParam<?> primitiveParam) {
 		super(primitiveParam);
 
@@ -40,6 +40,6 @@
 
 	@Override
-	public PrimitiveParam getParam() {
-		return (PrimitiveParam) param;
+	public PrimitiveParam<?> getParam() {
+		return (PrimitiveParam<?>) param;
 	}
 
Index: /java/main/src/main/java/com/framsticks/hosting/ServerInstance.java
===================================================================
--- /java/main/src/main/java/com/framsticks/hosting/ServerInstance.java	(revision 86)
+++ /java/main/src/main/java/com/framsticks/hosting/ServerInstance.java	(revision 87)
@@ -2,8 +2,6 @@
 
 import com.framsticks.core.*;
-import com.framsticks.params.CompositeParam;
 import com.framsticks.params.ConstructionException;
 import com.framsticks.params.FramsClass;
-import com.framsticks.params.FramsClassBuilder;
 import com.framsticks.params.Param;
 import com.framsticks.core.LocalInstance;
@@ -44,5 +42,5 @@
 		hosted.setName("hosted");
 		hosted.configure(hostedConfig);
-		root = new Node((CompositeParam) Param.build().name("root").id("root").type("o" + hosted.getClass().getCanonicalName()).finish(), hosted);
+		root = new Node(Param.build().name("root").id("root").type("o" + hosted.getClass().getCanonicalName()), hosted);
 	}
 
@@ -59,5 +57,5 @@
 		try {
 			Class<?> nativeClass = Class.forName(id);
-			FramsClass framsClass = FramsClassBuilder.buildForClass(nativeClass);
+			FramsClass framsClass = FramsClass.build().forClass(nativeClass);
 
 			if (!framsClass.getId().equals(id)) {
Index: /java/main/src/main/java/com/framsticks/leftovers/FavouritesXMLFactory.java
===================================================================
--- /java/main/src/main/java/com/framsticks/leftovers/FavouritesXMLFactory.java	(revision 86)
+++ /java/main/src/main/java/com/framsticks/leftovers/FavouritesXMLFactory.java	(revision 87)
@@ -29,6 +29,5 @@
 
 public class FavouritesXMLFactory {
-	private static final Logger log = Logger
-			.getLogger(FavouritesXMLFactory.class);
+	private static final Logger log = Logger.getLogger(FavouritesXMLFactory.class);
 
 	private static final String PATH_MARK = "path";
Index: /java/main/src/main/java/com/framsticks/leftovers/f0/NeuroClass.java
===================================================================
--- /java/main/src/main/java/com/framsticks/leftovers/f0/NeuroClass.java	(revision 86)
+++ /java/main/src/main/java/com/framsticks/leftovers/f0/NeuroClass.java	(revision 87)
@@ -183,5 +183,5 @@
 		StringBuilder sb = new StringBuilder();
 		sb.append("NeuroClass [getId=").append(framsClass.getId())
-				.append(", getName=").append(framsClass.getName())
+				.append(", name=").append(framsClass.getName())
 				.append(", description=").append(framsClass.getDescription())
 				.append(", groups#=").append(framsClass.getGroupCount())
Index: /java/main/src/main/java/com/framsticks/observers/Observer.java
===================================================================
--- /java/main/src/main/java/com/framsticks/observers/Observer.java	(revision 86)
+++ /java/main/src/main/java/com/framsticks/observers/Observer.java	(revision 87)
@@ -7,4 +7,5 @@
 import com.framsticks.params.annotations.ParamAnnotation;
 import com.framsticks.util.lang.Casting;
+
 import org.apache.commons.configuration.Configuration;
 import org.apache.log4j.Logger;
@@ -50,4 +51,5 @@
 	}
 
+	// @XStreamImplicit(itemFieldName = "endpoint")
 	public Endpoint addEndpointForInstance(Instance instance) {
 		Endpoint endpoint = createEndpoint();
Index: /java/main/src/main/java/com/framsticks/params/AccessInterface.java
===================================================================
--- /java/main/src/main/java/com/framsticks/params/AccessInterface.java	(revision 86)
+++ /java/main/src/main/java/com/framsticks/params/AccessInterface.java	(revision 87)
@@ -74,4 +74,6 @@
 	FramsClass getFramsClass();
 
+	boolean tryAutoAppend(Object object);
+
 
 }
Index: /java/main/src/main/java/com/framsticks/params/ArrayListAccess.java
===================================================================
--- /java/main/src/main/java/com/framsticks/params/ArrayListAccess.java	(revision 86)
+++ /java/main/src/main/java/com/framsticks/params/ArrayListAccess.java	(revision 87)
@@ -31,4 +31,5 @@
 	@Override
 	public Param getParam(int i) {
+		//TODO listAccessParam
 		return Param.build().id(Integer.toString(i)).name(elementAccess.getId()).type("o " + elementAccess.getId()).finish();
 	}
Index: /java/main/src/main/java/com/framsticks/params/CompositeParam.java
===================================================================
--- /java/main/src/main/java/com/framsticks/params/CompositeParam.java	(revision 86)
+++ /java/main/src/main/java/com/framsticks/params/CompositeParam.java	(revision 87)
@@ -1,14 +1,17 @@
 package com.framsticks.params;
 
-import com.framsticks.util.lang.Strings;
+import javax.annotation.concurrent.Immutable;
 
 /**
  * @author Piotr Sniegowski
  */
+@Immutable
 public abstract class CompositeParam extends ValueParam {
+
 	protected final String containedTypeName;
 
-	public CompositeParam(String containedTypeName) {
-		this.containedTypeName = Strings.notEmpty(containedTypeName) ? containedTypeName : null;
+	public CompositeParam(ParamBuilder builder) {
+		super(builder);
+		this.containedTypeName = builder.getContainedTypeName();
 	}
 
Index: /java/main/src/main/java/com/framsticks/params/FramsClass.java
===================================================================
--- /java/main/src/main/java/com/framsticks/params/FramsClass.java	(revision 86)
+++ /java/main/src/main/java/com/framsticks/params/FramsClass.java	(revision 87)
@@ -8,4 +8,7 @@
 
 import javax.annotation.Nonnull;
+import javax.annotation.concurrent.Immutable;
+
+import org.apache.log4j.Logger;
 
 /**
@@ -21,12 +24,20 @@
  * @author Piotr Sniegowski
  */
+@Immutable
 @FramsClassAnnotation(id = "class", name = "class")
 public final class FramsClass {
 
-	/** The offset of the parameter (applied for newly added parameter). */
-	protected int fieldsNumber;
+	private final static Logger log = Logger.getLogger(FramsClass.class);
 
-	/** The groups. */
-	protected List<Group> groups = new ArrayList<Group>();
+	protected final String id;
+
+	protected final String name;
+
+	protected final String description;
+
+	protected final List<Group> groups;
+
+	/** The param list (for accessing parameters by offset in O(1) time. */
+	protected final List<Param> paramList;
 
 	/**
@@ -36,15 +47,6 @@
 	protected Map<String, Param> paramEntryMap = new LinkedHashMap<String, Param>();
 
-	/** The param list (for accessing parameters by offset in O(1) time. */
-	protected List<Param> paramList = new ArrayList<Param>();
-
-	/** The param getId map (for fast lookup of offset based on name */
-	protected Map<String, Integer> paramIdMap = new HashMap<String, Integer>();
-
-	protected String id;
-
-	protected String name;
-
-	protected String description;
+	// /** The param getId map (for fast lookup of offset based on name */
+	// protected Map<String, Integer> paramIdMap = new HashMap<String, Integer>();
 
 	public Collection<Param> getParamEntries() {
@@ -52,49 +54,26 @@
 	}
 
-	public FramsClass() {
+	public FramsClass(String id, String name, String description, List<Param> params, List<Group> groups) {
 
-	}
+		this.id = id;
+		this.name = name;
+		this.description = description;
+		this.groups = groups;
+		this.paramList = params;
 
-	public FramsClass(String id, String name, String description) {
-		this.setId(id);
-		this.setName(name);
-		this.setDescription(description);
-	}
+		for (Param param : params) {
+			paramEntryMap.put(param.getId(), param);
+			try {
+				Group group = groups.get(param.getGroup());
+				if (group != null) {
+					group.addProperty(param);
+				}
+			} catch (IndexOutOfBoundsException ignored) {
 
-	/**
-	 * Adds new param entry.
-	 *
-	 * @param param
-	 *            the new param entry
-	 */
-	public FramsClass append(Param param) {
-		// if (param.hasFlag(Flags.USERHIDDEN)) {
-		// 	return this;
-		// }
-		paramEntryMap.put(param.getId(), param);
-		//paramEntryMap.put(param.getInternalId(), param);
-		paramList.add(param);
-		try {
-			Group group = groups.get(param.getGroup());
-			if (group != null) {
-				group.addProperty(param);
 			}
-		} catch (IndexOutOfBoundsException ignored) {
-
 		}
 
-		return this;
-	}
+		log.trace("created framsclass " + this);
 
-	public FramsClass append(ParamBuilder builder) {
-		return append(builder.finish());
-	}
-
-	/**
-	 * Adds new group.
-	 */
-	public FramsClass appendGroup(Group group) {
-		groups.add(group);
-		return this;
 	}
 
@@ -206,20 +185,9 @@
 	@Override
 	public String toString() {
-		return id;
+		return id + "(" + name + ")";
 	}
 
-	@ParamAnnotation
-	public void setId(String id) {
-		this.id = id;
-	}
-
-	@ParamAnnotation
-	public void setName(String name) {
-		this.name = name;
-	}
-
-	@ParamAnnotation(id = "desc")
-	public void setDescription(String description) {
-		this.description = description;
+	public static FramsClassBuilder build() {
+		return new FramsClassBuilder();
 	}
 }
Index: /java/main/src/main/java/com/framsticks/params/FramsClassBuilder.java
===================================================================
--- /java/main/src/main/java/com/framsticks/params/FramsClassBuilder.java	(revision 86)
+++ /java/main/src/main/java/com/framsticks/params/FramsClassBuilder.java	(revision 87)
@@ -7,4 +7,8 @@
 import java.lang.reflect.ParameterizedType;
 import java.lang.reflect.Type;
+import java.util.ArrayList;
+import java.util.Collections;
+import java.util.HashMap;
+import java.util.LinkedList;
 import java.util.List;
 import java.util.Map;
@@ -12,4 +16,5 @@
 import org.apache.log4j.Logger;
 
+import com.framsticks.params.annotations.AutoAppendAnnotation;
 import com.framsticks.params.annotations.FramsClassAnnotation;
 import com.framsticks.params.annotations.ParamAnnotation;
@@ -18,8 +23,8 @@
 import com.framsticks.util.lang.Strings;
 
+@FramsClassAnnotation(id = "class", name = "class")
 public class FramsClassBuilder {
 	private static final Logger log =
 		Logger.getLogger(FramsClassBuilder.class);
-
 
 	public static String getName(FramsClassAnnotation fca, Class<?> javaClass) {
@@ -63,23 +68,40 @@
 		}
 
-		if (type.equals(Integer.class) || type.equals(int.class)) {
-			return "d";
-		}
-		if (type.equals(String.class)) {
-			return "s";
-		}
-		if (type.equals(Double.class) || type.equals(double.class)) {
-			return "f";
-		}
-		if (type.equals(Boolean.class) || type.equals(boolean.class)) {
-			return "d 0 1";
-		}
-		if (type.equals(Object.class)) {
-			return "x";
-		}
 		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)) {
+				return "d";
+			}
+			if (cl.equals(String.class)) {
+				return "s";
+			}
+			if (cl.equals(Double.class) || cl.equals(double.class)) {
+				return "f";
+			}
+			if (cl.equals(Boolean.class) || cl.equals(boolean.class)) {
+				return "d 0 1";
+			}
+			if (cl.equals(Object.class)) {
+				return "x";
+			}
+
 			return "o " + ((Class<?>) type).getCanonicalName();
 		}
-		return null;
+
+		throw new ConstructionException().msg("failed to find framsticks for native type").arg("type", type);
 	}
 
@@ -141,21 +163,124 @@
 	}
 
-	public static FramsClass buildForClass(Class<?> javaClass) throws ConstructionException {
+	public static Map<Class<?>, FramsClass> synchronizedCacheForBasedOnForJavaClass = Collections.synchronizedMap(new HashMap<Class<?>, FramsClass>());
+
+	public FramsClass forClass(Class<?> javaClass) throws ConstructionException {
+		FramsClass result = synchronizedCacheForBasedOnForJavaClass.get(javaClass);
+		if (result != null) {
+			return result;
+		}
+
+		log.info("building for class " + javaClass);
 
 		FramsClassAnnotation fca = javaClass.getAnnotation(FramsClassAnnotation.class);
 		if (fca == null) {
-			log.error(javaClass.getName() + " is not annotated with FramsClassAnnotation");
-			return null;
-		}
-
-		FramsClass framsClass = new FramsClass(getId(fca, javaClass), getName(fca, javaClass), "");
+			throw new ConstructionException().msg("java class is not annotated with FramsClassAnnotation").arg("java", javaClass);
+		}
+
+		id(getId(fca, javaClass));
+		name(getName(fca, javaClass));
 
 		Map<String, ParamCandidate> candidates = ParamCandidate.getAllCandidates(javaClass);
 
 		for (ParamCandidate pc : candidates.values()) {
-			framsClass.append(Param.build().id(pc.getId()).name(pc.getName()).type(getParamTypeForNativeType(pc.getType())).flags(pc.getFlags()));
-		}
-
-		return framsClass;
-	}
+			param(Param.build().id(pc.getId()).name(pc.getName()).type(getParamTypeForNativeType(pc.getType())).flags(pc.getFlags()));
+		}
+
+		result = finish();
+
+		synchronizedCacheForBasedOnForJavaClass.put(javaClass, result);
+
+		return result;
+	}
+
+
+	protected String id;
+
+	protected String name;
+
+	protected String description;
+
+	protected final List<Param> params = new LinkedList<>();
+
+	protected List<Group> groups = new ArrayList<Group>();
+
+	@ParamAnnotation
+	public FramsClassBuilder id(String id) {
+		this.id = id;
+		return this;
+	}
+
+	@ParamAnnotation
+	public FramsClassBuilder name(String name) {
+		this.name = name;
+		return this;
+	}
+
+	public FramsClassBuilder idAndName(String v) {
+		this.id = v;
+		this.name = v;
+		return this;
+	}
+
+	@ParamAnnotation(id = "desc")
+	public FramsClassBuilder description(String description) {
+		this.description = description;
+		return this;
+	}
+
+	public FramsClassBuilder() {
+	}
+
+	public FramsClass finish() {
+		return new FramsClass(id, name, description, params, groups);
+	}
+
+	public FramsClassBuilder append(Param param) {
+		params.add(param);
+		return this;
+	}
+
+	@AutoAppendAnnotation
+	public FramsClassBuilder param(ParamBuilder builder) {
+		return append(builder.finish());
+	}
+
+	@AutoAppendAnnotation
+	public FramsClassBuilder group(Group group) {
+		groups.add(group);
+		return this;
+	}
+
+	/**
+	 * @return the id
+	 */
+	@ParamAnnotation
+	public String getId() {
+		return id;
+	}
+
+	/**
+	 * @return the name
+	 */
+	@ParamAnnotation
+	public String getName() {
+		return name;
+	}
+
+	/**
+	 * @return the description
+	 */
+	@ParamAnnotation(id = "desc")
+	public String getDescription() {
+		return description;
+	}
+
+	public FramsClassBuilder group(String group) {
+		return group(new Group(group));
+	}
+
+	public ParamBuilder param(String id) {
+		return new ParamBuilder(this).id(id);
+	}
+
 }
Index: /java/main/src/main/java/com/framsticks/params/Group.java
===================================================================
--- /java/main/src/main/java/com/framsticks/params/Group.java	(revision 86)
+++ /java/main/src/main/java/com/framsticks/params/Group.java	(revision 87)
@@ -4,9 +4,15 @@
 import java.util.List;
 
+import com.framsticks.params.annotations.FramsClassAnnotation;
+import com.framsticks.params.annotations.ParamAnnotation;
+
 /**
  * @author Piotr Sniegowski
  */
+@FramsClassAnnotation
 public class Group {
-	String name;
+
+	@ParamAnnotation
+	protected String name;
 
 	/**
@@ -40,9 +46,4 @@
 	}
 
-	/*
-			 * (non-Javadoc)
-			 *
-			 * @see java.lang.Object#toString()
-			 */
 	@Override
 	public String toString() {
@@ -50,3 +51,11 @@
 	}
 
+	/**
+	 * @return the name
+	 */
+	@ParamAnnotation
+	public String getName() {
+		return name;
+	}
+
 }
Index: /java/main/src/main/java/com/framsticks/params/ListAccess.java
===================================================================
--- /java/main/src/main/java/com/framsticks/params/ListAccess.java	(revision 86)
+++ /java/main/src/main/java/com/framsticks/params/ListAccess.java	(revision 87)
@@ -75,3 +75,9 @@
 	}
 
-}
+	//TODO it could actually be used also
+	@Override
+	public boolean tryAutoAppend(Object object) {
+		return false;
+	}
+
+};
Index: /java/main/src/main/java/com/framsticks/params/Param.java
===================================================================
--- /java/main/src/main/java/com/framsticks/params/Param.java	(revision 86)
+++ /java/main/src/main/java/com/framsticks/params/Param.java	(revision 87)
@@ -2,6 +2,8 @@
 
 
-import com.framsticks.params.types.DecimalParam;
-import com.framsticks.params.types.StringParam;
+import javax.annotation.concurrent.Immutable;
+
+import com.framsticks.params.annotations.FramsClassAnnotation;
+import com.framsticks.params.annotations.ParamAnnotation;
 
 /**
@@ -14,35 +16,45 @@
  * @author Piotr Śniegowski
  */
+@Immutable
+@FramsClassAnnotation(id = "prop", name = "prop")
 public abstract class Param {
 
 	/** The parameter id. */
-	protected String id;
+	protected final String id;
 
+	//TODO
 	/**
 	 * The parameter internal id. It's set by a user to use user's own getId in
 	 * code
 	 */
-	protected String internalId;
+	protected final String internalId;
 
 	/** The parameter name. */
-	protected String name;
+	protected final String name;
 
 	/** The help (description) concerning parameter. */
-	protected String help;
+	protected final String help;
 
 	/** The number of group, that parameter belongs to. */
-	protected Integer group;
+	protected final Integer group;
 
 	/** The getFlags stored as a bit sum. */
-	protected Integer flags;
+	protected final Integer flags;
 
+	//TODO
 	/** The variable determining whether the parameter is an extra parameter. */
-	protected Integer extra;
+	protected final Integer extra;
 
-
-	public Param() {
-
+	public Param(ParamBuilder builder) {
+		id = builder.getId();
+		internalId = builder.getInternalId();
+		name = builder.getName();
+		help = builder.getHelp();
+		group = builder.getGroup();
+		flags = builder.getFlags();
+		extra = 0;
 	}
 
+	@ParamAnnotation
 	public String getId() {
 		return id;
@@ -53,16 +65,20 @@
 	}
 
+	@ParamAnnotation
 	public String getName() {
 		return name;
 	}
 
+	@ParamAnnotation
 	public String getHelp() {
 		return help;
 	}
 
+	@ParamAnnotation
 	public Integer getGroup() {
 		return group;
 	}
 
+	@ParamAnnotation
 	public Integer getFlags() {
 		return flags;
@@ -79,8 +95,4 @@
 	public Integer getExtra() {
 		return extra;
-	}
-
-	public void setInternalId(String internalId) {
-		this.internalId = internalId;
 	}
 
@@ -104,16 +116,11 @@
 	}
 
-	public static FramsClass getFramsClass() {
-		return new FramsClass("prop", "prop", null)
-			.append(Param.build().id("name").name("Name").type(StringParam.class).finish())
-			.append(Param.build().id("id").name("Id").type(StringParam.class).finish())
-			.append(Param.build().id("type").name("Type").type(StringParam.class).finish())
-			.append(Param.build().id("help").name("Help").type(StringParam.class).finish())
-			.append(Param.build().id("flags").name("Flags").type(DecimalParam.class).finish());
-	}
-
 	public static ParamBuilder build() {
 		return new ParamBuilder();
 	}
 
+	public static ParamBuilder build(FramsClassBuilder builder) {
+		return new ParamBuilder(builder);
+	}
+
 }
Index: /java/main/src/main/java/com/framsticks/params/ParamBuilder.java
===================================================================
--- /java/main/src/main/java/com/framsticks/params/ParamBuilder.java	(revision 86)
+++ /java/main/src/main/java/com/framsticks/params/ParamBuilder.java	(revision 87)
@@ -4,11 +4,12 @@
 import com.framsticks.params.annotations.ParamAnnotation;
 import com.framsticks.params.types.*;
-import com.framsticks.util.lang.Numbers;
+import com.framsticks.util.FramsticksException;
+import com.framsticks.util.lang.Strings;
 
 import org.apache.log4j.Logger;
 
-import java.util.ArrayList;
+import java.lang.reflect.InvocationTargetException;
 import java.util.Arrays;
-
+import java.util.List;
 
 /**
@@ -56,9 +57,71 @@
 	private Class<? extends Param> paramType;
 
-	private Param param;
-	private PrimitiveParam primitiveParam;
-	private String typeString;
+	private Object min;
+
+	private Object max;
+
+	private Object def;
+
+	String containedTypeName;
+
+	protected FramsClassBuilder classBuilder;
 
 	public ParamBuilder() {
+		this(null);
+	}
+
+
+	/**
+	 * @param classBuilder
+	 */
+	public ParamBuilder(FramsClassBuilder classBuilder) {
+		this.classBuilder = classBuilder;
+	}
+
+
+	/**
+	 * @return the min
+	 */
+	public Object getMin() {
+		return min;
+	}
+
+	/**
+	 * @return the max
+	 */
+	public Object getMax() {
+		return max;
+	}
+
+	/**
+	 * @return the def
+	 */
+	public Object getDef() {
+		return def;
+	}
+
+	public String getContainedTypeName() {
+		return Strings.notEmpty(containedTypeName) ? containedTypeName : null;
+	}
+
+	/**
+	 * @return the enumValues
+	 */
+	public List<String> getEnumValues() {
+		return enumValues;
+	}
+
+	/**
+	 * @return the procedureSignature
+	 */
+	public String getProcedureSignature() {
+		return procedureSignature;
+	}
+
+	/**
+	 * @return the uid
+	 */
+	public String getUid() {
+		return uid;
 	}
 
@@ -70,13 +133,10 @@
 	 *             when Param getType is not defined
 	 */
-	public Param finish()  {
-		assert param != null;
-		param.id = id;
-		param.name = name;
-		param.help = help;
-		param.group = group;
-		param.flags = flags;
-		param.internalId = internalId;
-		return param;
+	public Param finish() {
+		try {
+			return paramType.getConstructor(ParamBuilder.class).newInstance(this);
+		} catch (InstantiationException | IllegalAccessException | IllegalArgumentException | InvocationTargetException | NoSuchMethodException | SecurityException e) {
+			throw new FramsticksException().msg("failed to create param").cause(e).arg("name", name);
+		}
 	}
 
@@ -87,27 +147,13 @@
 	}
 
-	protected <T extends Param> ParamBuilder internalSetType(Class<? extends Param> type, T param) {
-		this.paramType = type;
-		this.param = param;
-		if (param instanceof PrimitiveParam) {
-			primitiveParam = (PrimitiveParam) param;
-		}
-		typeString = param.getFramsTypeName();
-		return this;
-	}
-
 	public <T extends Param> ParamBuilder type(Class<T> type) {
 		assert type != null;
-		try {
-			return internalSetType(type, type.newInstance());
-		} catch (InstantiationException | IllegalAccessException e) {
-			e.printStackTrace();
-		}
-		return this;
-	}
-
-	public <T extends Param> ParamBuilder type(T param) {
-		return internalSetType(param.getClass(), param);
-	}
+		this.paramType = type;
+		return this;
+	}
+
+	// public <T extends Param> ParamBuilder type(T param) {
+	//	return internalSetType(param.getClass(), param);
+	// }
 
 
@@ -120,4 +166,11 @@
 	}
 
+	/**
+	 * @return the internalId
+	 */
+	public String getInternalId() {
+		return internalId;
+	}
+
 	public ParamBuilder setInternalId(String internalId) {
 		this.internalId = internalId;
@@ -144,25 +197,26 @@
 
 	protected <T extends Number> void parseMinMaxDefNumber(Class<T> type, String second, String third) {
-		if (primitiveParam == null) {
-			log.warn("param is not a primitive param");
-			return;
-		}
 		if (second != null) {
-			this.primitiveParam.min = Numbers.parse(second, type);
-			if (this.primitiveParam.min == null) {
-				log.warn("value of min attribute was invalid");
-			}
+			min = second;
 		}
 		if (third != null) {
-			this.primitiveParam.max = Numbers.parse(third, type);
-			if (this.primitiveParam.max == null) {
-				log.warn("value of min attribute was invalid");
-			}
-		}
-	}
+			max = third;
+		}
+	}
+
+	protected List<String> enumValues;
+
+	public ParamBuilder enums(List<String> values) {
+		enumValues = values;
+		return type(EnumParam.class);
+	}
+
+	protected String procedureSignature;
+
+	protected String uid;
 
 	@ParamAnnotation
 	public ParamBuilder type(String type) {
-		typeString = type;
+		// typeString = type;
 
 		log.trace("parsing type: " + type);
@@ -175,16 +229,11 @@
 		switch (first.charAt(0)) {
 			case 'o': {
-				type(new ObjectParam(second != null ? second : first.substring(1)));
+				containedTypeName = second != null ? second : first.substring(1);
+				type(ObjectParam.class);
 				break;
 			}
 			case 'p': {
-				ProcedureParam procedureParam = new ProcedureParam();
-				String signature = type.substring(1);
-				try {
-					procedureParam.parseSignature(signature);
-				} catch (Exception e) {
-					log.error("invalid procedure signature '" + signature + "': " + e);
-				}
-				type(procedureParam);
+				procedureSignature = type.substring(1);
+				type(ProcedureParam.class);
 				break;
 			}
@@ -193,5 +242,5 @@
 				int tildeIndex = type.indexOf("~");
 				if (tildeIndex != -1) {
-					type(new EnumParam(new ArrayList<String>(Arrays.asList(type.substring(tildeIndex + 1).split("~")))));
+					enums(Arrays.asList(type.substring(tildeIndex + 1).split("~")));
 				} else {
 					if (first.length() >= 2) {
@@ -214,9 +263,9 @@
 						type(BooleanParam.class);
 					}
-					if (param == null) {
+					if (paramType == null) {
 						type(DecimalParam.class);
 					}
 				}
-				if (this.param instanceof DecimalParam) {
+				if (DecimalParam.class.isAssignableFrom(this.paramType)) {
 					parseMinMaxDefNumber(Integer.class, second, third);
 				}
@@ -243,5 +292,11 @@
 			}
 			case 'l': {
-				type(third != null ? new UniqueListParam(second, third) : new ArrayListParam(second));
+				containedTypeName = second;
+				if (third != null) {
+					type(UniqueListParam.class);
+					uid = third;
+				} else {
+					type(ArrayListParam.class);
+				}
 				break;
 			}
@@ -294,5 +349,5 @@
 	@ParamAnnotation
 	public String getType() {
-		return typeString;
+		return "?";
 	}
 
@@ -304,29 +359,23 @@
 	}
 
-	public <T> ParamBuilder min(T min) {
-		if (primitiveParam != null) {
-			this.primitiveParam.min = min;
-		}
-		return this;
-	}
-
-	public <T> ParamBuilder max(T max) {
-		if (primitiveParam != null) {
-			this.primitiveParam.max = max;
-		}
-		return this;
-	}
-
-	public <T> ParamBuilder def(T def) {
-		if (def != null && primitiveParam != null) {
-			this.primitiveParam.def = def;
-		}
-		return this;
-	}
-
-	public Class<?> getStorageType() {
-		assert param != null;
-		return param.getStorageType();
-	}
+	public ParamBuilder min(Object min) {
+		this.min = min;
+		return this;
+	}
+
+	public ParamBuilder max(Object max) {
+		this.max = max;
+		return this;
+	}
+
+	public ParamBuilder def(Object def) {
+		this.def = def;
+		return this;
+	}
+
+	// public Class<?> getStorageType() {
+	//	assert param != null;
+	//	return param.getStorageType();
+	// }
 
 	public Param build(String line) throws Exception {
@@ -386,5 +435,10 @@
 	}
 
-
+	public ParamBuilder defaultDef(Object def) {
+		if (this.def == null) {
+			return def(def);
+		}
+		return this;
+	}
 
 }
Index: /java/main/src/main/java/com/framsticks/params/PrimitiveParam.java
===================================================================
--- /java/main/src/main/java/com/framsticks/params/PrimitiveParam.java	(revision 86)
+++ /java/main/src/main/java/com/framsticks/params/PrimitiveParam.java	(revision 87)
@@ -1,18 +1,33 @@
 package com.framsticks.params;
 
+import com.framsticks.util.FramsticksException;
+import com.framsticks.util.lang.Numbers;
+
+import javax.annotation.concurrent.Immutable;
 
 /**
  * @author Piotr Sniegowski
  */
-public abstract class PrimitiveParam extends ValueParam {
+@Immutable
+public abstract class PrimitiveParam<S> extends ValueParam {
 
 	/** The minimum allowed value of parameter. */
-	protected Object min;
+	protected final Object min;
 
 	/** The maximum allowed value of parameter. */
-	protected Object max;
+	protected final Object max;
 
 	/** The default value of parameter. */
-	protected Object def;
+	protected final Object def;
+
+	/**
+	 * @param builder
+	 */
+	public PrimitiveParam(ParamBuilder builder) {
+		super(builder);
+		min = builder.getMin();
+		max = builder.getMax();
+		def = builder.getDef();
+	}
 
 	public void save(SinkInterface sink, Object value) {
@@ -21,12 +36,24 @@
 
 	protected <T> T tryCastAndReturn(Object value, Class<T> type) {
-		if (value == null)
+		if (value == null) {
 			return null;
+		}
 		try {
-			return type.cast(value);
+			if (type.equals(Object.class)) {
+				return type.cast(value);
+			}
+			if (value instanceof String) {
+				if (type.equals(String.class)) {
+					return type.cast(value);
+				}
+				// if (Number.class.isAssignableFrom(type)) {
+				return Numbers.parse((String) value, type);
+				// }
+				// return null;
+			} else {
+				return type.cast(value);
+			}
 		} catch (ClassCastException e) {
-			throw new ClassCastException("property \"" + name
-					+ "\" type is \"" + value.getClass().getName()
-					+ "\", not \"" + type.getName() + "\"");
+			throw new FramsticksException().msg("failed to cast").cause(e).arg("param", this).arg("actual", value.getClass()).arg("requested", type);
 		}
 	}
@@ -64,5 +91,5 @@
 	 * @throws ClassCastException the class cast exception, thrown when given getType is incorrect
 	 */
-	public <T> T getDef(Class<T> type) throws ClassCastException {
+	public <T> T getDef(Class<T> type) {
 		return tryCastAndReturn(def, type);
 	}
Index: /java/main/src/main/java/com/framsticks/params/PropertiesAccess.java
===================================================================
--- /java/main/src/main/java/com/framsticks/params/PropertiesAccess.java	(revision 86)
+++ /java/main/src/main/java/com/framsticks/params/PropertiesAccess.java	(revision 87)
@@ -27,5 +27,5 @@
 
 	public PropertiesAccess(FramsClass framsClass) {
-		setFramsClass(framsClass);
+		super(framsClass);
 	}
 
@@ -88,4 +88,8 @@
 	}
 
+	@Override
+	public boolean tryAutoAppend(Object object) {
+		return false;
+	}
 
 }
Index: /java/main/src/main/java/com/framsticks/params/ReflectionAccess.java
===================================================================
--- /java/main/src/main/java/com/framsticks/params/ReflectionAccess.java	(revision 86)
+++ /java/main/src/main/java/com/framsticks/params/ReflectionAccess.java	(revision 87)
@@ -4,10 +4,15 @@
 import java.lang.reflect.InvocationTargetException;
 import java.lang.reflect.Method;
+import java.util.Collections;
 import java.util.HashMap;
 import java.util.Map;
 
+import javax.annotation.concurrent.Immutable;
+
 import org.apache.log4j.Logger;
 
+import com.framsticks.params.annotations.AutoAppendAnnotation;
 import com.framsticks.util.FramsticksException;
+import com.framsticks.util.lang.Pair;
 
 import static com.framsticks.util.lang.Containers.*;
@@ -22,28 +27,146 @@
  */
 public class ReflectionAccess extends SimpleAbstractAccess {
-	private final static Logger log = Logger.getLogger(ReflectionAccess.class
-			.getName());
+	private final static Logger log = Logger.getLogger(ReflectionAccess.class.getName());
 
 	protected final Class<?> reflectedClass;
+	protected final Backend backend;
+
 	private Object object;
 
-
-	protected interface ReflectedSetter {
-		public <T> void set(Object object, T value) throws IllegalArgumentException, IllegalAccessException, InvocationTargetException;
-	}
-
-	protected interface ReflectedGetter {
-		public abstract <T> T get(Object object, Class<T> type) throws IllegalArgumentException, IllegalAccessException, InvocationTargetException;
-	}
-
-	protected static class ReflectedValueParam {
-		public ReflectedSetter setter;
-		public ReflectedGetter getter;
-	}
-
-	protected final Map<ValueParam, ReflectedValueParam> reflectedValueParams = new HashMap<>();
+	@Immutable
+	public static class Backend {
+
+		protected static Map<Pair<Class<?>, FramsClass>, Backend> synchronizedCache = Collections.synchronizedMap(new HashMap<Pair<Class<?>, FramsClass>, Backend>());
+
+		public static class ReflectedValueParam {
+			public ReflectedSetter setter;
+			public ReflectedGetter getter;
+		}
+
+		public interface ReflectedSetter {
+			public <T> void set(Object object, T value) throws IllegalArgumentException, IllegalAccessException, InvocationTargetException;
+		}
+
+		public interface ReflectedGetter {
+			public abstract <T> T get(Object object, Class<T> type) throws IllegalArgumentException, IllegalAccessException, InvocationTargetException;
+		}
+
+		protected final Map<ValueParam, ReflectedValueParam> params;
+		protected final Map<Class<?>, Method> autoAppendMethods;
+
+		/**
+		 * @param params
+		 */
+		public Backend(Map<ValueParam, ReflectedValueParam> params, Map<Class<?>, Method> autoAppendMethods) {
+			// this.params = Collections.unmodifiableMap(params);
+			this.params = params;
+			this.autoAppendMethods = autoAppendMethods;
+		}
+
+		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);
+			final Map<ValueParam, ReflectedValueParam> params = new HashMap<>();
+
+			Map<String, ParamCandidate> candidates = ParamCandidate.getAllCandidates(reflectedClass);
+
+			try {
+				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(Flags.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());
+					}
+
+					ReflectedValueParam rvp = new ReflectedValueParam();
+					params.put(vp, rvp);
+					final boolean primitive = pc.isPrimitive();
+					if (pc.getField() != null) {
+						final Field f = pc.getField();
+						rvp.getter = new ReflectedGetter() {
+							@Override
+							public <T> T get(Object object, Class<T> type) throws IllegalArgumentException, IllegalAccessException {
+								return type.cast(f.get(object));
+							}
+						};
+						if (!pc.isFinal()) {
+							rvp.setter = 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();
+
+						rvp.getter = 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();
+							rvp.setter = 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);
+			}
+
+			Map<Class<?>, Method> autoAppendMethods = new HashMap<>();
+
+			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);
+					}
+					autoAppendMethods.put(args[0], m);
+				}
+
+				javaClass = javaClass.getSuperclass();
+			}
+
+			backend = new Backend(params, autoAppendMethods);
+			synchronizedCache.put(id, backend);
+			return backend;
+		}
+
+	}
 
 	public ReflectionAccess(Class<?> reflectedClass) throws ConstructionException {
-		this(reflectedClass, FramsClassBuilder.buildForClass(reflectedClass));
+		this(reflectedClass, FramsClass.build().forClass(reflectedClass));
 	}
 
@@ -68,71 +191,8 @@
 
 	public ReflectionAccess(Class<?> reflectedClass, FramsClass framsClass) throws ConstructionException {
+		super(framsClass);
 		this.reflectedClass = reflectedClass;
-		setFramsClass(framsClass);
-
-		Map<String, ParamCandidate> candidates = ParamCandidate.getAllCandidates(reflectedClass);
-
-		try {
-			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(Flags.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());
-				}
-
-				ReflectedValueParam rvp = new ReflectedValueParam();
-				reflectedValueParams.put(vp, rvp);
-				final boolean primitive = pc.isPrimitive();
-				if (pc.getField() != null) {
-					final Field f = pc.getField();
-					rvp.getter = new ReflectedGetter() {
-						@Override
-						public <T> T get(Object object, Class<T> type) throws IllegalArgumentException, IllegalAccessException {
-							return type.cast(f.get(object));
-						}
-					};
-					if (!pc.isFinal()) {
-						rvp.setter = 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();
-
-					rvp.getter = 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();
-						rvp.setter = 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);
-		}
+		this.backend = Backend.getOrCreateFor(reflectedClass, framsClass);
+		// log.info("created ReflectionAccess " + this);
 	}
 
@@ -149,5 +209,5 @@
 				}
 
-				return reflectedValueParams.get(param).getter.get(object, type);
+				return backend.params.get(param).getter.get(object, type);
 			} catch (IllegalAccessException | IllegalArgumentException | InvocationTargetException e) {
 				throw new FramsticksException().msg("failed to get").cause(e);
@@ -169,5 +229,5 @@
 					throw new FramsticksException().msg("no object set");
 				}
-				ReflectedSetter s = reflectedValueParams.get(param).setter;
+				Backend.ReflectedSetter s = backend.params.get(param).setter;
 				if (s == null) {
 					throw new FramsticksException().msg("trying to set final");
@@ -238,5 +298,4 @@
 	}
 
-
 	@Override
 	public ReflectionAccess cloneAccess() throws ConstructionException {
@@ -265,4 +324,19 @@
 	}
 
+	@Override
+	public boolean tryAutoAppend(Object value) {
+		assert object != null;
+		for (Map.Entry<Class<?>, Method> a : backend.autoAppendMethods.entrySet()) {
+			if (a.getKey().isAssignableFrom(value.getClass())) {
+				try {
+					a.getValue().invoke(object, value);
+					return true;
+				} catch (IllegalAccessException | IllegalArgumentException | InvocationTargetException e) {
+					throw new FramsticksException().msg("failed to auto append").cause(e).arg("value", value).arg("into object", object).arg("with method", a.getValue());
+				}
+			}
+		}
+		return false;
+	}
 }
 
Index: /java/main/src/main/java/com/framsticks/params/Registry.java
===================================================================
--- /java/main/src/main/java/com/framsticks/params/Registry.java	(revision 86)
+++ /java/main/src/main/java/com/framsticks/params/Registry.java	(revision 87)
@@ -5,6 +5,9 @@
 import com.framsticks.params.annotations.FramsClassAnnotation;
 
+import java.util.Collections;
 import java.util.HashMap;
+import java.util.HashSet;
 import java.util.Map;
+import java.util.Set;
 
 /**
@@ -14,25 +17,63 @@
 	private static final Logger log = Logger.getLogger(Registry.class.getName());
 
-	protected final Map<String, Class<?>> reflectedClasses = new HashMap<String, Class<?>>();
-	protected final Map<String, String> invertedReflectedClasses = new HashMap<String, String>();
-	protected final Map<String, FramsClass> infoCache = new HashMap<String, FramsClass>();
+	public static class DoubleSet<K1, K2, V> {
+		protected final Map<K1, V> byId = new HashMap<>();
+		protected final Map<K2, V> byName = new HashMap<>();
+		protected final Set<V> values = new HashSet<>();
 
-	public final void registerReflectedClass(String name, String id, String className) {
-		try {
-			registerReflectedClass(name, id, Class.forName(className));
-		} catch (ClassNotFoundException e) {
-			log.fatal("class not found during registration: " + e);
+		public int size() {
+			return values.size();
+		}
+
+		public void put(K1 id, K2 name, V value) {
+			values.add(value);
+			if (id != null) {
+				byId.put(id, value);
+			}
+			if (name != null) {
+				byName.put(name, value);
+			}
+		}
+
+		public boolean containsValue(V value) {
+			return values.contains(value);
+
+		}
+
+		public boolean containsKey(String identifier) {
+			return byId.containsKey(identifier) || byName.containsKey(identifier);
+		}
+
+		public V get(String identifier) {
+			if (byId.containsKey(identifier)) {
+				return byId.get(identifier);
+			}
+			if (byName.containsKey(identifier)) {
+				return byName.get(identifier);
+			}
+			return null;
+		}
+
+		public Set<V> getValues() {
+			return Collections.unmodifiableSet(values);
 		}
 	}
 
-	public void registerReflectedClass(String name, String id,
-			Class<?> reflectedClass) {
-		if (name != null) {
-			reflectedClasses.put(name, reflectedClass);
-			invertedReflectedClasses.put(reflectedClass.getCanonicalName(), name);
-		}
-		if (id != null) {
-			reflectedClasses.put(id, reflectedClass);
-		}
+	// protected void internalRegisterClass(FramsClass framsClass, @Nullable Class<?> javaClass) {
+	// 	assert framsClass.getName() != null;
+	// 	assert framsClass.getId() != null;
+	// 	infoCacheByName.put(framsClass.getName(), framsClass);
+	// 	infoCacheById.put(framsClass.getId(), framsClass);
+
+	// 	if (javaClass != null) {
+	// 		reflectedClasses.put(framsClass, javaClass);
+	// 		infoCacheByJavaName.put(javaClass.getCanonicalName(), framsClass);
+	// 	}
+	// }
+	protected final DoubleSet<String, String, Class<?>> javaClasses = new DoubleSet<>();
+	protected final DoubleSet<String, String, FramsClass> framsClasses = new DoubleSet<>();
+
+	public void registerReflectedClass(String name, String id, Class<?> reflectedClass) {
+		javaClasses.put(id, name, reflectedClass);
 	}
 
@@ -50,6 +91,7 @@
 
 	public AccessInterface createAccess(String name, FramsClass framsClass) throws ConstructionException {
-		if (reflectedClasses.containsKey(name)) {
-			return new ReflectionAccess(reflectedClasses.get(name), framsClass);
+		assert framsClasses.containsValue(framsClass);
+		if (javaClasses.containsKey(name)) {
+			return new ReflectionAccess(javaClasses.get(name), framsClass);
 		}
 		return new PropertiesAccess(framsClass);
@@ -57,24 +99,13 @@
 
 	public void putInfoIntoCache(FramsClass framsClass) {
-		if (infoCache.containsKey(framsClass.getId())) {
-			log.info("already cached " + framsClass);
-			return;
-		}
 		log.debug("caching info for " + framsClass);
-		infoCache.put(framsClass.getId(), framsClass);
-		infoCache.put(framsClass.getName(), framsClass);
+		framsClasses.put(framsClass.getId(), framsClass.getName(), framsClass);
 	}
 
-	public FramsClass getInfoFromCache(String id) {
-		if (id == null) {
+	public FramsClass getInfoFromCache(String identifier) {
+		if (identifier == null) {
 			return null;
 		}
-		if (infoCache.containsKey(id)) {
-			return infoCache.get(id);
-		}
-		if (invertedReflectedClasses.containsKey(id)) {
-			return getInfoFromCache(invertedReflectedClasses.get(id));
-		}
-		return null;
+		return framsClasses.get(identifier);
 	}
 
@@ -102,3 +133,11 @@
 	}
 
+	public Set<Class<?>> getReflectedClasses() {
+		return javaClasses.getValues();
+	}
+
+	public Set<FramsClass> getInfoCache() {
+		return framsClasses.getValues();
+	}
+
 }
Index: /java/main/src/main/java/com/framsticks/params/SimpleAbstractAccess.java
===================================================================
--- /java/main/src/main/java/com/framsticks/params/SimpleAbstractAccess.java	(revision 86)
+++ /java/main/src/main/java/com/framsticks/params/SimpleAbstractAccess.java	(revision 87)
@@ -24,4 +24,13 @@
 	private final static Logger log = Logger.getLogger(SimpleAbstractAccess.class.getName());
 
+	protected final FramsClass framsClass;
+
+	/**
+	 * @param framsClass
+	 */
+	public SimpleAbstractAccess(FramsClass framsClass) {
+		this.framsClass = framsClass;
+	}
+
 	@Override
 	public final FramsClass getFramsClass() {
@@ -29,7 +38,4 @@
 	}
 
-	public void setFramsClass(FramsClass framsClass) {
-		this.framsClass = framsClass;
-	}
 	/**
 	 * Simple String key, value class.
@@ -51,5 +57,4 @@
 	}
 
-	protected FramsClass framsClass;
 
 	@Override
@@ -141,5 +146,5 @@
 	@Override
 	public void setMin(int i) {
-		PrimitiveParam entry = framsClass.getParamEntry(i, PrimitiveParam.class);
+		PrimitiveParam<?> entry = framsClass.getParamEntry(i, PrimitiveParam.class);
 		if (entry == null) {
 			return;
@@ -160,5 +165,5 @@
 	@Override
 	public void setMax(int i) {
-		PrimitiveParam entry = framsClass.getParamEntry(i, PrimitiveParam.class);
+		PrimitiveParam<?> entry = framsClass.getParamEntry(i, PrimitiveParam.class);
 		if (entry == null) {
 			return;
@@ -188,5 +193,5 @@
 		assert framsClass != null;
 		sink.print(framsClass.getId()).print(":").breakLine();
-		for (PrimitiveParam p : filterInstanceof(framsClass.getParamEntries(), PrimitiveParam.class)) {
+		for (PrimitiveParam<?> p : filterInstanceof(framsClass.getParamEntries(), PrimitiveParam.class)) {
 			Object value = get(p, Object.class);
 			if (value == null) {
Index: /java/main/src/main/java/com/framsticks/params/UniqueListAccess.java
===================================================================
--- /java/main/src/main/java/com/framsticks/params/UniqueListAccess.java	(revision 86)
+++ /java/main/src/main/java/com/framsticks/params/UniqueListAccess.java	(revision 87)
@@ -44,4 +44,5 @@
 			return getParam(i);
 		}
+		//TODO list access here
 		return Param.build().id(id).name(elementAccess.getId()).type("o " + elementAccess.getId()).finish();
 	}
Index: /java/main/src/main/java/com/framsticks/params/ValueParam.java
===================================================================
--- /java/main/src/main/java/com/framsticks/params/ValueParam.java	(revision 86)
+++ /java/main/src/main/java/com/framsticks/params/ValueParam.java	(revision 87)
@@ -1,9 +1,19 @@
 package com.framsticks.params;
 
+
+import javax.annotation.concurrent.Immutable;
 
 /**
  * @author Piotr Sniegowski
  */
+@Immutable
 public abstract class ValueParam extends Param {
+
+	/**
+	 * @param builder
+	 */
+	public ValueParam(ParamBuilder builder) {
+		super(builder);
+	}
 
 	public ReassignResult<?> reassign(Object newValue, Object oldValue) throws CastFailure {
@@ -11,5 +21,5 @@
 	}
 
-	public abstract <T> T getDef(Class<T> type) throws ClassCastException;
+	public abstract <T> T getDef(Class<T> type);
 
 }
Index: /java/main/src/main/java/com/framsticks/params/annotations/AutoAppendAnnotation.java
===================================================================
--- /java/main/src/main/java/com/framsticks/params/annotations/AutoAppendAnnotation.java	(revision 87)
+++ /java/main/src/main/java/com/framsticks/params/annotations/AutoAppendAnnotation.java	(revision 87)
@@ -0,0 +1,11 @@
+package com.framsticks.params.annotations;
+
+import java.lang.annotation.ElementType;
+import java.lang.annotation.Retention;
+import java.lang.annotation.RetentionPolicy;
+import java.lang.annotation.Target;
+
+@Retention(RetentionPolicy.RUNTIME)
+@Target(ElementType.METHOD)
+public @interface AutoAppendAnnotation {
+}
Index: /java/main/src/main/java/com/framsticks/params/types/ArrayListParam.java
===================================================================
--- /java/main/src/main/java/com/framsticks/params/types/ArrayListParam.java	(revision 86)
+++ /java/main/src/main/java/com/framsticks/params/types/ArrayListParam.java	(revision 87)
@@ -4,4 +4,5 @@
 import com.framsticks.params.ArrayListAccess;
 import com.framsticks.params.CastFailure;
+import com.framsticks.params.ParamBuilder;
 import com.framsticks.params.ReassignResult;
 import com.framsticks.util.lang.Casting;
@@ -12,11 +13,14 @@
 import java.util.List;
 
+import javax.annotation.concurrent.Immutable;
+
 /**
  * @author Piotr Sniegowski
  */
+@Immutable
 public class ArrayListParam extends ListParam {
 
-	public ArrayListParam(String containedTypeName) {
-		super(containedTypeName);
+	public ArrayListParam(ParamBuilder builder) {
+		super(builder);
 	}
 
Index: /java/main/src/main/java/com/framsticks/params/types/BinaryParam.java
===================================================================
--- /java/main/src/main/java/com/framsticks/params/types/BinaryParam.java	(revision 86)
+++ /java/main/src/main/java/com/framsticks/params/types/BinaryParam.java	(revision 87)
@@ -2,12 +2,23 @@
 
 import com.framsticks.params.CastFailure;
+import com.framsticks.params.ParamBuilder;
 import com.framsticks.params.PrimitiveParam;
 import com.framsticks.params.ReassignResult;
 import com.framsticks.util.lang.Numbers;
 
+import javax.annotation.concurrent.Immutable;
+
 /**
  * @author Piotr Sniegowski
  */
-public class BinaryParam extends PrimitiveParam {
+@Immutable
+public class BinaryParam extends PrimitiveParam<Integer> {
+
+	/**
+	 * @param builder
+	 */
+	public BinaryParam(ParamBuilder builder) {
+		super(builder);
+	}
 
 	@Override
Index: /java/main/src/main/java/com/framsticks/params/types/BooleanParam.java
===================================================================
--- /java/main/src/main/java/com/framsticks/params/types/BooleanParam.java	(revision 86)
+++ /java/main/src/main/java/com/framsticks/params/types/BooleanParam.java	(revision 87)
@@ -1,5 +1,8 @@
 package com.framsticks.params.types;
 
+import javax.annotation.concurrent.Immutable;
+
 import com.framsticks.params.CastFailure;
+import com.framsticks.params.ParamBuilder;
 import com.framsticks.params.PrimitiveParam;
 import com.framsticks.params.ReassignResult;
@@ -10,8 +13,12 @@
  * @author Piotr Sniegowski
  */
-public class BooleanParam extends PrimitiveParam {
+@Immutable
+public class BooleanParam extends PrimitiveParam<Boolean> {
 
-	public BooleanParam() {
-		def = false;
+	/**
+	 * @param builder
+	 */
+	public BooleanParam(ParamBuilder builder) {
+		super(builder.defaultDef(false));
 	}
 
Index: /java/main/src/main/java/com/framsticks/params/types/ColorParam.java
===================================================================
--- /java/main/src/main/java/com/framsticks/params/types/ColorParam.java	(revision 86)
+++ /java/main/src/main/java/com/framsticks/params/types/ColorParam.java	(revision 87)
@@ -2,11 +2,24 @@
 
 import com.framsticks.params.CastFailure;
+import com.framsticks.params.ParamBuilder;
 import com.framsticks.params.PrimitiveParam;
 import com.framsticks.params.ReassignResult;
+
+import javax.annotation.concurrent.Immutable;
 
 /**
  * @author Piotr Sniegowski
  */
-public class ColorParam extends PrimitiveParam {
+@Immutable
+public class ColorParam extends PrimitiveParam<Object> {
+
+
+
+	/**
+	 * @param builder
+	 */
+	public ColorParam(ParamBuilder builder) {
+		super(builder);
+	}
 
 	@Override
Index: /java/main/src/main/java/com/framsticks/params/types/DecimalParam.java
===================================================================
--- /java/main/src/main/java/com/framsticks/params/types/DecimalParam.java	(revision 86)
+++ /java/main/src/main/java/com/framsticks/params/types/DecimalParam.java	(revision 87)
@@ -2,13 +2,20 @@
 
 import com.framsticks.params.CastFailure;
+import com.framsticks.params.ParamBuilder;
 import com.framsticks.params.ReassignResult;
+
+import javax.annotation.concurrent.Immutable;
 
 /**
  * @author Piotr Sniegowski
  */
+@Immutable
 public class DecimalParam extends NumberParam<Integer> {
 
-	public DecimalParam() {
-		def = 0;
+	/**
+	 * @param builder
+	 */
+	public DecimalParam(ParamBuilder builder) {
+		super(builder.defaultDef(0));
 	}
 
Index: /java/main/src/main/java/com/framsticks/params/types/EnumParam.java
===================================================================
--- /java/main/src/main/java/com/framsticks/params/types/EnumParam.java	(revision 86)
+++ /java/main/src/main/java/com/framsticks/params/types/EnumParam.java	(revision 87)
@@ -3,10 +3,16 @@
 import java.util.List;
 
+import com.framsticks.params.ParamBuilder;
+
+import javax.annotation.concurrent.Immutable;
+
 /**
  * @author Piotr Sniegowski
  */
+@Immutable
 public class EnumParam extends DecimalParam {
 
-	List<String> enums;
+
+	protected final List<String> enums;
 
 	/**
@@ -15,7 +21,8 @@
 	 * @param enums
 	 */
-	public EnumParam(List<String> enums) {
-		assert(enums != null);
-		this.enums = enums;
+	public EnumParam(ParamBuilder builder) {
+		super(builder);
+		this.enums = builder.getEnumValues();
+		assert(this.enums != null);
 	}
 
Index: /java/main/src/main/java/com/framsticks/params/types/EventParam.java
===================================================================
--- /java/main/src/main/java/com/framsticks/params/types/EventParam.java	(revision 86)
+++ /java/main/src/main/java/com/framsticks/params/types/EventParam.java	(revision 87)
@@ -2,9 +2,22 @@
 
 import com.framsticks.params.Param;
+import com.framsticks.params.ParamBuilder;
+
+import javax.annotation.concurrent.Immutable;
 
 /**
  * @author Piotr Sniegowski
  */
+@Immutable
 public class EventParam extends Param {
+
+
+
+	/**
+	 * @param builder
+	 */
+	public EventParam(ParamBuilder builder) {
+		super(builder);
+	}
 
 	@Override
Index: /java/main/src/main/java/com/framsticks/params/types/FloatParam.java
===================================================================
--- /java/main/src/main/java/com/framsticks/params/types/FloatParam.java	(revision 86)
+++ /java/main/src/main/java/com/framsticks/params/types/FloatParam.java	(revision 87)
@@ -2,13 +2,20 @@
 
 import com.framsticks.params.CastFailure;
+import com.framsticks.params.ParamBuilder;
 import com.framsticks.params.ReassignResult;
+
+import javax.annotation.concurrent.Immutable;
 
 /**
  * @author Piotr Sniegowski
  */
+@Immutable
 public class FloatParam extends NumberParam<Double> {
 
-	public FloatParam() {
-		def = 0.0;
+	/**
+	 * @param builder
+	 */
+	public FloatParam(ParamBuilder builder) {
+		super(builder.defaultDef(0.0));
 	}
 
Index: /java/main/src/main/java/com/framsticks/params/types/ListParam.java
===================================================================
--- /java/main/src/main/java/com/framsticks/params/types/ListParam.java	(revision 86)
+++ /java/main/src/main/java/com/framsticks/params/types/ListParam.java	(revision 87)
@@ -2,12 +2,16 @@
 
 import com.framsticks.params.CompositeParam;
+import com.framsticks.params.ParamBuilder;
+
+import javax.annotation.concurrent.Immutable;
 
 /**
  * @author Piotr Sniegowski
  */
+@Immutable
 public abstract class ListParam extends CompositeParam {
 
-	public ListParam(String containedTypeName) {
-		super(containedTypeName);
+	public ListParam(ParamBuilder builder) {
+		super(builder);
 	}
 
Index: /java/main/src/main/java/com/framsticks/params/types/NumberParam.java
===================================================================
--- /java/main/src/main/java/com/framsticks/params/types/NumberParam.java	(revision 86)
+++ /java/main/src/main/java/com/framsticks/params/types/NumberParam.java	(revision 87)
@@ -3,4 +3,5 @@
 import com.framsticks.params.CastFailure;
 import com.framsticks.params.Flags;
+import com.framsticks.params.ParamBuilder;
 import com.framsticks.params.PrimitiveParam;
 import com.framsticks.params.ReassignResult;
@@ -8,8 +9,18 @@
 import com.framsticks.util.lang.Numbers;
 
+import javax.annotation.concurrent.Immutable;
+
 /**
  * @author Piotr Sniegowski
  */
-public abstract class NumberParam<T extends Number & Comparable<T>> extends PrimitiveParam {
+@Immutable
+public abstract class NumberParam<T extends Number & Comparable<T>> extends PrimitiveParam<T> {
+
+	/**
+	 * @param builder
+	 */
+	public NumberParam(ParamBuilder builder) {
+		super(builder);
+	}
 
 	@Override
@@ -21,5 +32,5 @@
 		T v = null;
 		if (newValue instanceof String) {
-			v = Numbers.parse((String)newValue, type);
+			v = Numbers.parse((String) newValue, type);
 		} else {
 			v = Casting.tryCast(type, newValue);
Index: /java/main/src/main/java/com/framsticks/params/types/ObjectParam.java
===================================================================
--- /java/main/src/main/java/com/framsticks/params/types/ObjectParam.java	(revision 86)
+++ /java/main/src/main/java/com/framsticks/params/types/ObjectParam.java	(revision 87)
@@ -4,13 +4,17 @@
 import com.framsticks.params.CastFailure;
 import com.framsticks.params.CompositeParam;
+import com.framsticks.params.ParamBuilder;
 import com.framsticks.params.ReassignResult;
+
+import javax.annotation.concurrent.Immutable;
 
 /**
  * @author Piotr Sniegowski
  */
+@Immutable
 public class ObjectParam extends CompositeParam {
 
-	public ObjectParam(String containedTypeName) {
-		super(containedTypeName);
+	public ObjectParam(ParamBuilder builder) {
+		super(builder);
 	}
 
Index: /java/main/src/main/java/com/framsticks/params/types/ProcedureParam.java
===================================================================
--- /java/main/src/main/java/com/framsticks/params/types/ProcedureParam.java	(revision 86)
+++ /java/main/src/main/java/com/framsticks/params/types/ProcedureParam.java	(revision 87)
@@ -2,4 +2,6 @@
 
 import com.framsticks.params.Param;
+import com.framsticks.params.ParamBuilder;
+import com.framsticks.util.FramsticksException;
 import com.framsticks.util.lang.Strings;
 
@@ -9,48 +11,54 @@
 import java.util.regex.Pattern;
 
+import javax.annotation.concurrent.Immutable;
+
 /**
  * @author Piotr Sniegowski
  */
+@Immutable
 public class ProcedureParam extends Param {
-	private Param resultType;
-	private List<Param> argumentsType = new ArrayList<Param>();
+	private final Param resultType;
+	private final List<Param> argumentsType = new ArrayList<Param>();
 
 	private static Pattern addressPattern = Pattern.compile("^([^\\(]+)?\\(([^\\)]*)\\)$");
+
+	/**
+	 * @param builder
+	 */
+	public ProcedureParam(ParamBuilder builder) {
+		super(builder);
+		String signature = builder.getProcedureSignature();
+		if (Strings.notEmpty(signature)) {
+			Matcher matcher = addressPattern.matcher(signature);
+			if (!matcher.matches()) {
+				throw new FramsticksException().msg("invalid signature");
+			}
+			String result = Strings.collapse(matcher.group(1));
+			resultType = (result != null) ? parseType(result, null) : null;
+			String arguments = matcher.group(2);
+			if (!Strings.notEmpty(arguments)) {
+				return;
+			}
+			for (String a : arguments.split(",")) {
+				int space = a.indexOf(' ');
+				String type;
+				String name;
+				if (space == -1) {
+					type = a;
+					name = null;
+				} else {
+					type = a.substring(0, space);
+					name = a.substring(space + 1);
+				}
+				argumentsType.add(parseType(type, name));
+			}
+		} else {
+			resultType = null;
+		}
+	}
 
 	protected static Param parseType(String type, String name) {
 		return Param.build().type(type).name(name).finish();
 	}
-
-	public void parseSignature(String signature) throws Exception {
-		if (!Strings.notEmpty(signature)) {
-			return;
-		}
-		Matcher matcher = addressPattern.matcher(signature);
-		if (!matcher.matches()) {
-			throw new Exception("invalid signature");
-		}
-		String result = Strings.collapse(matcher.group(1));
-		if (result != null) {
-			resultType = parseType(result, null);
-		}
-		String arguments = matcher.group(2);
-		if (!Strings.notEmpty(arguments)) {
-			return;
-		}
-		for (String a : arguments.split(",")) {
-			int space = a.indexOf(' ');
-			String type;
-			String name;
-			if (space == -1) {
-				type = a;
-				name = null;
-			} else {
-				type = a.substring(0, space);
-				name = a.substring(space + 1);
-			}
-			argumentsType.add(parseType(type, name));
-		}
-	}
-
 
 	@Override
Index: /java/main/src/main/java/com/framsticks/params/types/StringParam.java
===================================================================
--- /java/main/src/main/java/com/framsticks/params/types/StringParam.java	(revision 86)
+++ /java/main/src/main/java/com/framsticks/params/types/StringParam.java	(revision 87)
@@ -2,12 +2,26 @@
 
 import com.framsticks.params.CastFailure;
+import com.framsticks.params.ParamBuilder;
 import com.framsticks.params.PrimitiveParam;
 import com.framsticks.params.ReassignResult;
 import com.framsticks.params.SinkInterface;
 
+import javax.annotation.concurrent.Immutable;
+
 /**
  * @author Piotr Sniegowski
  */
-public class StringParam extends PrimitiveParam {
+@Immutable
+public class StringParam extends PrimitiveParam<String> {
+
+
+
+	/**
+	 * @param builder
+	 */
+	public StringParam(ParamBuilder builder) {
+		super(builder);
+	}
+
 	@Override
 	public Class<?> getStorageType() {
Index: /java/main/src/main/java/com/framsticks/params/types/UniqueListParam.java
===================================================================
--- /java/main/src/main/java/com/framsticks/params/types/UniqueListParam.java	(revision 86)
+++ /java/main/src/main/java/com/framsticks/params/types/UniqueListParam.java	(revision 87)
@@ -3,4 +3,5 @@
 import com.framsticks.params.AccessInterface;
 import com.framsticks.params.CastFailure;
+import com.framsticks.params.ParamBuilder;
 import com.framsticks.params.ReassignResult;
 import com.framsticks.params.UniqueListAccess;
@@ -10,14 +11,17 @@
 import java.util.Map;
 
+import javax.annotation.concurrent.Immutable;
+
 /**
  * @author Piotr Sniegowski
  */
+@Immutable
 public class UniqueListParam extends ListParam {
 
 	final String uidName;
 
-	public UniqueListParam(String containedTypeName, String uidName) {
-		super(containedTypeName);
-		this.uidName = uidName;
+	public UniqueListParam(ParamBuilder builder) {
+		super(builder);
+		this.uidName = builder.getUid();
 	}
 
Index: /java/main/src/main/java/com/framsticks/params/types/UniversalParam.java
===================================================================
--- /java/main/src/main/java/com/framsticks/params/types/UniversalParam.java	(revision 86)
+++ /java/main/src/main/java/com/framsticks/params/types/UniversalParam.java	(revision 87)
@@ -2,11 +2,24 @@
 
 import com.framsticks.params.CastFailure;
+import com.framsticks.params.ParamBuilder;
 import com.framsticks.params.PrimitiveParam;
 import com.framsticks.params.ReassignResult;
+
+import javax.annotation.concurrent.Immutable;
 
 /**
  * @author Piotr Sniegowski
  */
-public class UniversalParam extends PrimitiveParam {
+@Immutable
+public class UniversalParam extends PrimitiveParam<Object> {
+
+
+	/**
+	 * @param builder
+	 */
+	public UniversalParam(ParamBuilder builder) {
+		super(builder);
+	}
+
 	@Override
 	public Class<?> getStorageType() {
Index: /java/main/src/main/java/com/framsticks/parsers/F0Parser.java
===================================================================
--- /java/main/src/main/java/com/framsticks/parsers/F0Parser.java	(revision 86)
+++ /java/main/src/main/java/com/framsticks/parsers/F0Parser.java	(revision 87)
@@ -176,5 +176,5 @@
 			return exceptions;
 		}
-		for (PrimitiveParam p : Containers.filterInstanceof(paramsC, PrimitiveParam.class)) {
+		for (PrimitiveParam<?> p : Containers.filterInstanceof(paramsC, PrimitiveParam.class)) {
 			Object def = p.getDef(Object.class);
 			if (def != null) {
@@ -209,5 +209,5 @@
 				if (currentParam != null) {
 					if (pair.value != null) {
-						PrimitiveParam vp = (PrimitiveParam) currentParam;
+						PrimitiveParam<?> vp = (PrimitiveParam<?>) currentParam;
 						int setFlag = access.set(vp, pair.value);
 						if ((setFlag & Flags.PSET_HITMIN) != 0) {
@@ -240,5 +240,5 @@
 	}
 
-	private static Exception createBoundaryHitException(AccessInterface access, PrimitiveParam param, String value, int flag) {
+	private static Exception createBoundaryHitException(AccessInterface access, PrimitiveParam<?> param, String value, int flag) {
 		boolean minimum = (flag & Flags.PSET_HITMIN) != 0;
 		String boundary = (minimum ? param.getMin(Object.class) : param.getMax(Object.class)).toString();
Index: /java/main/src/main/java/com/framsticks/parsers/F0Writer.java
===================================================================
--- /java/main/src/main/java/com/framsticks/parsers/F0Writer.java	(revision 86)
+++ /java/main/src/main/java/com/framsticks/parsers/F0Writer.java	(revision 87)
@@ -49,5 +49,5 @@
 			}
 			Object value = access.get(param, Object.class);
-			if (omitDefaults && (param instanceof PrimitiveParam) && Misc.equals(value, ((PrimitiveParam) param).getDef(Object.class))) {
+			if (omitDefaults && (param instanceof PrimitiveParam) && Misc.equals(value, ((PrimitiveParam<?>) param).getDef(Object.class))) {
 				contiguous = false;
 				continue;
Index: /java/main/src/main/java/com/framsticks/parsers/GenotypeLoader.java
===================================================================
--- /java/main/src/main/java/com/framsticks/parsers/GenotypeLoader.java	(revision 86)
+++ /java/main/src/main/java/com/framsticks/parsers/GenotypeLoader.java	(revision 87)
@@ -13,45 +13,45 @@
 	public GenotypeLoader() throws Exception {
 
-		FramsClass entries = new FramsClass("Genotype", "org", "A Genotype with the associated performance information. All but one Genotype objects are placed in Genotype Groups. There is also a single static Genotype object not associated with a group, which is used as a temporary storage by genetic operators and some functions from GenePools.");
-		entries
-			.appendGroup(new Group("Body"))
-			.appendGroup(new Group("Performance"))
-			.appendGroup(new Group("Fitness"))
-			.appendGroup(new Group("Conversions"));
+		FramsClass entries = FramsClass.build()
+			.name("Genotype")
+			.id("org")
+			.description("A Genotype with the associated performance information. All but one Genotype objects are placed in Genotype Groups. There is also a single static Genotype object not associated with a group, which is used as a temporary storage by genetic operators and some functions from GenePools.")
+			.group("Body")
+			.group("Performance")
+			.group("Fitness")
+			.group("Conversions")
+			.param(Param.build().id("name").group(0).name("Name").type(StringParam.class).min(0).max(40))
+			.param(Param.build().id("genotype").group(0).name("Genotype").type(StringParam.class).min(1))
+			.param(Param.build().id("info").group(0).name("Info").type(StringParam.class).min(1).help("Additional information or comments"))
+			.param(Param.build().id("simi").group(1).flags(Flags.READONLY | Flags.DONTSAVE).name("Similarity").type(FloatParam.class))
+			.param(Param.build().id("energ0").group(1).flags(Flags.READONLY | Flags.DONTSAVE).name("Starting energy").type(FloatParam.class))
+			.param(Param.build().id("strsiz").group(1).flags(Flags.READONLY | Flags.DONTSAVE | Flags.USERHIDDEN).name("Body parts (deprecated; use numparts)").type(FloatParam.class))
+			.param(Param.build().id("strjoints").group(1).flags(Flags.READONLY | Flags.DONTSAVE | Flags.USERHIDDEN).name("Body joints (deprecated; use numjoints)").type(FloatParam.class))
+			.param(Param.build().id("nnsiz").group(1).flags(Flags.READONLY | Flags.DONTSAVE | Flags.USERHIDDEN).name("Brain size (deprecated; use numneurons)").type(FloatParam.class))
+			.param(Param.build().id("nncon").group(1).flags(Flags.READONLY | Flags.DONTSAVE | Flags.USERHIDDEN).name("Brain connections (deprecated; use numconnections)").type(FloatParam.class))
+			.param(Param.build().id("numparts").group(1).flags(Flags.READONLY | Flags.DONTSAVE).name("Body parts").type(FloatParam.class))
+			.param(Param.build().id("numjoints").group(1).flags(Flags.READONLY | Flags.DONTSAVE).name("Body joints").type(FloatParam.class))
+			.param(Param.build().id("numneurons").group(1).flags(Flags.READONLY | Flags.DONTSAVE).name("Brain size").type(FloatParam.class))
+			.param(Param.build().id("numconnections").group(1).flags(Flags.READONLY | Flags.DONTSAVE).name("Brain connections").type(FloatParam.class))
+			.param(Param.build().id("num").group(2).name("Ordinal number").type(DecimalParam.class))
+			.param(Param.build().id("gnum").group(2).name("Generation").type(DecimalParam.class))
+			.param(Param.build().id("popsiz").group(2).flags(Flags.USERHIDDEN).name("Deprecated; use entities").type(DecimalParam.class))
+			.param(Param.build().id("entities").group(2).flags(Flags.DONTSAVE).name("Instances").type(DecimalParam.class).help("Copies of this genotype"))
+			.param(Param.build().id("lifespan").group(2).name("Life span").type(FloatParam.class).help("Average life span"))
+			.param(Param.build().id("velocity").group(2).name("Velocity").type(FloatParam.class).help("Average velocity"))
+			.param(Param.build().id("distance").group(2).name("Distance").type(FloatParam.class))
+			.param(Param.build().id("vertvel").group(2).name("Vertical velocity").type(FloatParam.class))
+			.param(Param.build().id("vertpos").group(2).name("Vertical position").type(FloatParam.class))
+			.param(Param.build().id("fit").group(3).flags(Flags.READONLY | Flags.DONTSAVE).name("Fitness").type(FloatParam.class))
+			.param(Param.build().id("fit2").group(3).flags(Flags.READONLY | Flags.DONTSAVE).name("Final fitness").type(FloatParam.class).help("Fitness shifted by (avg-n*stddev)"))
+			.param(Param.build().id("f0genotype").group(4).flags(Flags.READONLY | Flags.DONTSAVE).name("f0 genotype").type(StringParam.class).min(1).help("converted to f0 genotype"))
+			.param(Param.build().id("user1").group(2).name("User field 1").type(UniversalParam.class))
+			.param(Param.build().id("user2").group(2).name("User field 2").type(UniversalParam.class))
+			.param(Param.build().id("user3").group(2).name("User field 3").type(UniversalParam.class))
+			.param(Param.build().id("isValid").group(0).flags(Flags.READONLY | Flags.DONTSAVE | Flags.USERHIDDEN).name("Valid").type(DecimalParam.class).min(0).max(1))
+			.param(Param.build().id("uid").group(0).flags(Flags.READONLY | Flags.USERHIDDEN).name("#").type("s").help("Unique identifier"))
+			.finish();
 
-		//getId, group-number, getFlags, getName, getType(getType min max), getHelp
-		entries
-			.append(Param.build().id("name").group(0).name("Name").type(StringParam.class).min(0).max(40))
-			.append(Param.build().id("genotype").group(0).name("Genotype").type(StringParam.class).min(1))
-			.append(Param.build().id("info").group(0).name("Info").type(StringParam.class).min(1).help("Additional information or comments"))
-			.append(Param.build().id("simi").group(1).flags(Flags.READONLY | Flags.DONTSAVE).name("Similarity").type(FloatParam.class))
-			.append(Param.build().id("energ0").group(1).flags(Flags.READONLY | Flags.DONTSAVE).name("Starting energy").type(FloatParam.class))
-			.append(Param.build().id("strsiz").group(1).flags(Flags.READONLY | Flags.DONTSAVE | Flags.USERHIDDEN).name("Body parts (deprecated; use numparts)").type(FloatParam.class))
-			.append(Param.build().id("strjoints").group(1).flags(Flags.READONLY | Flags.DONTSAVE | Flags.USERHIDDEN).name("Body joints (deprecated; use numjoints)").type(FloatParam.class))
-			.append(Param.build().id("nnsiz").group(1).flags(Flags.READONLY | Flags.DONTSAVE | Flags.USERHIDDEN).name("Brain size (deprecated; use numneurons)").type(FloatParam.class))
-			.append(Param.build().id("nncon").group(1).flags(Flags.READONLY | Flags.DONTSAVE | Flags.USERHIDDEN).name("Brain connections (deprecated; use numconnections)").type(FloatParam.class))
-			.append(Param.build().id("numparts").group(1).flags(Flags.READONLY | Flags.DONTSAVE).name("Body parts").type(FloatParam.class))
-			.append(Param.build().id("numjoints").group(1).flags(Flags.READONLY | Flags.DONTSAVE).name("Body joints").type(FloatParam.class))
-			.append(Param.build().id("numneurons").group(1).flags(Flags.READONLY | Flags.DONTSAVE).name("Brain size").type(FloatParam.class))
-			.append(Param.build().id("numconnections").group(1).flags(Flags.READONLY | Flags.DONTSAVE).name("Brain connections").type(FloatParam.class))
-			.append(Param.build().id("num").group(2).name("Ordinal number").type(DecimalParam.class))
-			.append(Param.build().id("gnum").group(2).name("Generation").type(DecimalParam.class))
-			.append(Param.build().id("popsiz").group(2).flags(Flags.USERHIDDEN).name("Deprecated; use entities").type(DecimalParam.class))
-			.append(Param.build().id("entities").group(2).flags(Flags.DONTSAVE).name("Instances").type(DecimalParam.class).help("Copies of this genotype"))
-			.append(Param.build().id("lifespan").group(2).name("Life span").type(FloatParam.class).help("Average life span"))
-			.append(Param.build().id("velocity").group(2).name("Velocity").type(FloatParam.class).help("Average velocity"))
-			.append(Param.build().id("distance").group(2).name("Distance").type(FloatParam.class))
-			.append(Param.build().id("vertvel").group(2).name("Vertical velocity").type(FloatParam.class))
-			.append(Param.build().id("vertpos").group(2).name("Vertical position").type(FloatParam.class))
-			.append(Param.build().id("fit").group(3).flags(Flags.READONLY | Flags.DONTSAVE).name("Fitness").type(FloatParam.class))
-			.append(Param.build().id("fit2").group(3).flags(Flags.READONLY | Flags.DONTSAVE).name("Final fitness").type(FloatParam.class).help("Fitness shifted by (avg-n*stddev)"))
-			.append(Param.build().id("f0genotype").group(4).flags(Flags.READONLY | Flags.DONTSAVE).name("f0 genotype").type(StringParam.class).min(1).help("converted to f0 genotype"))
-			.append(Param.build().id("user1").group(2).name("User field 1").type(UniversalParam.class))
-			.append(Param.build().id("user2").group(2).name("User field 2").type(UniversalParam.class))
-			.append(Param.build().id("user3").group(2).name("User field 3").type(UniversalParam.class))
-			.append(Param.build().id("isValid").group(0).flags(Flags.READONLY | Flags.DONTSAVE | Flags.USERHIDDEN).name("Valid").type(DecimalParam.class).min(0).max(1))
-			.append(Param.build().id("uid").group(0).flags(Flags.READONLY | Flags.USERHIDDEN).name("#").type("s").help("Unique identifier"));
-
-		ReflectionAccess reflectionParam = new ReflectionAccess(Genotype.class, entries);
+		AccessInterface reflectionParam = new PropertiesAccess(entries);
 		addAccessInterface(reflectionParam);
 		addBreakCondition(Status.AfterObject);
Index: /java/main/src/main/java/com/framsticks/parsers/Loaders.java
===================================================================
--- /java/main/src/main/java/com/framsticks/parsers/Loaders.java	(revision 86)
+++ /java/main/src/main/java/com/framsticks/parsers/Loaders.java	(revision 87)
@@ -12,32 +12,33 @@
 
 	public static FramsClass loadFramsClass(SourceInterface source) throws ConstructionException {
-		MultiParamLoader loader = new MultiParamLoader();
+		final MultiParamLoader loader = new MultiParamLoader();
 		loader.setNewSource(source);
 
-		FramsClass result = new FramsClass();
+		final FramsClassBuilder builder = FramsClass.build();
 
-		AccessInterface framsClassAccess = new ReflectionAccess(FramsClass.class, FramsClassBuilder.buildForClass(FramsClass.class));
-		AccessInterface paramBuilderAccess = new ReflectionAccess(ParamBuilder.class, FramsClassBuilder.buildForClass(ParamBuilder.class));
-		framsClassAccess.select(result);
+		final AccessInterface framsClassAccess = new ReflectionAccess(FramsClassBuilder.class, FramsClass.build().forClass(FramsClassBuilder.class));
+		AccessInterface paramBuilderAccess = new ReflectionAccess(ParamBuilder.class, FramsClass.build().forClass(ParamBuilder.class));
+		framsClassAccess.select(builder);
 		loader.addAccessInterface(framsClassAccess);
 		loader.addAccessInterface(paramBuilderAccess);
 
-		loader.addBreakCondition(MultiParamLoader.Status.AfterObject);
+		loader.addListener(MultiParamLoader.Status.AfterObject, new MultiParamLoader.StatusListener() {
+			@Override
+			public void onStatusChange() {
+				Object object = loader.returnObject();
+				if (object instanceof FramsClassBuilder) {
+					return;
+				}
+				if (framsClassAccess.select(builder).tryAutoAppend(object)) {
+					return;
+				}
+				throw new ConstructionException().msg("failed to interpretate object").arg("object", object);
+
+			}
+		});
 
 		try {
-			MultiParamLoader.Status condition;
-			while ((condition = loader.go()) != MultiParamLoader.Status.Finished) {
-				if (condition == MultiParamLoader.Status.AfterObject) {
-
-					Object object = loader.returnObject();
-					if (object instanceof FramsClass) {
-						continue;
-					}
-					if (object instanceof ParamBuilder) {
-						result.append(((ParamBuilder) object).finish());
-					}
-				}
-			}
-			return result;
+			loader.go();
+			return builder.finish();
 		} catch (Exception e) {
 			throw new ConstructionException().msg("an error occurred while loading class description").arg("before", loader.getCurrentLine()).cause(e);
Index: /java/main/src/main/java/com/framsticks/parsers/MultiParamLoader.java
===================================================================
--- /java/main/src/main/java/com/framsticks/parsers/MultiParamLoader.java	(revision 86)
+++ /java/main/src/main/java/com/framsticks/parsers/MultiParamLoader.java	(revision 87)
@@ -9,4 +9,9 @@
 
 public class MultiParamLoader {
+
+	public interface StatusListener {
+		void onStatusChange();
+	}
+
 	private final static Logger log = Logger.getLogger(MultiParamLoader.class);
 
@@ -27,4 +32,6 @@
 	}
 
+	protected final Map<Status, List<StatusListener>> listeners = new HashMap<>();
+
 	/**
 	 * Specifies the action that should be taken inside loops.
@@ -36,9 +43,10 @@
 	protected AccessInterface lastAccessInterface;
 
+	protected static FramsClass emptyFramsClass = FramsClass.build().idAndName("<empty>").finish();
 	/**
 	 * Empty Param representing unknown classes - used to omit unknown
 	 * objects in the file.
 	 */
-	protected AccessInterface emptyParam = new PropertiesAccess(new FramsClass());
+	protected AccessInterface emptyParam = new PropertiesAccess(emptyFramsClass);
 
 	/**
@@ -57,8 +65,4 @@
 	private Status status = Status.None;
 
-	private void setStatus(Status status) {
-		log.trace("changing status: " + this.status.toString() + " -> " + status.toString());
-		this.status = status;
-	}
 
 	/**
@@ -176,9 +180,7 @@
 			lastAccessInterface.load(currentSource);
 
-			if (isBreakCondition(Status.AfterObject)) {
-				// break after object creation
+			if (changeStatus(Status.AfterObject)) {
 				return LoopAction.Break;
 			}
-
 			return LoopAction.Continue;
 		} else if (status == Status.BeforeUnknown) {
@@ -187,6 +189,7 @@
 			// found unknown object
 			emptyParam.load(currentSource);
-			setStatus(Status.AfterObject);
-
+			if (changeStatus(Status.AfterObject)) {
+				return LoopAction.Break;
+			}
 			return LoopAction.Continue;
 		}
@@ -236,10 +239,11 @@
 	 */
 	private LoopAction isCommentLine(String line) {
-		if (line.charAt(0) == '#' && isBreakCondition(Status.OnComment)) {
-			// it's a simple comment - maybe we should break?
+		if (line.charAt(0) == '#') {
 			lastComment = line;
-			return LoopAction.Break;
-		}
-
+			if (changeStatus(Status.OnComment)) {
+				// it's a simple comment - maybe we should break?
+				return LoopAction.Break;
+			}
+		}
 		return LoopAction.Nothing;
 	}
@@ -255,6 +259,5 @@
 
 			if (lastAccessInterface != null) {
-				if (isBreakCondition(Status.BeforeObject)) {
-					log.debug("breaking before object");
+				if (changeStatus(Status.BeforeObject)) {
 					return LoopAction.Break;
 				} else {
@@ -263,6 +266,5 @@
 			} else {
 				lastUnknownObjectName = typeName;
-				if (isBreakCondition(Status.BeforeUnknown)) {
-					log.debug("breaking before unknown");
+				if (changeStatus(Status.BeforeUnknown)) {
 					return LoopAction.Break;
 				} else {
@@ -309,5 +311,5 @@
 		}
 
-		setStatus(Status.Finished);
+		changeStatus(Status.Finished);
 	}
 
@@ -320,5 +322,5 @@
 
 		currentSource = source;
-		setStatus(Status.Loading);
+		changeStatus(Status.Loading);
 
 		return true;
@@ -391,7 +393,13 @@
 	 * Checks whether execution should break on selected condition.
 	 */
-	private boolean isBreakCondition(Status condition) {
-		setStatus(condition);
-		return breakConditions.contains(condition);
+	private boolean changeStatus(Status status) {
+		log.trace("changing status: " + this.status.toString() + " -> " + status.toString());
+		this.status = status;
+		if (listeners.containsKey(status)) {
+			for (StatusListener l : listeners.get(status)) {
+				l.onStatusChange();
+			}
+		}
+		return breakConditions.contains(status);
 	}
 
@@ -405,3 +413,10 @@
 		return result;
 	}
+
+	public void addListener(Status status, StatusListener listener) {
+		if (!listeners.containsKey(status)) {
+			listeners.put(status, new LinkedList<StatusListener>());
+		}
+		listeners.get(status).add(listener);
+	}
 }
Index: /java/main/src/main/java/com/framsticks/parsers/Schema.java
===================================================================
--- /java/main/src/main/java/com/framsticks/parsers/Schema.java	(revision 86)
+++ /java/main/src/main/java/com/framsticks/parsers/Schema.java	(revision 87)
@@ -8,4 +8,7 @@
 
 import com.framsticks.params.*;
+import com.framsticks.params.annotations.AutoAppendAnnotation;
+import com.framsticks.params.annotations.FramsClassAnnotation;
+import com.framsticks.util.FramsticksException;
 import com.framsticks.util.lang.Numbers;
 import org.apache.log4j.Logger;
@@ -30,4 +33,5 @@
  * (please replace name and surname with my personal data)
  */
+@FramsClassAnnotation
 public class Schema {
 
@@ -42,4 +46,11 @@
 		//return new FileInputStream(new File(Schema.class.getResource("/parsers/f0def.xml").getPath()));
 		return Schema.class.getResourceAsStream("/parsers/f0def.xml");
+	}
+
+	public Schema() {
+	}
+
+	@AutoAppendAnnotation
+	public void addNeuroClass(NeuroClass neuroClass) {
 	}
 
@@ -52,5 +63,5 @@
 	 *             the exception if one occurred while reading the stream
 	 */
-	public Schema(InputStream inputStream) throws Exception {
+	public Schema(InputStream inputStream) {
 
 		DocumentBuilderFactory factory;
@@ -105,8 +116,6 @@
 
 		} catch (IOException | ParserConfigurationException | SAXException e) {
-			logger.fatal("unexpected exception occurred: ", e);
-			throw e;
-		}
-
+			throw new FramsticksException().msg("unexpected exception occurred").cause(e);
+		}
 	}
 
@@ -170,13 +179,12 @@
 	 *             the exception in case of any error
 	 */
-	private static FramsClass processClass(Node classNode) throws Exception {
+	private static FramsClass processClass(Node classNode) {
 		String classId = null;
 		String className = "";
 		String classDescription = "";
 		try {
-			classId = classNode.getAttributes().getNamedItem("ID")
-					.getNodeValue();
+			classId = classNode.getAttributes().getNamedItem("ID").getNodeValue();
 		} catch (NullPointerException e) {
-			throw new Exception("Class getId is not defined!");
+			throw new FramsticksException().msg("class id is not defined");
 		}
 
@@ -184,5 +192,5 @@
 		classDescription = getAttributeFromNode("DESCRIPTION", classNode);
 
-		FramsClass framsClass = new FramsClass(classId, className, classDescription);
+		FramsClassBuilder builder = FramsClass.build().id(classId).name(className).description(classDescription);
 
 		NodeList classProperties = classNode.getChildNodes();
@@ -195,8 +203,7 @@
 				String name = getAttribute(attributes, "NAME");
 				if (name == null) {
-					logger.warn("Group name in class \"" + classId + "\" ("
-							+ className + ") is undefined");
+					logger.warn("Group name in class \"" + classId + "\" (" + className + ") is undefined");
 				} else {
-					framsClass.appendGroup(new Group(name));
+					builder.group(new Group(name));
 				}
 			} else if ("PROP".equals(node.getNodeName())
@@ -204,11 +211,10 @@
 
 				NamedNodeMap attributes = node.getAttributes();
-				Param param = processParameter(attributes, classId);
-				framsClass.append(param);
+				processParameter(attributes, classId, builder);
 			}
 
 		}
 
-		return framsClass;
+		return builder.finish();
 	}
 
@@ -231,14 +237,14 @@
 	 *             the exception in case of any error
 	 */
-	private static Param processParameter(NamedNodeMap attributes, String classId)
-			throws Exception {
+	private static void processParameter(NamedNodeMap attributes, String classId, FramsClassBuilder classBuilder) {
 
 		String id = getAttribute(attributes, "ID");
-		if (id == null)
-			throw new Exception("Property ID in class \"" + classId
-					+ "\" is undefined");
+		if (id == null) {
+			throw new FramsticksException().msg("Property ID in class \"" + classId + "\" is undefined");
+		}
 		String type = getAttribute(attributes, "TYPE");
-		if (type == null)
-			throw new Exception("TYPE of property \"" + id + "\" is undefined");
+		if (type == null) {
+			throw new FramsticksException().msg("TYPE of property \"" + id + "\" is undefined");
+		}
 
 		String name = getAttribute(attributes, "NAME");
@@ -261,6 +267,5 @@
 		}
 
-		ParamBuilder builder = Param.build();
-		builder.id(id).name(name).help(description).group(group).flags(flags);
+		ParamBuilder builder = classBuilder.param(id).name(name).help(description).group(group).flags(flags);
 
 		builder.type(type);
@@ -282,5 +287,6 @@
 			builder.type(type);
 		}
-		return builder.finish();
+
+		classBuilder.param(builder);
 	}
 
Index: /java/main/src/main/java/com/framsticks/parsers/XmlLoader.java
===================================================================
--- /java/main/src/main/java/com/framsticks/parsers/XmlLoader.java	(revision 87)
+++ /java/main/src/main/java/com/framsticks/parsers/XmlLoader.java	(revision 87)
@@ -0,0 +1,5 @@
+package com.framsticks.parsers;
+
+public class XmlLoader {
+
+}
Index: /java/main/src/main/java/com/framsticks/remote/RemoteInstance.java
===================================================================
--- /java/main/src/main/java/com/framsticks/remote/RemoteInstance.java	(revision 86)
+++ /java/main/src/main/java/com/framsticks/remote/RemoteInstance.java	(revision 87)
@@ -124,4 +124,7 @@
 	}
 
+	public void setAddress(String address) {
+		setConnection(new ClientConnection(address));
+	}
 
 	public void setConnection(ClientConnection connection) {
@@ -372,5 +375,5 @@
 						loader.setNewSource(files.get(0).getContent());
 						loader.addBreakCondition(MultiParamLoader.Status.AfterObject);
-						ReflectionAccess access = new ReflectionAccess(ListChange.class, ListChange.getFramsClass());
+						ReflectionAccess access = new ReflectionAccess(ListChange.class, FramsClass.build().forClass(ListChange.class));
 						loader.addAccessInterface(access);
 						MultiParamLoader.Status status;
Index: /java/main/src/main/java/com/framsticks/util/FramsticksException.java
===================================================================
--- /java/main/src/main/java/com/framsticks/util/FramsticksException.java	(revision 86)
+++ /java/main/src/main/java/com/framsticks/util/FramsticksException.java	(revision 87)
@@ -35,4 +35,5 @@
 	}
 
+	@Override
 	public String getMessage() {
 		StringBuilder b = new StringBuilder();
@@ -49,4 +50,7 @@
 			b.append("(").append(d.build()).append(")");
 		}
+		if (this.getCause() instanceof FramsticksException) {
+			b.append(" caused by ").append(this.getCause().getMessage());
+		}
 		return b.toString();
 	}
Index: /java/main/src/main/java/com/framsticks/util/lang/Numbers.java
===================================================================
--- /java/main/src/main/java/com/framsticks/util/lang/Numbers.java	(revision 86)
+++ /java/main/src/main/java/com/framsticks/util/lang/Numbers.java	(revision 87)
@@ -9,5 +9,5 @@
 
 	@SuppressWarnings("unchecked")
-	public static <T extends Number> T parse(String value, Class<T> type) {
+	public static <T> T parse(String value, Class<T> type) {
 		if (!Strings.notEmpty(value)) {
 			return null;
@@ -30,4 +30,8 @@
 	}
 
+	// @SuppressWarnings("unchecked")
+	// public static <T> T parse(String value, Class<T> type) {
+	// 	return null;
+	// }
 
 
Index: /java/main/src/main/java/com/framsticks/util/lang/Pair.java
===================================================================
--- /java/main/src/main/java/com/framsticks/util/lang/Pair.java	(revision 86)
+++ /java/main/src/main/java/com/framsticks/util/lang/Pair.java	(revision 87)
@@ -18,3 +18,18 @@
 	}
 
+	@Override
+	public boolean equals(Object obj) {
+		if (!(obj instanceof Pair)) {
+			return false;
+		}
+		Pair<?, ?> p = (Pair<?, ?>) obj;
+		// return first == p.first && second.equals(se)
+		return first.equals(p.first) && second.equals(p.second);
+	}
+
+	@Override
+	public int hashCode() {
+		return (first != null ? first.hashCode() : 0) ^ (second != null ? second.hashCode() : 0);
+	}
+
 }
Index: /java/main/src/main/resources/configs/framsticks.xml
===================================================================
--- /java/main/src/main/resources/configs/framsticks.xml	(revision 87)
+++ /java/main/src/main/resources/configs/framsticks.xml	(revision 87)
@@ -0,0 +1,12 @@
+<?xml version="1.0" encoding="UTF-8"?>
+<framsticks>
+	<com.framsticks.gui.Browser>
+		<size>1500 800</size>
+		<endpoint>
+			<com.framsticks.remote.RemoteInstance>
+				<name>localhost:9009</name>
+				<address>localhost:9009</address>
+			</com.framsticks.remote.RemoteInstance>
+		</endpoint>
+	</com.framsticks.gui.Browser>
+</framsticks>
Index: /java/main/src/test/java/com/framsticks/core/ListChangeTest.java
===================================================================
--- /java/main/src/test/java/com/framsticks/core/ListChangeTest.java	(revision 87)
+++ /java/main/src/test/java/com/framsticks/core/ListChangeTest.java	(revision 87)
@@ -0,0 +1,66 @@
+package com.framsticks.core;
+
+import org.testng.annotations.BeforeMethod;
+import org.testng.annotations.Test;
+
+import com.framsticks.core.ListChange.Action;
+import com.framsticks.params.FramsClass;
+import com.framsticks.params.ListSource;
+import com.framsticks.params.ReflectionAccess;
+import com.framsticks.test.TestConfiguration;
+
+import static org.fest.assertions.Assertions.*;
+
+@Test
+public class ListChangeTest extends TestConfiguration {
+
+	FramsClass framsClass;
+	ReflectionAccess access;
+	ListChange listChange;
+
+	@BeforeMethod
+	public void clearListChange() {
+		listChange = new ListChange();
+	}
+
+	// @Test
+	// public void testEnumBehaviour() {
+	// 	for (Action a : Action.class.getEnumConstants()) {
+	// 		// assertThat(Integer.class.cast(a)).isEqualTo(a.ordinal());
+	// 		assertThat(Action.class.cast(a.ordinal())).isEqualTo(a);
+	// 	}
+
+	// }
+
+	@Test
+	public void testFramsClass() {
+		framsClass = FramsClass.build().forClass(ListChange.class);
+		assertThat(framsClass.getParamCount()).isEqualTo(3);
+	}
+
+	@Test(dependsOnMethods = "testFramsClass")
+	public void createReflectionAccess() {
+		access = new ReflectionAccess(ListChange.class, framsClass);
+	}
+
+	@Test(dependsOnMethods = "createReflectionAccess")
+	public void manualAccess() {
+		access.select(listChange);
+
+		access.set("type", 2);
+		assertThat(listChange.action).isEqualTo(Action.Modify);
+
+		listChange.action = Action.Remove;
+		assertThat(access.get("type", Integer.class)).isEqualTo(1);
+	}
+
+	@Test(dependsOnMethods = "createReflectionAccess")
+	public void load() throws Exception {
+		access.select(listChange).load(ListSource.createFrom("type:2", "pos:5", "id:test"));
+
+		assertThat(listChange.action).isEqualTo(Action.Modify);
+		assertThat(listChange.position).isEqualTo(5);
+		assertThat(listChange.identifier).isEqualTo("test");
+	}
+
+}
Index: /java/main/src/test/java/com/framsticks/core/PathTest.java
===================================================================
--- /java/main/src/test/java/com/framsticks/core/PathTest.java	(revision 87)
+++ /java/main/src/test/java/com/framsticks/core/PathTest.java	(revision 87)
@@ -0,0 +1,31 @@
+package com.framsticks.core;
+
+// import static org.mutabilitydetector.unittesting.MutabilityAssert.*;
+// import static org.mutabilitydetector.unittesting.MutabilityMatchers.*;
+// import static org.mutabilitydetector.unittesting.AllowedReason.*;
+
+// import java.util.LinkedList;
+
+import org.testng.annotations.BeforeClass;
+import org.testng.annotations.Test;
+
+import com.framsticks.test.TestConfiguration;
+
+@Test
+public class PathTest extends TestConfiguration {
+
+	@BeforeClass
+	public void setUp() {
+		// assertInstancesOf(Path.class,
+		// 		areImmutable(),
+		// 		allowingNonFinalFields(),
+		// 		provided(String.class).isAlsoImmutable(),
+		// 		provided(LinkedList.class).isAlsoImmutable()
+		// 		);
+	}
+
+	@Test
+	public void testPath() {
+	}
+
+}
Index: /java/main/src/test/java/com/framsticks/core/ProgramTest.java
===================================================================
--- /java/main/src/test/java/com/framsticks/core/ProgramTest.java	(revision 87)
+++ /java/main/src/test/java/com/framsticks/core/ProgramTest.java	(revision 87)
@@ -0,0 +1,24 @@
+package com.framsticks.core;
+
+import java.io.InputStream;
+
+import org.testng.annotations.Test;
+
+// import com.framsticks.gui.Browser;
+import com.framsticks.test.TestConfiguration;
+
+import static org.fest.assertions.Assertions.*;
+
+@Test
+public class ProgramTest extends TestConfiguration {
+
+	@Test
+	public void testConfiguration() {
+		InputStream stream = ProgramTest.class.getResourceAsStream("/configs/test.xml");
+		assertThat(stream).isNotNull();
+
+
+		//assertThat(object).isInstanceOf(Browser.class);
+	}
+
+}
Index: /java/main/src/test/java/com/framsticks/gui/BrowserTest.java
===================================================================
--- /java/main/src/test/java/com/framsticks/gui/BrowserTest.java	(revision 86)
+++ /java/main/src/test/java/com/framsticks/gui/BrowserTest.java	(revision 87)
@@ -14,5 +14,4 @@
 import org.testng.annotations.*;
 
-import com.framsticks.communication.ClientConnection;
 import com.framsticks.remote.RemoteInstance;
 import com.framsticks.test.TestConfiguration;
@@ -42,5 +41,5 @@
 		RemoteInstance localhost = new RemoteInstance();
 		localhost.setName("localhost");
-		localhost.setConnection(new ClientConnection("localhost:9009"));
+		localhost.setAddress("localhost:9009");
 
 		browser.addEndpointForInstance(localhost);
Index: /java/main/src/test/java/com/framsticks/model/CreatureTest.java
===================================================================
--- /java/main/src/test/java/com/framsticks/model/CreatureTest.java	(revision 86)
+++ /java/main/src/test/java/com/framsticks/model/CreatureTest.java	(revision 87)
@@ -4,5 +4,4 @@
 
 import com.framsticks.params.FramsClass;
-import com.framsticks.params.FramsClassBuilder;
 import com.framsticks.params.ReflectionAccess;
 import com.framsticks.params.types.ArrayListParam;
@@ -23,5 +22,5 @@
 	@Test
 	public void testFramsClass() {
-		framsClass = FramsClassBuilder.buildForClass(Creature.class);
+		framsClass = FramsClass.build().forClass(Creature.class);
 
 		assertThat(framsClass).describedAs("framsClass").isNotNull();
@@ -35,5 +34,5 @@
 	}
 
-	@Test
+	@Test(dependsOnMethods = "testFramsClass")
 	public void testReflectionAccess() throws IllegalArgumentException, IllegalAccessException, NoSuchFieldException, SecurityException {
 		access = new ReflectionAccess(Creature.class, framsClass);
Index: /java/main/src/test/java/com/framsticks/params/ParamBuilderTest.java
===================================================================
--- /java/main/src/test/java/com/framsticks/params/ParamBuilderTest.java	(revision 86)
+++ /java/main/src/test/java/com/framsticks/params/ParamBuilderTest.java	(revision 87)
@@ -18,5 +18,5 @@
 	@Test
 	public void createBuilderFramsClass() {
-		builderFramsClass = FramsClassBuilder.buildForClass(ParamBuilder.class);
+		builderFramsClass = FramsClass.build().forClass(ParamBuilder.class);
 
 		assertThat(builderFramsClass.getParamCount()).isEqualTo(6);
Index: /java/main/src/test/java/com/framsticks/parsers/F0ParserTest.java
===================================================================
--- /java/main/src/test/java/com/framsticks/parsers/F0ParserTest.java	(revision 86)
+++ /java/main/src/test/java/com/framsticks/parsers/F0ParserTest.java	(revision 87)
@@ -2,8 +2,10 @@
 
 import org.testng.annotations.*;
+
 import com.framsticks.model.*;
 import com.framsticks.model.Package;
 import com.framsticks.params.*;
 import com.framsticks.params.types.FloatParam;
+import com.framsticks.params.types.StringParam;
 import com.framsticks.test.TestConfiguration;
 import com.framsticks.util.math.Point3d;
@@ -34,9 +36,11 @@
 	public void primitiveParam() {
 		FramsClass joint = schema.getRegistry().getInfoFromCache("j");
-		PrimitiveParam dx = joint.getParamEntry("dx", PrimitiveParam.class);
+		PrimitiveParam<?> dx = joint.getParamEntry("dx", PrimitiveParam.class);
 		assertThat(dx).isInstanceOf(FloatParam.class);
 		assertThat(schema.getNeuroClasses().size()).isEqualTo(21);
 		assertThat(dx.getName()).isEqualTo("delta.x");
 		assertThat(dx.getMin(Double.class)).isEqualTo(-2.0, delta(0.0));
+
+		assertThat(schema.getRegistry().getInfoFromCache("n").getParamEntry("d", StringParam.class).getDef(String.class)).isEqualTo("N");
 	}
 
Index: /java/main/src/test/java/com/framsticks/parsers/SchemaTest.java
===================================================================
--- /java/main/src/test/java/com/framsticks/parsers/SchemaTest.java	(revision 87)
+++ /java/main/src/test/java/com/framsticks/parsers/SchemaTest.java	(revision 87)
@@ -0,0 +1,23 @@
+package com.framsticks.parsers;
+
+import org.testng.annotations.*;
+
+import com.framsticks.params.Registry;
+import com.framsticks.test.TestConfiguration;
+
+import static org.fest.assertions.Assertions.*;
+
+public class SchemaTest extends TestConfiguration {
+
+	Schema schema;
+	Registry registry;
+
+	@Test
+	public void loadSchema() {
+		schema = new Schema(Schema.getDefaultDefinitionAsStream());
+		registry = schema.getRegistry();
+		// Package.register(schema.getRegistry());
+		assertThat(registry.getInfoCache().size()).isEqualTo(5);
+	}
+
+}
Index: /java/main/src/test/resources/configs/test.xml
===================================================================
--- /java/main/src/test/resources/configs/test.xml	(revision 87)
+++ /java/main/src/test/resources/configs/test.xml	(revision 87)
@@ -0,0 +1,10 @@
+<?xml version="1.0" encoding="UTF-8"?>
+<framsticks>
+	<instance class="com.framsticks.gui.Browser">
+		<!-- <size>1500 800</size> -->
+		<endpoint class="com.framsticks.remote.RemoteInstance">
+			<name>localhost:9009</name>
+			<address>localhost:9009</address>
+		</endpoint>
+	</instance>
+</framsticks>
