Building a Javascript object tree from a Java ResourceBundle using State Pattern

As discussed in my Internationalisation in Ext-JS using Mix-ins post, I provide internationalisation for Ext-JS classes using their mix-in system which allows me to load a generated Javascript file. That Javascript file is generated from a Java ResourceBundle.

The Javascript file is served by a servlet:

private void makeMixin(String extClass, HttpServletResponse response, ResourceBundle resources)
        throws IOException
{
	response.setContentType("text/javascript");
	Writer out = response.getWriter();
	out.write("Ext.define('");
	out.write(extClass);
	out.write("',{");

	Node root = new Node("text");
	Enumeration<String> keyEnum = resources.getKeys();
	while(keyEnum.hasMoreElements())
	{
		String key = keyEnum.nextElement();
		String value = resources.getString(key);
		root.addProperty(key, value);
	}
	root.toJavascriptObject(out);
	out.write("});");
}

Node.java uses State Pattern to build up a tree. I have used an Enum to encode the states, so they contain only behaviour and not the different values that each state can hold. There are arguments for making the states out of classes and having each state hold the differing information it needs. There are also arguments for doing it the simple way and using if statements in the methods.

All state that the Node class can hold is provided within the class, though only one of the three value holding variables is relevant at a given time.

private final String m_name;
private Map<String, Node> m_children;
private List<String> m_valueArray;
private String m_value;
private NodeState m_state = NodeState.EMPTY;

The properties are handled by the recursive addProperty method:

/**
 * Add this property into the tree of nodes. Split it on the '.' character so that in Javascript
 * the final property is referenced the same way.
 *
 * @param key property key with levels separated by '.', or empty to set a value here.
 * @param value value to enter.
 */
public void addProperty(String key, String value)
{
	if(key == null || key.length() == 0)
	{
		addValue(value);
		return;
	}

	String childKey = key;
	String restKey = null;

	int dotPos = key.indexOf('.');
	if(dotPos > 0 && dotPos < key.length() - 1)
	{
		childKey = key.substring(0, dotPos);
		restKey = key.substring(dotPos + 1);
	}

	Node child = getOrCreateChild(childKey);
	child.addProperty(restKey, value);
}

/**
 * Get an existing child, or create a new child with the name and add it.
 * @param childKey
 * @return
 */
private Node getOrCreateChild(String childKey)
{
	Node child = null;
	if(m_children != null)
	{
		child = m_children.get(childKey);
	}
	if(child == null)
	{
		child = new Node(childKey);
		addChild(child);
	}
	return child;
}

The State Pattern comes into effect with methods like addValue()

/**
 * Add a Value to the node.
 * This results in a text node. If more values are added then it becomes an array node.
 * @param value the value to add.
 */
private void addValue(String value)
{
	m_state = m_state.addValue(this, value);
}

The State Pattern is a static inner class, so has access to the private variables.

/**
 * State Pattern for the Node class to handle change in behaviour depending on
 * the Node's type.
 */
enum NodeState
{
	/**
	 * An Empty node does not serialise at all.
	 * An Empty node can become a Text node by adding a value, or a Compound node by adding a child.
	 */
	EMPTY
	{
		@Override
		NodeState addChild(Node node, Node child)
		{
			node.m_children = new HashMap<String, Node>();
			node.m_children.put(child.getName(), child);
			return NodeState.COMPOUND;
		}

		@Override
		NodeState addValue(Node node, String value)
		{
			node.m_value = value;
			return NodeState.TEXT;
		}

		@Override
		void toJavascriptObject(Writer out, Node node) throws IOException
		{
			// Empty node has nothing.
		}
	},

// ....

	/** Add a child to the node. */
	abstract NodeState addChild(Node node, Node child);

	/** Add a value to the node. */
	abstract NodeState addValue(Node node, String value);

	/** Output the node as a javascript object. */
	abstract void toJavascriptObject(Writer out, Node node) throws IOException;
}

1 thought on “Building a Javascript object tree from a Java ResourceBundle using State Pattern

  1. My “boolean” values often comes from grlias form postings (checkboxes) and the values can be null. “True” values (checked) come back as “on” which, is a bit odd. So my boolean conversion isdef obtainBoolean(value) { if (value == null) return false final String b = “$value”.trim().toLowerCase() return b == “t” || b == “true” || b == “y” || b == “yes” || b == “on” || b == “1”}

Leave a Reply

Your email address will not be published. Required fields are marked *

This site uses Akismet to reduce spam. Learn how your comment data is processed.