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);
+	}
+
 }
