From fd1f75278de620c820ac82214e526fa85d247992 Mon Sep 17 00:00:00 2001
From: Nikolche Mihajlovski <nikolce.mihajlovski@gmail.com>
Date: Wed, 29 Aug 2012 23:01:12 +0200
Subject: [PATCH] Implemented configurable names of the generated classes
 (issue #50).

---
 .../java/com/tightdb/example/Example.java     |   4 +-
 .../com/tightdb/example/SmallExample.java     |   4 +-
 .../com/tightdb/example/TutorialExample.java  | 304 +++++++++---------
 .../tightdb/generator/CodeGenProcessor.java   | 301 +++++++++++------
 .../java/com/tightdb/generator/ModelInfo.java |  37 +++
 .../java/com/tightdb/generator/Templates.java |  12 +-
 src/main/java/com/tightdb/lib/Table.java      |  15 +
 .../resources/codegen-templates/cursor.ftl    |  18 +-
 .../resources/codegen-templates/query.ftl     |  14 +-
 .../resources/codegen-templates/table.ftl     |  18 +-
 .../resources/codegen-templates/table_add.ftl |   2 +-
 .../codegen-templates/table_insert.ftl        |   2 +-
 src/main/resources/codegen-templates/view.ftl |  10 +-
 13 files changed, 451 insertions(+), 290 deletions(-)
 create mode 100644 src/main/java/com/tightdb/generator/ModelInfo.java

diff --git a/src/main/java/com/tightdb/example/Example.java b/src/main/java/com/tightdb/example/Example.java
index f79d47013b..36cf555e3d 100644
--- a/src/main/java/com/tightdb/example/Example.java
+++ b/src/main/java/com/tightdb/example/Example.java
@@ -33,7 +33,7 @@ public static void main(String[] args) {
 	 * generating. And Refresh (F5) tightdb-example to see generated classes.
 	 */
 
-	@Table
+	@Table(row = "Employee")
 	class employee {
 		String firstName;
 		String lastName;
@@ -45,7 +45,7 @@ class employee {
 		phone phones;
 	}
 
-	@Table
+	@Table(row = "Phone")
 	class phone {
 		String type;
 		String number;
diff --git a/src/main/java/com/tightdb/example/SmallExample.java b/src/main/java/com/tightdb/example/SmallExample.java
index 6a49817ff2..ee9ca1bf40 100644
--- a/src/main/java/com/tightdb/example/SmallExample.java
+++ b/src/main/java/com/tightdb/example/SmallExample.java
@@ -22,7 +22,7 @@ public static void main(String[] args) {
 	}
 
 
-	@Table
+	@Table(row = "Employee")
 	class employee {
 		String firstName;
 		String lastName;
@@ -34,7 +34,7 @@ class employee {
 		phone phones;
 	}
 
-	@Table
+	@Table(row = "Phone")
 	class phone {
 		String type;
 		String number;
diff --git a/src/main/java/com/tightdb/example/TutorialExample.java b/src/main/java/com/tightdb/example/TutorialExample.java
index f86658e36b..ad021ebfe8 100644
--- a/src/main/java/com/tightdb/example/TutorialExample.java
+++ b/src/main/java/com/tightdb/example/TutorialExample.java
@@ -1,152 +1,152 @@
-//
-// This example is used in the short introduction "Tightdb Java Interface"
-// The @@ comments below are used for automatic extraction to the documentation
-//
-
-package com.tightdb.example;
-
-import java.io.IOException;
-
-import com.tightdb.Group;
-import com.tightdb.example.generated.People;
-import com.tightdb.example.generated.PeopleQuery;
-import com.tightdb.example.generated.PeopleTable;
-import com.tightdb.lib.Table;
-
-// @@Example: create_table @@
-public class TutorialExample {
-	
-    @Table
-    class people {
-    	String name;
-    	int age;
-    	boolean hired;
-    }
-    
-    public static void main(String[] args) {
-        PeopleTable people = new PeopleTable();
-        // ...
-		// @@EndExample@@
-		        
-		/****************************** BASIC OPERATIONS *************************/
-		
-		// @@Example: insert_rows @@
-		people.add("John", 20, true);
-		people.add("Mary", 21, false);
-		people.add("Lars", 32, true);
-		people.add("Phil", 43, false);
-		people.add("Anni", 53, true);
-		// @@EndExample@@
-		
-		// @@Example: insert_at_index @@
-		people.insert(2, "Frank", 34, true);
-		// @@EndExample@@
-		
-		/****************************** GETTERS AND SETTERS **********************/
-		
-		// @@Example: accessing_rows @@
-		// 2 ways to get the value
-		String name = people.at(2).getName(); // name => "Mary"
-		// or
-		String name2 = people.at(2).name.get();
-		
-		// 2 ways to set the value
-		people.at(2).name.set("NewName");
-		// or
-		people.at(2).setName("NewName"); 
-		// @@EndExample@@
-		
-		System.out.println("at(2).getName -> " + name + " or " + name2);
-		System.out.println("at(2).setName('NewName') -> " + people.at(2).getName());
-				
-		// @@Example: number_of_rows @@
-		if (!people.isEmpty()) {
-		    long s = people.size(); // s => 6
-		}
-		// @@EndExample@@
-		
-		System.out.println("Size = " + people.size() + "\n");
-		
-		/****************************** ITERATION OF ALL RECORDS *****************/
-		
-		// lazy iteration over the table
-		
-		// @@Example: iteration @@
-		for (People person : people) {
-			System.out.println(person.getName() + " is " + person.getAge() + " years old.");
-		}
-		// @@EndExample@@
-		
-		/****************************** SIMPLE QUERY *****************************/
-		
-		// @@Example: simple_seach @@
-		People p = people.name.equal("John").findFirst();
-		// @@EndExample@@
-		
-		System.out.println("\nFind 'John': " + p + "\n");
-		
-		/****************************** COMPLEX QUERY ****************************/
-		
-		// @@Example: advanced_search @@
-		// Define the query
-		PeopleQuery query = people.name.contains("a")
-								.group()
-									.hired.equal(false)
-									.or()
-									.name.endsWith("y")
-								.endGroup()
-								.age.between(20, 30);
-		// Count matches
-		System.out.println(query.count() + " person match query.");
-		
-		// Perform query and use the result
-		for (People person : query.findAll()) {
-		    // ... do something with matching 'person'
-		}        
-		// @@EndExample
-		
-		/****************************** DATA REMOVAL *****************************/
-		// @@Example: deleting_row @@
-		people.remove(2);
-		// @@EndExample@@
-		
-		System.out.println("\nRemoved row 2. Down to " + people.size() + " rows.\n");
-		
-		/****************************** SERIALIZE ********************************/
-		
-		// @@Example: serialisation @@
-		// Create Table in Group
-		Group group = new Group();
-		PeopleTable people1 = new PeopleTable(group);
-		
-		people1.add("John", 20, true);
-		people1.add("Mary", 21, false);
-		        
-		// Write to disk
-		try {
-		    group.writeToFile("people.tightdb");
-		} catch (IOException e) {
-		    e.printStackTrace();
-		}  
-		        
-		// Load a group from disk (and print contents)
-		Group fromDisk = new Group("people.tightdb");
-		PeopleTable people2 = new PeopleTable(fromDisk);
-		       
-		for (People person : people2) {
-		    System.out.println(person.getName() + " is " + person.getAge() + " years old");
-		}
-		
-		// Write same group to memory buffer
-		byte[] buffer = group.writeToMem();
-		        
-		// Load a group from memory (and print contents)
-		Group fromMem = new Group(buffer);
-		PeopleTable people3 = new PeopleTable(fromMem);
-		
-		for (People person : people3) {
-		    System.out.println(person.getName() + " is " + person.getAge() + " years old");
-		}
-		// @@EndExample@@
-	}
-}
+//
+// This example is used in the short introduction "Tightdb Java Interface"
+// The @@ comments below are used for automatic extraction to the documentation
+//
+
+package com.tightdb.example;
+
+import java.io.IOException;
+
+import com.tightdb.Group;
+import com.tightdb.example.generated.PeopleQuery;
+import com.tightdb.example.generated.PeopleRow;
+import com.tightdb.example.generated.PeopleTable;
+import com.tightdb.lib.Table;
+
+// @@Example: create_table @@
+public class TutorialExample {
+	
+    @Table
+    class people {
+    	String name;
+    	int age;
+    	boolean hired;
+    }
+    
+    public static void main(String[] args) {
+        PeopleTable people = new PeopleTable();
+        // ...
+		// @@EndExample@@
+		        
+		/****************************** BASIC OPERATIONS *************************/
+		
+		// @@Example: insert_rows @@
+		people.add("John", 20, true);
+		people.add("Mary", 21, false);
+		people.add("Lars", 32, true);
+		people.add("Phil", 43, false);
+		people.add("Anni", 53, true);
+		// @@EndExample@@
+		
+		// @@Example: insert_at_index @@
+		people.insert(2, "Frank", 34, true);
+		// @@EndExample@@
+		
+		/****************************** GETTERS AND SETTERS **********************/
+		
+		// @@Example: accessing_rows @@
+		// 2 ways to get the value
+		String name = people.at(2).getName(); // name => "Mary"
+		// or
+		String name2 = people.at(2).name.get();
+		
+		// 2 ways to set the value
+		people.at(2).name.set("NewName");
+		// or
+		people.at(2).setName("NewName"); 
+		// @@EndExample@@
+		
+		System.out.println("at(2).getName -> " + name + " or " + name2);
+		System.out.println("at(2).setName('NewName') -> " + people.at(2).getName());
+				
+		// @@Example: number_of_rows @@
+		if (!people.isEmpty()) {
+		    long s = people.size(); // s => 6
+		}
+		// @@EndExample@@
+		
+		System.out.println("Size = " + people.size() + "\n");
+		
+		/****************************** ITERATION OF ALL RECORDS *****************/
+		
+		// lazy iteration over the table
+		
+		// @@Example: iteration @@
+		for (PeopleRow person : people) {
+			System.out.println(person.getName() + " is " + person.getAge() + " years old.");
+		}
+		// @@EndExample@@
+		
+		/****************************** SIMPLE QUERY *****************************/
+		
+		// @@Example: simple_seach @@
+		PeopleRow p = people.name.equal("John").findFirst();
+		// @@EndExample@@
+		
+		System.out.println("\nFind 'John': " + p + "\n");
+		
+		/****************************** COMPLEX QUERY ****************************/
+		
+		// @@Example: advanced_search @@
+		// Define the query
+		PeopleQuery query = people.name.contains("a")
+								.group()
+									.hired.equal(false)
+									.or()
+									.name.endsWith("y")
+								.endGroup()
+								.age.between(20, 30);
+		// Count matches
+		System.out.println(query.count() + " person match query.");
+		
+		// Perform query and use the result
+		for (PeopleRow person : query.findAll()) {
+		    // ... do something with matching 'person'
+		}        
+		// @@EndExample
+		
+		/****************************** DATA REMOVAL *****************************/
+		// @@Example: deleting_row @@
+		people.remove(2);
+		// @@EndExample@@
+		
+		System.out.println("\nRemoved row 2. Down to " + people.size() + " rows.\n");
+		
+		/****************************** SERIALIZE ********************************/
+		
+		// @@Example: serialisation @@
+		// Create Table in Group
+		Group group = new Group();
+		PeopleTable people1 = new PeopleTable(group);
+		
+		people1.add("John", 20, true);
+		people1.add("Mary", 21, false);
+		        
+		// Write to disk
+		try {
+		    group.writeToFile("people.tightdb");
+		} catch (IOException e) {
+		    e.printStackTrace();
+		}  
+		        
+		// Load a group from disk (and print contents)
+		Group fromDisk = new Group("people.tightdb");
+		PeopleTable people2 = new PeopleTable(fromDisk);
+		       
+		for (PeopleRow person : people2) {
+		    System.out.println(person.getName() + " is " + person.getAge() + " years old");
+		}
+		
+		// Write same group to memory buffer
+		byte[] buffer = group.writeToMem();
+		        
+		// Load a group from memory (and print contents)
+		Group fromMem = new Group(buffer);
+		PeopleTable people3 = new PeopleTable(fromMem);
+		
+		for (PeopleRow person : people3) {
+		    System.out.println(person.getName() + " is " + person.getAge() + " years old");
+		}
+		// @@EndExample@@
+	}
+}
diff --git a/src/main/java/com/tightdb/generator/CodeGenProcessor.java b/src/main/java/com/tightdb/generator/CodeGenProcessor.java
index 1dcb2fe916..1db32fcd34 100644
--- a/src/main/java/com/tightdb/generator/CodeGenProcessor.java
+++ b/src/main/java/com/tightdb/generator/CodeGenProcessor.java
@@ -9,11 +9,15 @@
 import java.util.HashSet;
 import java.util.List;
 import java.util.Map;
+import java.util.Map.Entry;
 import java.util.Set;
 
 import javax.annotation.processing.RoundEnvironment;
+import javax.lang.model.element.AnnotationMirror;
+import javax.lang.model.element.AnnotationValue;
 import javax.lang.model.element.Element;
 import javax.lang.model.element.ElementKind;
+import javax.lang.model.element.ExecutableElement;
 import javax.lang.model.element.PackageElement;
 import javax.lang.model.element.TypeElement;
 import javax.lang.model.element.VariableElement;
@@ -40,22 +44,26 @@ public class CodeGenProcessor extends AbstractAnnotationProcessor {
 	private final CodeRenderer renderer = new CodeRenderer();
 
 	static {
-		NUM_TYPES = new HashSet<String>(Arrays.asList("long", "int", "byte", "short", "java.lang.Long", "java.lang.Integer", "java.lang.Byte",
-				"java.lang.Short"));
+		NUM_TYPES = new HashSet<String>(Arrays.asList("long", "int", "byte",
+				"short", "java.lang.Long", "java.lang.Integer",
+				"java.lang.Byte", "java.lang.Short"));
 	}
 
 	private Map<String, TypeElement> tables = new HashMap<String, TypeElement>();
 	private Map<String, TypeElement> subtables = new HashMap<String, TypeElement>();
+	private Map<String, ModelInfo> modelsInfo = new HashMap<String, ModelInfo>();
 	private FieldSorter fieldSorter;
 
 	@Override
-	public void processAnnotations(Set<? extends TypeElement> annotations, RoundEnvironment env) throws Exception {
+	public void processAnnotations(Set<? extends TypeElement> annotations,
+			RoundEnvironment env) throws Exception {
 		fieldSorter = new FieldSorter(logger);
 
 		for (TypeElement annotation : annotations) {
 			String annotationName = annotation.getQualifiedName().toString();
 			if (annotationName.equals(Table.class.getCanonicalName())) {
-				Set<? extends Element> elements = env.getElementsAnnotatedWith(annotation);
+				Set<? extends Element> elements = env
+						.getElementsAnnotatedWith(annotation);
 				processAnnotatedElements(elements);
 			} else {
 				logger.warn("Unexpected annotation: " + annotationName);
@@ -63,14 +71,17 @@ public void processAnnotations(Set<? extends TypeElement> annotations, RoundEnvi
 		}
 	}
 
-	private void processAnnotatedElements(Set<? extends Element> elements) throws IOException {
+	private void processAnnotatedElements(Set<? extends Element> elements)
+			throws IOException {
 		logger.info("Processing " + elements.size() + " elements...");
 
-		URI uri = filer.getResource(StandardLocation.SOURCE_OUTPUT, "", "foo").toUri();
+		URI uri = filer.getResource(StandardLocation.SOURCE_OUTPUT, "", "foo")
+				.toUri();
 		if (uri.toString().equals("foo")) {
-			throw new RuntimeException("The path of the Java source and generated files must be configured as source output! (see -s option of javac)");
+			throw new RuntimeException(
+					"The path of the Java source and generated files must be configured as source output! (see -s option of javac)");
 		}
-		
+
 		File file = new File(uri);
 		File sourcesPath = file.getParentFile();
 
@@ -79,121 +90,193 @@ private void processAnnotatedElements(Set<? extends Element> elements) throws IO
 		for (Element element : elements) {
 			if (element instanceof TypeElement) {
 				TypeElement model = (TypeElement) element;
-				String modelType = model.getQualifiedName().toString();
+				setupModelInfo(model);
+			}
+		}
+		
+		for (Element element : elements) {
+			if (element instanceof TypeElement) {
+				TypeElement model = (TypeElement) element;
+				processModel(sourcesPath, model);
+			}
+		}
+		
+	}
+
+	private void setupModelInfo(TypeElement model) {
+		AnnotationMirror annotationMirror = getAnnotationInfo(model,
+				Table.class);
+		String tableName = getAttribute(annotationMirror, "table");
+		String cursorName = getAttribute(annotationMirror, "row");
+		String viewName = getAttribute(annotationMirror, "view");
+		String queryName = getAttribute(annotationMirror, "query");
+
+		String entity = StringUtils
+				.capitalize(model.getSimpleName().toString());
+
+		tableName = tableName == null ? calculateTableName(entity) : tableName;
+		cursorName = cursorName == null ? calculateCursorName(entity)
+				: cursorName;
+		viewName = viewName == null ? calculateViewName(entity) : viewName;
+		queryName = queryName == null ? calculateQueryName(entity) : queryName;
+
+		modelsInfo.put(entity, new ModelInfo(tableName, cursorName, viewName,
+				queryName));
+	}
+
+	private void processModel(File sourcesPath, TypeElement model) {
+		String modelType = model.getQualifiedName().toString();
 
-				List<VariableElement> fields = getFields(element);
+		List<VariableElement> fields = getFields(model);
 
-				// sort the fields, due to unresolved bug in Eclipse APT
-				fieldSorter.sortFields(fields, model, sourcesPath);
+		// sort the fields, due to unresolved bug in Eclipse APT
+		fieldSorter.sortFields(fields, model, sourcesPath);
 
-				// get the capitalized model name
-				String entity = StringUtils.capitalize(model.getSimpleName().toString());
+		// get the capitalized model name
+		String entity = StringUtils
+				.capitalize(model.getSimpleName().toString());
 
-				logger.info("Generating code for entity '" + entity + "' with " + fields.size() + " columns...");
+		logger.info("Generating code for entity '" + entity + "' with "
+				+ fields.size() + " columns...");
 
-				/*********** Prepare the attributes for the templates ****************/
+		/*********** Prepare the attributes for the templates ****************/
 
-				/* Construct the list of columns */
+		/* Construct the list of columns */
 
-				int index = 0;
-				final List<Model> columns = new ArrayList<Model>();
-				for (VariableElement field : fields) {
-					String columnType = getColumnType(field);
-					if (columnType != null) {
-						String originalType = fieldType(field);
-						String fieldType = getAdjustedFieldType(field);
-						String paramType = getParamType(field);
-						String fieldName = field.getSimpleName().toString();
+		final List<Model> columns = getColumns(fields);
+		if (columns.isEmpty()) {
+			logger.warn(MSG_NO_COLUMNS, model);
+		}
 
-						boolean isSubtable = isSubtable(fieldType);
-						String subtype = isSubtable ? getSubtableType(field) : null;
+		/* Set the attributes */
 
-						Model column = new Model();
-						column.put("name", fieldName);
-						column.put("type", columnType);
-						column.put("originalType", originalType);
-						column.put("fieldType", fieldType);
-						column.put("paramType", paramType);
-						column.put("index", index++);
-						column.put("isSubtable", isSubtable);
-						column.put("subtype", subtype);
+		String packageName = calculatePackageName(model);
+		boolean isNested = isSubtable(modelType);
 
-						columns.add(column);
-					} else {
-						logger.error(MSG_INCORRECT_TYPE, field);
-					}
-				}
+		ModelInfo modelInfo = modelsInfo.get(entity);
+
+		Map<String, Object> commonAttr = new HashMap<String, Object>();
+		commonAttr.put("columns", columns);
+		commonAttr.put("isNested", isNested);
+		commonAttr.put("packageName", packageName);
+		commonAttr.put("tableName", modelInfo.getTableName());
+		commonAttr.put("viewName", modelInfo.getViewName());
+		commonAttr.put("cursorName", modelInfo.getCursorName());
+		commonAttr.put("queryName", modelInfo.getQueryName());
+		commonAttr.put("java_header", INFO_GENERATED);
 
-				if (columns.isEmpty()) {
-					logger.warn(MSG_NO_COLUMNS, model);
+		generateSources(model, modelInfo.getTableName(),
+				modelInfo.getCursorName(), modelInfo.getViewName(),
+				modelInfo.getQueryName(), packageName, commonAttr);
+	}
+
+	private List<Model> getColumns(List<VariableElement> fields) {
+		int index = 0;
+		final List<Model> columns = new ArrayList<Model>();
+		for (VariableElement field : fields) {
+			String columnType = getColumnType(field);
+			if (columnType != null) {
+				String originalType = fieldType(field);
+				String fieldType = getAdjustedFieldType(field);
+				String paramType = getParamType(field);
+				String fieldName = field.getSimpleName().toString();
+
+				boolean isSubtable = isSubtable(fieldType);
+				String subtype = isSubtable ? getSubtableType(field) : null;
+
+				Model column = new Model();
+				column.put("name", fieldName);
+				column.put("type", columnType);
+				column.put("originalType", originalType);
+				column.put("fieldType", fieldType);
+				column.put("paramType", paramType);
+				column.put("index", index++);
+				column.put("isSubtable", isSubtable);
+				
+				if (isSubtable) {
+					ModelInfo subModelInfo = modelsInfo.get(subtype);
+					column.put("subTableName", subModelInfo.getTableName());
+					column.put("subCursorName", subModelInfo.getCursorName());
+					column.put("subViewName", subModelInfo.getViewName());
+					column.put("subQueryName", subModelInfo.getQueryName());
 				}
 
-				/* Set the attributes */
+				columns.add(column);
+			} else {
+				logger.error(MSG_INCORRECT_TYPE, field);
+			}
+		}
+		return columns;
+	}
 
-				String packageName = calculatePackageName(model);
+	private void generateSources(TypeElement model, String tableName,
+			String cursorName, String viewName, String queryName,
+			String packageName, Map<String, Object> commonAttr) {
+		/*********** Generate the table class ****************/
 
-				boolean isNested = isSubtable(modelType);
+		Model table = new Model();
+		table.put("name", tableName);
+		table.putAll(commonAttr);
 
-				Map<String, Object> commonAttr = new HashMap<String, Object>();
-				commonAttr.put("entity", entity);
-				commonAttr.put("columns", columns);
-				commonAttr.put("isNested", isNested);
-				commonAttr.put("packageName", packageName);
-				commonAttr.put("java_header", INFO_GENERATED);
+		/* Generate the "add" method in the table class */
 
-				/*********** Generate the table class ****************/
+		Model methodAdd = new Model();
+		methodAdd.putAll(commonAttr);
+		table.put("add", renderer.render("table_add.ftl", methodAdd));
 
-				Model table = new Model();
-				table.put("name", entity + "Table");
-				table.putAll(commonAttr);
+		/* Generate the "insert" method in the table class */
 
-				/* Generate the "add" method in the table class */
+		Model methodInsert = new Model();
+		methodInsert.putAll(commonAttr);
+		table.put("insert", renderer.render("table_insert.ftl", methodInsert));
 
-				Model methodAdd = new Model();
-				methodAdd.put("columns", columns);
-				methodAdd.put("entity", entity);
-				table.put("add", renderer.render("table_add.ftl", methodAdd));
+		/* Generate the table class */
 
-				/* Generate the "insert" method in the table class */
+		String tableContent = renderer.render("table.ftl", table);
+		writeToSourceFile(packageName, tableName, tableContent, model);
 
-				Model methodInsert = new Model();
-				methodInsert.put("columns", columns);
-				methodInsert.put("entity", entity);
-				table.put("insert", renderer.render("table_insert.ftl", methodInsert));
+		/*********** Generate the cursor class ****************/
 
-				/* Generate the table class */
+		Model cursor = new Model();
+		cursor.put("name", cursorName);
+		cursor.putAll(commonAttr);
 
-				String tableContent = renderer.render("table.ftl", table);
-				writeToSourceFile(packageName, entity + "Table", tableContent, model);
+		String cursorContent = renderer.render("cursor.ftl", cursor);
+		writeToSourceFile(packageName, cursorName, cursorContent, model);
 
-				/*********** Generate the cursor class ****************/
+		/*********** Generate the view class ****************/
 
-				Model cursor = new Model();
-				cursor.put("name", entity);
-				cursor.putAll(commonAttr);
+		Model view = new Model();
+		view.put("name", viewName);
+		view.putAll(commonAttr);
 
-				String cursorContent = renderer.render("cursor.ftl", cursor);
-				writeToSourceFile(packageName, entity, cursorContent, model);
+		String viewContent = renderer.render("view.ftl", view);
+		writeToSourceFile(packageName, viewName, viewContent, model);
 
-				/*********** Generate the view class ****************/
+		/*********** Generate the query class ****************/
 
-				Model view = new Model();
-				view.put("name", entity + "View");
-				view.putAll(commonAttr);
+		Model query = new Model();
+		query.put("name", queryName);
+		query.putAll(commonAttr);
 
-				String viewContent = renderer.render("view.ftl", view);
-				writeToSourceFile(packageName, entity + "View", viewContent, model);
+		String queryContent = renderer.render("query.ftl", query);
+		writeToSourceFile(packageName, queryName, queryContent, model);
+	}
 
-				/*********** Generate the query class ****************/
+	private String calculateTableName(String entity) {
+		return entity + "Table";
+	}
 
-				Model query = new Model();
-				query.put("name", entity + "Query");
-				query.putAll(commonAttr);
+	private String calculateViewName(String entity) {
+		return entity + "View";
+	}
 
-				String queryContent = renderer.render("query.ftl", query);
-				writeToSourceFile(packageName, entity + "Query", queryContent, model);
-			}
-		}
+	private String calculateQueryName(String entity) {
+		return entity + "Query";
+	}
+
+	private String calculateCursorName(String entity) {
+		return entity + "Row";
 	}
 
 	private String calculatePackageName(TypeElement model) {
@@ -205,9 +288,10 @@ private String calculatePackageName(TypeElement model) {
 		if (parent instanceof PackageElement) {
 			PackageElement pkg = (PackageElement) parent;
 			String pkgName = pkg.getQualifiedName().toString();
-			return pkgName.isEmpty() ? "": pkgName + ".generated";
+			return pkgName.isEmpty() ? "" : pkgName + ".generated";
 		} else {
-			logger.error("Couldn't calculate the target package! Using default: " + DEFAULT_PACKAGE);
+			logger.error("Couldn't calculate the target package! Using default: "
+					+ DEFAULT_PACKAGE);
 			return DEFAULT_PACKAGE;
 		}
 	}
@@ -243,7 +327,8 @@ private void prepareTables(Set<? extends Element> elements) {
 		}
 	}
 
-	private boolean isReferencedBy(TypeElement model, Set<? extends Element> elements) {
+	private boolean isReferencedBy(TypeElement model,
+			Set<? extends Element> elements) {
 		String modelType = model.getQualifiedName().toString();
 
 		for (Element element : elements) {
@@ -253,10 +338,12 @@ private boolean isReferencedBy(TypeElement model, Set<? extends Element> element
 						VariableElement field = (VariableElement) enclosedElement;
 						TypeMirror fieldType = field.asType();
 						if (fieldType.getKind().equals(TypeKind.DECLARED)) {
-							Element typeAsElement = typeUtils.asElement(fieldType);
+							Element typeAsElement = typeUtils
+									.asElement(fieldType);
 							if (typeAsElement instanceof TypeElement) {
 								TypeElement typeElement = (TypeElement) typeAsElement;
-								if (typeElement.getQualifiedName().toString().equals(modelType)) {
+								if (typeElement.getQualifiedName().toString()
+										.equals(modelType)) {
 									return true;
 								}
 							}
@@ -303,7 +390,8 @@ private String fieldType(VariableElement field) {
 	}
 
 	private String fieldSimpleType(VariableElement field) {
-		return fieldType(field).replaceFirst("<.*>", "").replaceFirst(".*\\.", "");
+		return fieldType(field).replaceFirst("<.*>", "").replaceFirst(".*\\.",
+				"");
 	}
 
 	private String getSubtableType(VariableElement field) {
@@ -336,4 +424,25 @@ private String getParamType(VariableElement field) {
 		return type;
 	}
 
+	private static AnnotationMirror getAnnotationInfo(TypeElement typeElement,
+			Class<?> clazz) {
+		String clazzName = clazz.getName();
+		for (AnnotationMirror m : typeElement.getAnnotationMirrors()) {
+			if (m.getAnnotationType().toString().equals(clazzName)) {
+				return m;
+			}
+		}
+		return null;
+	}
+
+	private static String getAttribute(AnnotationMirror annotationMirror,
+			String name) {
+		for (Entry<? extends ExecutableElement, ? extends AnnotationValue> entry : annotationMirror
+				.getElementValues().entrySet()) {
+			if (entry.getKey().getSimpleName().toString().equals(name)) {
+				return String.valueOf(entry.getValue().getValue());
+			}
+		}
+		return null;
+	}
 }
diff --git a/src/main/java/com/tightdb/generator/ModelInfo.java b/src/main/java/com/tightdb/generator/ModelInfo.java
new file mode 100644
index 0000000000..1bc476608e
--- /dev/null
+++ b/src/main/java/com/tightdb/generator/ModelInfo.java
@@ -0,0 +1,37 @@
+package com.tightdb.generator;
+
+public class ModelInfo {
+
+	private final String tableName;
+
+	private final String cursorName;
+
+	private final String viewName;
+
+	private final String queryName;
+
+	public ModelInfo(String tableName, String cursorName, String viewName,
+			String queryName) {
+		this.tableName = tableName;
+		this.cursorName = cursorName;
+		this.viewName = viewName;
+		this.queryName = queryName;
+	}
+
+	public String getTableName() {
+		return tableName;
+	}
+
+	public String getCursorName() {
+		return cursorName;
+	}
+
+	public String getViewName() {
+		return viewName;
+	}
+
+	public String getQueryName() {
+		return queryName;
+	}
+
+}
diff --git a/src/main/java/com/tightdb/generator/Templates.java b/src/main/java/com/tightdb/generator/Templates.java
index ed7c49ad51..8fc2304047 100644
--- a/src/main/java/com/tightdb/generator/Templates.java
+++ b/src/main/java/com/tightdb/generator/Templates.java
@@ -2,10 +2,10 @@
 
 /* This class is automatically generated from the .ftl templates */
 public class Templates {
-    public static final String TABLE = "${java_header}\r\n<#if packageName?has_content>\r\npackage ${packageName};\r\n</#if>\r\n\r\nimport com.tightdb.*;\r\nimport com.tightdb.lib.*;\r\n\r\n/**\r\n * This class represents a TightDB table and was automatically generated.\r\n */\r\n<#if isNested>public class ${entity}Table extends AbstractSubtable<${entity}, ${entity}View, ${entity}Query> {\r\n<#else>public class ${entity}Table extends AbstractTable<${entity}, ${entity}View, ${entity}Query> {\r\n</#if>\r\n\tpublic static final EntityTypes<${entity}Table, ${entity}View, ${entity}, ${entity}Query> TYPES = new EntityTypes<${entity}Table, ${entity}View, ${entity}, ${entity}Query>(${entity}Table.class, ${entity}View.class, ${entity}.class, ${entity}Query.class);\r\n\r\n<#foreach f in columns><#if f.isSubtable>\tpublic final ${f.type}RowsetColumn<${entity}, ${entity}View, ${entity}Query, ${f.subtype}Table> ${f.name} = new ${f.type}RowsetColumn<${entity}, ${entity}View, ${entity}Query, ${f.subtype}Table>(TYPES, table, ${f.index}, \"${f.name}\", ${f.subtype}Table.class);\r\n<#else>\tpublic final ${f.type}RowsetColumn<${entity}, ${entity}View, ${entity}Query> ${f.name} = new ${f.type}RowsetColumn<${entity}, ${entity}View, ${entity}Query>(TYPES, table, ${f.index}, \"${f.name}\");\r\n</#if></#foreach>\r\n<#if isNested>\tpublic ${entity}Table(TableBase subtableBase) {\r\n\t\tsuper(TYPES, subtableBase);\r\n\t}\r\n<#else>\tpublic ${entity}Table() {\r\n\t\tsuper(TYPES);\r\n\t}\r\n\r\n\tpublic ${entity}Table(Group group) {\r\n\t\tsuper(TYPES, group);\r\n\t}\r\n</#if>\r\n\tpublic static void specifyStructure(TableSpec spec) {\r\n<#foreach f in columns><#if f.isSubtable>        add${f.type}Column(spec, \"${f.name}\", new ${f.subtype}Table(null));\r\n<#else>        add${f.type}Column(spec, \"${f.name}\");\r\n</#if></#foreach>    }\r\n\r\n${add}\r\n\r\n${insert}\r\n\r\n}\r\n";
-    public static final String TABLE_ADD = "    public ${entity} add(<#foreach f in columns><#if !f.isSubtable><#if (f_index > 0)>, </#if>${f.originalType} ${f.name}</#if></#foreach>) {\r\n        try {\r\n            long position = size();\r\n\r\n\t\t<#foreach f in columns><#if f.isSubtable>\tinsert${f.type}(${f.index}, position);\r\n\t\t<#else>\tinsert${f.type}(${f.index}, position, ${f.name});\r\n\t\t</#if></#foreach>   insertDone();\r\n\r\n            return cursor(position);\r\n        } catch (Exception e) {\r\n            throw addRowException(e);\r\n        }\r\n    }";
-    public static final String TABLE_INSERT = "    public ${entity} insert(long position, <#foreach f in columns><#if !f.isSubtable><#if (f_index > 0)>, </#if>${f.originalType} ${f.name}</#if></#foreach>) {\r\n        try {\r\n        <#foreach f in columns><#if f.isSubtable>\tinsert${f.type}(${f.index}, position);\r\n        <#else>\tinsert${f.type}(${f.index}, position, ${f.name});\r\n        </#if></#foreach>   insertDone();\r\n\r\n            return cursor(position);\r\n        } catch (Exception e) {\r\n            throw insertRowException(e);\r\n        }\r\n    }";
-    public static final String CURSOR = "${java_header}\r\n<#if packageName?has_content>\r\npackage ${packageName};\r\n</#if>\r\n\r\nimport com.tightdb.*;\r\nimport com.tightdb.lib.*;\r\n\r\n/**\r\n * This class represents a TightDB cursor and was automatically generated.\r\n */\r\npublic class ${entity} extends AbstractCursor<${entity}> {\r\n\r\n<#foreach f in columns><#if f.isSubtable>    public final ${f.type}CursorColumn<${entity}, ${entity}View, ${entity}Query, ${f.subtype}, ${f.subtype}Table> ${f.name};\r\n<#else>    public final ${f.type}CursorColumn<${entity}, ${entity}View, ${entity}Query> ${f.name};\r\n</#if></#foreach>\r\n\tpublic ${entity}(IRowsetBase rowset, long position) {\r\n\t\tsuper(${entity}Table.TYPES, rowset, position);\r\n\r\n<#foreach f in columns><#if f.isSubtable>        ${f.name} = new ${f.type}CursorColumn<${entity}, ${entity}View, ${entity}Query, ${f.subtype}, ${f.subtype}Table>(${entity}Table.TYPES, this, ${f.index}, \"${f.name}\", ${f.subtype}Table.class);\r\n<#else>        ${f.name} = new ${f.type}CursorColumn<${entity}, ${entity}View, ${entity}Query>(${entity}Table.TYPES, this, ${f.index}, \"${f.name}\");\r\n</#if></#foreach>\t}\r\n\r\n<#foreach f in columns><#if f.isSubtable>\tpublic ${f.subtype}Table get${f.name?cap_first}() {\r\n<#else>\tpublic ${f.fieldType} get${f.name?cap_first}() {\r\n</#if>\t\treturn this.${f.name}.get();\r\n\t}\r\n\r\n<#if f.isSubtable>\tpublic void set${f.name?cap_first}(${f.subtype}Table ${f.name}) {\r\n<#else>\tpublic void set${f.name?cap_first}(${f.fieldType} ${f.name}) {\r\n</#if>\t\tthis.${f.name}.set(${f.name});\r\n\t}\r\n\r\n</#foreach>\t@Override\r\n\tpublic AbstractColumn<?, ?, ?, ?>[] columns() {\r\n\t\treturn getColumnsArray(<#foreach f in columns>${f.name}<#if f_has_next>, </#if></#foreach>);\r\n\t}\r\n\r\n}\r\n";
-    public static final String VIEW = "${java_header}\r\n<#if packageName?has_content>\r\npackage ${packageName};\r\n</#if>\r\n\r\nimport com.tightdb.*;\r\nimport com.tightdb.lib.*;\r\n\r\n/**\r\n * This class represents a TightDB view and was automatically generated.\r\n */\r\npublic class ${entity}View extends AbstractView<${entity}, ${entity}View, ${entity}Query> {\r\n\r\n<#foreach f in columns><#if f.isSubtable>\tpublic final ${f.type}RowsetColumn<${entity}, ${entity}View, ${entity}Query, ${f.subtype}Table> ${f.name} = new ${f.type}RowsetColumn<${entity}, ${entity}View, ${entity}Query, ${f.subtype}Table>(${entity}Table.TYPES, rowset, ${f.index}, \"${f.name}\", ${f.subtype}Table.class);\r\n<#else>\tpublic final ${f.type}RowsetColumn<${entity}, ${entity}View, ${entity}Query> ${f.name} = new ${f.type}RowsetColumn<${entity}, ${entity}View, ${entity}Query>(${entity}Table.TYPES, rowset, ${f.index}, \"${f.name}\");\r\n</#if></#foreach>\r\n\tpublic ${entity}View(TableViewBase viewBase) {\r\n\t\tsuper(${entity}Table.TYPES, viewBase);\r\n\t}\r\n\r\n}\r\n";
-    public static final String QUERY = "${java_header}\r\n<#if packageName?has_content>\r\npackage ${packageName};\r\n</#if>\r\n\r\nimport com.tightdb.*;\r\nimport com.tightdb.lib.*;\r\n\r\n/**\r\n * This class represents a TightDB query and was automatically generated.\r\n */\r\npublic class ${entity}Query extends AbstractQuery<${entity}Query, ${entity}, ${entity}View> {\r\n\r\n<#foreach f in columns><#if f.isSubtable>    public final ${f.type}QueryColumn<${entity}, ${entity}View, ${entity}Query, ${f.subtype}Table> ${f.name};\r\n<#else>    public final ${f.type}QueryColumn<${entity}, ${entity}View, ${entity}Query> ${f.name};\r\n</#if></#foreach>\r\n\tpublic ${entity}Query(TableBase table, TableQuery query) {\r\n\t\tsuper(${entity}Table.TYPES, table, query);\r\n<#foreach f in columns><#if f.isSubtable>        ${f.name} = new ${f.type}QueryColumn<${entity}, ${entity}View, ${entity}Query, ${f.subtype}Table>(${entity}Table.TYPES, table, query, ${f.index}, \"${f.name}\", ${f.subtype}Table.class);\r\n<#else>        ${f.name} = new ${f.type}QueryColumn<${entity}, ${entity}View, ${entity}Query>(${entity}Table.TYPES, table, query, ${f.index}, \"${f.name}\");\r\n</#if></#foreach>\t}\r\n\r\n}\r\n";
+    public static final String TABLE = "${java_header}\r\n<#if packageName?has_content>\r\npackage ${packageName};\r\n</#if>\r\n\r\nimport com.tightdb.*;\r\nimport com.tightdb.lib.*;\r\n\r\n/**\r\n * This class represents a TightDB table and was automatically generated.\r\n */\r\n<#if isNested>public class ${tableName} extends AbstractSubtable<${cursorName}, ${viewName}, ${queryName}> {\r\n<#else>public class ${tableName} extends AbstractTable<${cursorName}, ${viewName}, ${queryName}> {\r\n</#if>\r\n\tpublic static final EntityTypes<${tableName}, ${viewName}, ${cursorName}, ${queryName}> TYPES = new EntityTypes<${tableName}, ${viewName}, ${cursorName}, ${queryName}>(${tableName}.class, ${viewName}.class, ${cursorName}.class, ${queryName}.class);\r\n\r\n<#foreach f in columns><#if f.isSubtable>\tpublic final ${f.type}RowsetColumn<${cursorName}, ${viewName}, ${queryName}, ${f.subTableName}> ${f.name} = new ${f.type}RowsetColumn<${cursorName}, ${viewName}, ${queryName}, ${f.subTableName}>(TYPES, table, ${f.index}, \"${f.name}\", ${f.subTableName}.class);\r\n<#else>\tpublic final ${f.type}RowsetColumn<${cursorName}, ${viewName}, ${queryName}> ${f.name} = new ${f.type}RowsetColumn<${cursorName}, ${viewName}, ${queryName}>(TYPES, table, ${f.index}, \"${f.name}\");\r\n</#if></#foreach>\r\n<#if isNested>\tpublic ${tableName}(TableBase subtableBase) {\r\n\t\tsuper(TYPES, subtableBase);\r\n\t}\r\n<#else>\tpublic ${tableName}() {\r\n\t\tsuper(TYPES);\r\n\t}\r\n\r\n\tpublic ${tableName}(Group group) {\r\n\t\tsuper(TYPES, group);\r\n\t}\r\n</#if>\r\n\tpublic static void specifyStructure(TableSpec spec) {\r\n<#foreach f in columns><#if f.isSubtable>        add${f.type}Column(spec, \"${f.name}\", new ${f.subTableName}(null));\r\n<#else>        add${f.type}Column(spec, \"${f.name}\");\r\n</#if></#foreach>    }\r\n\r\n${add}\r\n\r\n${insert}\r\n\r\n}\r\n";
+    public static final String TABLE_ADD = "    public ${cursorName} add(<#foreach f in columns><#if !f.isSubtable><#if (f_index > 0)>, </#if>${f.originalType} ${f.name}</#if></#foreach>) {\r\n        try {\r\n            long position = size();\r\n\r\n\t\t<#foreach f in columns><#if f.isSubtable>\tinsert${f.type}(${f.index}, position);\r\n\t\t<#else>\tinsert${f.type}(${f.index}, position, ${f.name});\r\n\t\t</#if></#foreach>   insertDone();\r\n\r\n            return cursor(position);\r\n        } catch (Exception e) {\r\n            throw addRowException(e);\r\n        }\r\n    }";
+    public static final String TABLE_INSERT = "    public ${cursorName} insert(long position, <#foreach f in columns><#if !f.isSubtable><#if (f_index > 0)>, </#if>${f.originalType} ${f.name}</#if></#foreach>) {\r\n        try {\r\n        <#foreach f in columns><#if f.isSubtable>\tinsert${f.type}(${f.index}, position);\r\n        <#else>\tinsert${f.type}(${f.index}, position, ${f.name});\r\n        </#if></#foreach>   insertDone();\r\n\r\n            return cursor(position);\r\n        } catch (Exception e) {\r\n            throw insertRowException(e);\r\n        }\r\n    }";
+    public static final String CURSOR = "${java_header}\r\n<#if packageName?has_content>\r\npackage ${packageName};\r\n</#if>\r\n\r\nimport com.tightdb.*;\r\nimport com.tightdb.lib.*;\r\n\r\n/**\r\n * This class represents a TightDB cursor and was automatically generated.\r\n */\r\npublic class ${name} extends AbstractCursor<${name}> {\r\n\r\n<#foreach f in columns><#if f.isSubtable>    public final ${f.type}CursorColumn<${name}, ${viewName}, ${queryName}, ${f.subCursorName}, ${f.subTableName}> ${f.name};\r\n<#else>    public final ${f.type}CursorColumn<${cursorName}, ${viewName}, ${queryName}> ${f.name};\r\n</#if></#foreach>\r\n\tpublic ${cursorName}(IRowsetBase rowset, long position) {\r\n\t\tsuper(${tableName}.TYPES, rowset, position);\r\n\r\n<#foreach f in columns><#if f.isSubtable>        ${f.name} = new ${f.type}CursorColumn<${cursorName}, ${viewName}, ${queryName}, ${f.subCursorName}, ${f.subTableName}>(${tableName}.TYPES, this, ${f.index}, \"${f.name}\", ${f.subTableName}.class);\r\n<#else>        ${f.name} = new ${f.type}CursorColumn<${cursorName}, ${viewName}, ${queryName}>(${tableName}.TYPES, this, ${f.index}, \"${f.name}\");\r\n</#if></#foreach>\t}\r\n\r\n<#foreach f in columns><#if f.isSubtable>\tpublic ${f.subTableName} get${f.name?cap_first}() {\r\n<#else>\tpublic ${f.fieldType} get${f.name?cap_first}() {\r\n</#if>\t\treturn this.${f.name}.get();\r\n\t}\r\n\r\n<#if f.isSubtable>\tpublic void set${f.name?cap_first}(${f.subTableName} ${f.name}) {\r\n<#else>\tpublic void set${f.name?cap_first}(${f.fieldType} ${f.name}) {\r\n</#if>\t\tthis.${f.name}.set(${f.name});\r\n\t}\r\n\r\n</#foreach>\t@Override\r\n\tpublic AbstractColumn<?, ?, ?, ?>[] columns() {\r\n\t\treturn getColumnsArray(<#foreach f in columns>${f.name}<#if f_has_next>, </#if></#foreach>);\r\n\t}\r\n\r\n}\r\n";
+    public static final String VIEW = "${java_header}\r\n<#if packageName?has_content>\r\npackage ${packageName};\r\n</#if>\r\n\r\nimport com.tightdb.*;\r\nimport com.tightdb.lib.*;\r\n\r\n/**\r\n * This class represents a TightDB view and was automatically generated.\r\n */\r\npublic class ${viewName} extends AbstractView<${cursorName}, ${viewName}, ${queryName}> {\r\n\r\n<#foreach f in columns><#if f.isSubtable>\tpublic final ${f.type}RowsetColumn<${cursorName}, ${viewName}, ${queryName}, ${f.subTableName}> ${f.name} = new ${f.type}RowsetColumn<${cursorName}, ${viewName}, ${queryName}, ${f.subTableName}>(${tableName}.TYPES, rowset, ${f.index}, \"${f.name}\", ${f.subTableName}.class);\r\n<#else>\tpublic final ${f.type}RowsetColumn<${cursorName}, ${viewName}, ${queryName}> ${f.name} = new ${f.type}RowsetColumn<${cursorName}, ${viewName}, ${queryName}>(${tableName}.TYPES, rowset, ${f.index}, \"${f.name}\");\r\n</#if></#foreach>\r\n\tpublic ${viewName}(TableViewBase viewBase) {\r\n\t\tsuper(${tableName}.TYPES, viewBase);\r\n\t}\r\n\r\n}\r\n";
+    public static final String QUERY = "${java_header}\r\n<#if packageName?has_content>\r\npackage ${packageName};\r\n</#if>\r\n\r\nimport com.tightdb.*;\r\nimport com.tightdb.lib.*;\r\n\r\n/**\r\n * This class represents a TightDB query and was automatically generated.\r\n */\r\npublic class ${name} extends AbstractQuery<${name}, ${cursorName}, ${viewName}> {\r\n\r\n<#foreach f in columns><#if f.isSubtable>    public final ${f.type}QueryColumn<${cursorName}, ${viewName}, ${name}, ${f.subTableName}> ${f.name};\r\n<#else>    public final ${f.type}QueryColumn<${cursorName}, ${viewName}, ${name}> ${f.name};\r\n</#if></#foreach>\r\n\tpublic ${name}(TableBase table, TableQuery query) {\r\n\t\tsuper(${tableName}.TYPES, table, query);\r\n<#foreach f in columns><#if f.isSubtable>        ${f.name} = new ${f.type}QueryColumn<${cursorName}, ${viewName}, ${name}, ${f.subTableName}>(${tableName}.TYPES, table, query, ${f.index}, \"${f.name}\", ${f.subTableName}.class);\r\n<#else>        ${f.name} = new ${f.type}QueryColumn<${cursorName}, ${viewName}, ${name}>(${tableName}.TYPES, table, query, ${f.index}, \"${f.name}\");\r\n</#if></#foreach>\t}\r\n\r\n}\r\n";
 }
diff --git a/src/main/java/com/tightdb/lib/Table.java b/src/main/java/com/tightdb/lib/Table.java
index ac72e31804..72eff40e48 100644
--- a/src/main/java/com/tightdb/lib/Table.java
+++ b/src/main/java/com/tightdb/lib/Table.java
@@ -1,5 +1,20 @@
 package com.tightdb.lib;
 
+import java.lang.annotation.ElementType;
+import java.lang.annotation.Retention;
+import java.lang.annotation.RetentionPolicy;
+import java.lang.annotation.Target;
+
+@Retention(RetentionPolicy.SOURCE)
+@Target(ElementType.TYPE)
 public @interface Table {
 
+	String row() default "";
+
+	String table() default "";
+
+	String view() default "";
+
+	String query() default "";
+
 }
diff --git a/src/main/resources/codegen-templates/cursor.ftl b/src/main/resources/codegen-templates/cursor.ftl
index 3b27d908b4..5a8a14984a 100644
--- a/src/main/resources/codegen-templates/cursor.ftl
+++ b/src/main/resources/codegen-templates/cursor.ftl
@@ -9,24 +9,24 @@ import com.tightdb.lib.*;
 /**
  * This class represents a TightDB cursor and was automatically generated.
  */
-public class ${entity} extends AbstractCursor<${entity}> {
+public class ${name} extends AbstractCursor<${name}> {
 
-<#foreach f in columns><#if f.isSubtable>    public final ${f.type}CursorColumn<${entity}, ${entity}View, ${entity}Query, ${f.subtype}, ${f.subtype}Table> ${f.name};
-<#else>    public final ${f.type}CursorColumn<${entity}, ${entity}View, ${entity}Query> ${f.name};
+<#foreach f in columns><#if f.isSubtable>    public final ${f.type}CursorColumn<${name}, ${viewName}, ${queryName}, ${f.subCursorName}, ${f.subTableName}> ${f.name};
+<#else>    public final ${f.type}CursorColumn<${cursorName}, ${viewName}, ${queryName}> ${f.name};
 </#if></#foreach>
-	public ${entity}(IRowsetBase rowset, long position) {
-		super(${entity}Table.TYPES, rowset, position);
+	public ${cursorName}(IRowsetBase rowset, long position) {
+		super(${tableName}.TYPES, rowset, position);
 
-<#foreach f in columns><#if f.isSubtable>        ${f.name} = new ${f.type}CursorColumn<${entity}, ${entity}View, ${entity}Query, ${f.subtype}, ${f.subtype}Table>(${entity}Table.TYPES, this, ${f.index}, "${f.name}", ${f.subtype}Table.class);
-<#else>        ${f.name} = new ${f.type}CursorColumn<${entity}, ${entity}View, ${entity}Query>(${entity}Table.TYPES, this, ${f.index}, "${f.name}");
+<#foreach f in columns><#if f.isSubtable>        ${f.name} = new ${f.type}CursorColumn<${cursorName}, ${viewName}, ${queryName}, ${f.subCursorName}, ${f.subTableName}>(${tableName}.TYPES, this, ${f.index}, "${f.name}", ${f.subTableName}.class);
+<#else>        ${f.name} = new ${f.type}CursorColumn<${cursorName}, ${viewName}, ${queryName}>(${tableName}.TYPES, this, ${f.index}, "${f.name}");
 </#if></#foreach>	}
 
-<#foreach f in columns><#if f.isSubtable>	public ${f.subtype}Table get${f.name?cap_first}() {
+<#foreach f in columns><#if f.isSubtable>	public ${f.subTableName} get${f.name?cap_first}() {
 <#else>	public ${f.fieldType} get${f.name?cap_first}() {
 </#if>		return this.${f.name}.get();
 	}
 
-<#if f.isSubtable>	public void set${f.name?cap_first}(${f.subtype}Table ${f.name}) {
+<#if f.isSubtable>	public void set${f.name?cap_first}(${f.subTableName} ${f.name}) {
 <#else>	public void set${f.name?cap_first}(${f.fieldType} ${f.name}) {
 </#if>		this.${f.name}.set(${f.name});
 	}
diff --git a/src/main/resources/codegen-templates/query.ftl b/src/main/resources/codegen-templates/query.ftl
index f1640f2bdd..e9fee62ec1 100644
--- a/src/main/resources/codegen-templates/query.ftl
+++ b/src/main/resources/codegen-templates/query.ftl
@@ -9,15 +9,15 @@ import com.tightdb.lib.*;
 /**
  * This class represents a TightDB query and was automatically generated.
  */
-public class ${entity}Query extends AbstractQuery<${entity}Query, ${entity}, ${entity}View> {
+public class ${name} extends AbstractQuery<${name}, ${cursorName}, ${viewName}> {
 
-<#foreach f in columns><#if f.isSubtable>    public final ${f.type}QueryColumn<${entity}, ${entity}View, ${entity}Query, ${f.subtype}Table> ${f.name};
-<#else>    public final ${f.type}QueryColumn<${entity}, ${entity}View, ${entity}Query> ${f.name};
+<#foreach f in columns><#if f.isSubtable>    public final ${f.type}QueryColumn<${cursorName}, ${viewName}, ${name}, ${f.subTableName}> ${f.name};
+<#else>    public final ${f.type}QueryColumn<${cursorName}, ${viewName}, ${name}> ${f.name};
 </#if></#foreach>
-	public ${entity}Query(TableBase table, TableQuery query) {
-		super(${entity}Table.TYPES, table, query);
-<#foreach f in columns><#if f.isSubtable>        ${f.name} = new ${f.type}QueryColumn<${entity}, ${entity}View, ${entity}Query, ${f.subtype}Table>(${entity}Table.TYPES, table, query, ${f.index}, "${f.name}", ${f.subtype}Table.class);
-<#else>        ${f.name} = new ${f.type}QueryColumn<${entity}, ${entity}View, ${entity}Query>(${entity}Table.TYPES, table, query, ${f.index}, "${f.name}");
+	public ${name}(TableBase table, TableQuery query) {
+		super(${tableName}.TYPES, table, query);
+<#foreach f in columns><#if f.isSubtable>        ${f.name} = new ${f.type}QueryColumn<${cursorName}, ${viewName}, ${name}, ${f.subTableName}>(${tableName}.TYPES, table, query, ${f.index}, "${f.name}", ${f.subTableName}.class);
+<#else>        ${f.name} = new ${f.type}QueryColumn<${cursorName}, ${viewName}, ${name}>(${tableName}.TYPES, table, query, ${f.index}, "${f.name}");
 </#if></#foreach>	}
 
 }
diff --git a/src/main/resources/codegen-templates/table.ftl b/src/main/resources/codegen-templates/table.ftl
index ebd3a27238..69e413e1a7 100644
--- a/src/main/resources/codegen-templates/table.ftl
+++ b/src/main/resources/codegen-templates/table.ftl
@@ -9,27 +9,27 @@ import com.tightdb.lib.*;
 /**
  * This class represents a TightDB table and was automatically generated.
  */
-<#if isNested>public class ${entity}Table extends AbstractSubtable<${entity}, ${entity}View, ${entity}Query> {
-<#else>public class ${entity}Table extends AbstractTable<${entity}, ${entity}View, ${entity}Query> {
+<#if isNested>public class ${tableName} extends AbstractSubtable<${cursorName}, ${viewName}, ${queryName}> {
+<#else>public class ${tableName} extends AbstractTable<${cursorName}, ${viewName}, ${queryName}> {
 </#if>
-	public static final EntityTypes<${entity}Table, ${entity}View, ${entity}, ${entity}Query> TYPES = new EntityTypes<${entity}Table, ${entity}View, ${entity}, ${entity}Query>(${entity}Table.class, ${entity}View.class, ${entity}.class, ${entity}Query.class);
+	public static final EntityTypes<${tableName}, ${viewName}, ${cursorName}, ${queryName}> TYPES = new EntityTypes<${tableName}, ${viewName}, ${cursorName}, ${queryName}>(${tableName}.class, ${viewName}.class, ${cursorName}.class, ${queryName}.class);
 
-<#foreach f in columns><#if f.isSubtable>	public final ${f.type}RowsetColumn<${entity}, ${entity}View, ${entity}Query, ${f.subtype}Table> ${f.name} = new ${f.type}RowsetColumn<${entity}, ${entity}View, ${entity}Query, ${f.subtype}Table>(TYPES, table, ${f.index}, "${f.name}", ${f.subtype}Table.class);
-<#else>	public final ${f.type}RowsetColumn<${entity}, ${entity}View, ${entity}Query> ${f.name} = new ${f.type}RowsetColumn<${entity}, ${entity}View, ${entity}Query>(TYPES, table, ${f.index}, "${f.name}");
+<#foreach f in columns><#if f.isSubtable>	public final ${f.type}RowsetColumn<${cursorName}, ${viewName}, ${queryName}, ${f.subTableName}> ${f.name} = new ${f.type}RowsetColumn<${cursorName}, ${viewName}, ${queryName}, ${f.subTableName}>(TYPES, table, ${f.index}, "${f.name}", ${f.subTableName}.class);
+<#else>	public final ${f.type}RowsetColumn<${cursorName}, ${viewName}, ${queryName}> ${f.name} = new ${f.type}RowsetColumn<${cursorName}, ${viewName}, ${queryName}>(TYPES, table, ${f.index}, "${f.name}");
 </#if></#foreach>
-<#if isNested>	public ${entity}Table(TableBase subtableBase) {
+<#if isNested>	public ${tableName}(TableBase subtableBase) {
 		super(TYPES, subtableBase);
 	}
-<#else>	public ${entity}Table() {
+<#else>	public ${tableName}() {
 		super(TYPES);
 	}
 
-	public ${entity}Table(Group group) {
+	public ${tableName}(Group group) {
 		super(TYPES, group);
 	}
 </#if>
 	public static void specifyStructure(TableSpec spec) {
-<#foreach f in columns><#if f.isSubtable>        add${f.type}Column(spec, "${f.name}", new ${f.subtype}Table(null));
+<#foreach f in columns><#if f.isSubtable>        add${f.type}Column(spec, "${f.name}", new ${f.subTableName}(null));
 <#else>        add${f.type}Column(spec, "${f.name}");
 </#if></#foreach>    }
 
diff --git a/src/main/resources/codegen-templates/table_add.ftl b/src/main/resources/codegen-templates/table_add.ftl
index 380b901500..664802d558 100644
--- a/src/main/resources/codegen-templates/table_add.ftl
+++ b/src/main/resources/codegen-templates/table_add.ftl
@@ -1,4 +1,4 @@
-    public ${entity} add(<#foreach f in columns><#if !f.isSubtable><#if (f_index > 0)>, </#if>${f.originalType} ${f.name}</#if></#foreach>) {
+    public ${cursorName} add(<#foreach f in columns><#if !f.isSubtable><#if (f_index > 0)>, </#if>${f.originalType} ${f.name}</#if></#foreach>) {
         try {
             long position = size();
 
diff --git a/src/main/resources/codegen-templates/table_insert.ftl b/src/main/resources/codegen-templates/table_insert.ftl
index 5f9aba4ede..a48d68a286 100644
--- a/src/main/resources/codegen-templates/table_insert.ftl
+++ b/src/main/resources/codegen-templates/table_insert.ftl
@@ -1,4 +1,4 @@
-    public ${entity} insert(long position, <#foreach f in columns><#if !f.isSubtable><#if (f_index > 0)>, </#if>${f.originalType} ${f.name}</#if></#foreach>) {
+    public ${cursorName} insert(long position, <#foreach f in columns><#if !f.isSubtable><#if (f_index > 0)>, </#if>${f.originalType} ${f.name}</#if></#foreach>) {
         try {
         <#foreach f in columns><#if f.isSubtable>	insert${f.type}(${f.index}, position);
         <#else>	insert${f.type}(${f.index}, position, ${f.name});
diff --git a/src/main/resources/codegen-templates/view.ftl b/src/main/resources/codegen-templates/view.ftl
index e03fa22428..4cfa92381d 100644
--- a/src/main/resources/codegen-templates/view.ftl
+++ b/src/main/resources/codegen-templates/view.ftl
@@ -9,13 +9,13 @@ import com.tightdb.lib.*;
 /**
  * This class represents a TightDB view and was automatically generated.
  */
-public class ${entity}View extends AbstractView<${entity}, ${entity}View, ${entity}Query> {
+public class ${viewName} extends AbstractView<${cursorName}, ${viewName}, ${queryName}> {
 
-<#foreach f in columns><#if f.isSubtable>	public final ${f.type}RowsetColumn<${entity}, ${entity}View, ${entity}Query, ${f.subtype}Table> ${f.name} = new ${f.type}RowsetColumn<${entity}, ${entity}View, ${entity}Query, ${f.subtype}Table>(${entity}Table.TYPES, rowset, ${f.index}, "${f.name}", ${f.subtype}Table.class);
-<#else>	public final ${f.type}RowsetColumn<${entity}, ${entity}View, ${entity}Query> ${f.name} = new ${f.type}RowsetColumn<${entity}, ${entity}View, ${entity}Query>(${entity}Table.TYPES, rowset, ${f.index}, "${f.name}");
+<#foreach f in columns><#if f.isSubtable>	public final ${f.type}RowsetColumn<${cursorName}, ${viewName}, ${queryName}, ${f.subTableName}> ${f.name} = new ${f.type}RowsetColumn<${cursorName}, ${viewName}, ${queryName}, ${f.subTableName}>(${tableName}.TYPES, rowset, ${f.index}, "${f.name}", ${f.subTableName}.class);
+<#else>	public final ${f.type}RowsetColumn<${cursorName}, ${viewName}, ${queryName}> ${f.name} = new ${f.type}RowsetColumn<${cursorName}, ${viewName}, ${queryName}>(${tableName}.TYPES, rowset, ${f.index}, "${f.name}");
 </#if></#foreach>
-	public ${entity}View(TableViewBase viewBase) {
-		super(${entity}Table.TYPES, viewBase);
+	public ${viewName}(TableViewBase viewBase) {
+		super(${tableName}.TYPES, viewBase);
 	}
 
 }