package org.unix4j.codegen.def;

import java.lang.reflect.Field;
import java.lang.reflect.Modifier;
import java.util.LinkedHashMap;
import java.util.Map;

import freemarker.template.ObjectWrapper;
import freemarker.template.TemplateHashModel;
import freemarker.template.TemplateModel;
import freemarker.template.TemplateModelException;

/**
 * Abstract base class for all element definitions. Public fields define visible
 * properties or nested elements.
 */
abstract public class AbstractElementDef implements TemplateHashModel {

	private final Map<String, Field> fields = new LinkedHashMap<String, Field>();

	public AbstractElementDef() {
		for (final Field field : getClass().getFields()) {
			if (0 == (field.getModifiers() & Modifier.STATIC)) {
				fields.put(field.getName(), field);
			}
		}
	}

	@Override
	public boolean isEmpty() throws TemplateModelException {
		return fields.isEmpty();
	}

	@Override
	public TemplateModel get(String key) throws TemplateModelException {
		final Object value = getFieldValue(key);
		if (value instanceof TemplateModel) {
			return (TemplateModel) value;
		}
		return value == null ? null : ObjectWrapper.DEFAULT_WRAPPER.wrap(value);
	}

	public Object getFieldValue(String name) {
		final Field field = fields.get(name);
		try {
			return field == null ? null : field.get(this);
		} catch (Exception e) {
			return "ERROR: cannot read field " + field.getName() + "e=" + e;
		}
	}

	@Override
	public String toString() {
		return toMap().toString();
	}

	public Map<String, Object> toMap() {
		final Map<String, Object> map = new LinkedHashMap<String, Object>();
		for (final String name : fields.keySet()) {
			map.put(name, getFieldValue(name));
		}
		return map;
	}

	public String toString(String indent) {
		return toMultiLineString(indent, toMap());
	}

	protected static String toMultiLineString(String indent, Map<String, ?> map) {
		final StringBuilder sb = new StringBuilder();
		int maxKeyLen = 0;
		for (final String key : map.keySet()) {
			maxKeyLen = Math.max(maxKeyLen, String.valueOf(key).length());
		}
		for (final Map.Entry<String, ?> e : map.entrySet()) {
			sb.append(indent).append(e.getKey()).append(": ");
			final int keyLen = String.valueOf(e.getKey()).length();
			for (int i = keyLen; i < maxKeyLen; i++) {
				sb.append(' ');
			}
			sb.append(e.getValue()).append('\n');
		}
		return sb.toString();
	}
}

@@ -535,16 +535,217 @@
 private 
+Map%3CString, Field%3E fields;%0A%0A%09private final Map%3CString, Field%3E getFields() %7B%0A%09%09if (fields == null) %7B%0A%09%09%09fields = initFields();%0A%09%09%7D%0A%09%09return fields;%0A%09%7D%0A%09private final Map%3CString, Field%3E initFields() %7B%0A%09%09
 final Ma
@@ -809,40 +809,8 @@
 %3E();
-%0A%0A%09public AbstractElementDef() %7B
 %0A%09%09f
@@ -960,24 +960,41 @@
 );%0A%09%09%09%7D%0A%09%09%7D%0A
+%09%09return fields;%0A
 %09%7D%0A%0A%09@Overri
@@ -1063,22 +1063,27 @@
 %09return 
-f
+getF
 ields
+()
 .isEmpty
@@ -1436,22 +1436,27 @@
 field = 
-f
+getF
 ields
+()
 .get(nam
@@ -1827,22 +1827,27 @@
  name : 
-f
+getF
 ields
+()
 .keySet(

fix for deadlock during codegen