package jsx3.xml;
import java.beans.BeanInfo;
import java.beans.Introspector;
import java.beans.PropertyDescriptor;
import java.lang.reflect.Method;
import java.util.HashMap;
import java.util.Iterator;
import java.util.List;
import java.util.Map;
/**
* The Record class somewhat mirrors a W3C Element, but simplified because we do
* not need namespaces, and things like Attr and NodeList.
* It is also vaguely similar to the {@link List} interface in that it is a
* container for attributes, however being also a container for other Records
* the interface has some name changes and is greatly simplified.
* @author Joe Walker [joe at getahead dot ltd dot uk]
*/
public final class Record extends Node
{
/**
* Ensure all Records have unique IDs
* @param id the jsxid for this record
*/
public Record(String id)
{
super(id);
}
public Record(Object data)
{
introspectBean(data);
}
public Record(String id, Object data)
{
introspectBean(data);
setId(id);
}
/**
* @param data
*/
private void introspectBean(Object data)
{
try
{
BeanInfo info = Introspector.getBeanInfo(data.getClass());
PropertyDescriptor[] descriptors = info.getPropertyDescriptors();
for (PropertyDescriptor descriptor : descriptors)
{
String name = descriptor.getName();
// We don't marshall getClass()
if ("class".equals(name))
{
continue;
}
Method getter = descriptor.getReadMethod();
if (getter != null)
{
Object reply = getter.invoke(data);
if (reply != null)
{
setAttribute(name, reply.toString());
if ("id".equals(name))
{
setId(reply.toString());
}
}
}
}
}
catch (Exception ex)
{
throw new IllegalStateException(ex);
}
}
// Generic attribute management ////////////////////////////////////////////
/**
* Retrieves an attribute value by name.
* @param name The name of the attribute to retrieve.
* @return The attribute value as a string, or <code>null</code> if the
* named attribute does not exist
*/
public String getAttribute(String name)
{
checkForJsxAttribute(name);
return attributes.get(name);
}
/**
* Adds a new attribute. If an attribute with that name is already present
* in the element, its value is changed to be that of the value
* parameter.
* @param name The name of the attribute to create or alter.
* @param value Value to set in string form.
*/
public Record setAttribute(String name, String value)
{
checkForJsxAttribute(name);
attributes.put(name, value);
return this;
}
/**
* Removes an attribute by name.
* <br>If no attribute with this name is found, this method has no effect.
* @param name The name of the attribute to remove.
* @return The old attribute value or null if it did not exist.
*/
public String removeAttribute(String name)
{
checkForJsxAttribute(name);
return attributes.remove(name);
}
/**
* Iterate over the names of the attributes (excluding the special JSX
* attributes) contained in this {@link Record}.
* @return an attribute name {@link Iterator}
*/
public Iterator<String> getAttributeNames()
{
return attributes.keySet().iterator();
}
/**
* An internal check to keep JSX attributes separate from normal attributes
* @param name The name of the attribute to check that it does not start
* with '<code>jsx</code>'
*/
private void checkForJsxAttribute(String name)
{
if (name.startsWith("jsx"))
{
throw new IllegalArgumentException("Special JSX Attribute keys should be set directly");
}
}
private Map<String, String> attributes = new HashMap<String, String>();
// Special JSX attribute management ////////////////////////////////////////
/**
* @return the disabled
*/
public Boolean getDisabled()
{
return disabled;
}
/**
* @param disabled the disabled to set
*/
public Record setDisabled(Boolean disabled)
{
this.disabled = disabled;
return this;
}
/**
* @return the divider
*/
public Boolean getDivider()
{
return divider;
}
/**
* @param divider the divider to set
*/
public Record setDivider(Boolean divider)
{
this.divider = divider;
return this;
}
/**
* @return the execute
*/
public String getExecute()
{
return execute;
}
/**
* @param execute the execute to set
*/
public Record setExecute(String execute)
{
this.execute = execute;
return this;
}
/**
* @return the groupName
*/
public String getGroupName()
{
return groupName;
}
/**
* @param groupName the groupName to set
*/
public Record setGroupName(String groupName)
{
this.groupName = groupName;
return this;
}
/**
* @return the image
*/
public String getImage()
{
return image;
}
/**
* @param image the image to set
*/
public Record setImage(String image)
{
this.image = image;
return this;
}
/**
* @return the keycodeString
*/
public String getKeycodeString()
{
return keycodeString;
}
/**
* @param keycodeString the keycodeString to set
*/
public Record setKeycodeString(String keycodeString)
{
this.keycodeString = keycodeString;
return this;
}
/**
* @return the noMask
*/
public String getNoMask()
{
return noMask;
}
/**
* @param noMask the noMask to set
*/
public Record setNoMask(String noMask)
{
this.noMask = noMask;
return this;
}
/**
* @return the selected
*/
public Boolean getSelected()
{
return selected;
}
/**
* @param selected the selected to set
*/
public Record setSelected(Boolean selected)
{
this.selected = selected;
return this;
}
/**
* @return the style
*/
public String getStyle()
{
return style;
}
/**
* @param style the style to set
*/
public Record setStyle(String style)
{
this.style = style;
return this;
}
/**
* @return the text
*/
public String getText()
{
return text;
}
/**
* @param text the text to set
*/
public Record setText(String text)
{
this.text = text;
return this;
}
/**
* @return the tip
*/
public String getTip()
{
return tip;
}
/**
* @param tip the tip to set
*/
public Record setTip(String tip)
{
this.tip = tip;
return this;
}
/**
* @return the unselectable
*/
public Boolean getUnselectable()
{
return unselectable;
}
/**
* @param unselectable the unselectable to set
*/
public Record setUnselectable(Boolean unselectable)
{
this.unselectable = unselectable;
return this;
}
// Support methods /////////////////////////////////////////////////////////
/**
* @param depth
* @return The string version of this record
*/
protected String toXml(int depth)
{
// Serialize the child records
StringBuilder buffer = new StringBuilder();
for (Record record : this)
{
buffer.append(Node.indent(depth));
buffer.append(record.toXml(depth + 1));
buffer.append("\n");
}
// Start the record tag
StringBuilder reply = new StringBuilder();
reply.append("<record jsxid=\"" + getId() + "\"");
// Add the JSX attributes
createAttributeOutput(reply, "jsxdisabled", disabled);
createAttributeOutput(reply, "jsxdivider", divider);
createAttributeOutput(reply, "jsxexecute", execute);
createAttributeOutput(reply, "jsxgroupname", groupName);
createAttributeOutput(reply, "jsximage", image);
createAttributeOutput(reply, "jsxkeycode", keycodeString);
createAttributeOutput(reply, "jsxnomask", noMask);
createAttributeOutput(reply, "jsxselected", selected);
createAttributeOutput(reply, "jsxstyle", style);
createAttributeOutput(reply, "jsxtext", text);
createAttributeOutput(reply, "jsxtip", tip);
createAttributeOutput(reply, "jsxunselectable", unselectable);
// Add the custom attributes
for (Iterator<Map.Entry<String, String>> it = attributes.entrySet().iterator(); it.hasNext();)
{
Map.Entry<String, String> entry = it.next();
createAttributeOutput(reply, entry.getKey(), entry.getValue());
}
if (buffer.length() == 0)
{
reply.append("/>\n");
}
else
{
reply.append(">\n");
reply.append(buffer.toString());
reply.append("</record>");
}
return reply.toString();
}
/**
* @param reply
*/
private void createAttributeOutput(StringBuilder reply, String name, Object value)
{
if (value != null)
{
reply.append(" ");
reply.append(name);
reply.append("=\"");
reply.append(value);
reply.append("\"");
}
}
/* (non-Javadoc)
* @see java.lang.Object#toString()
*/
@Override
public String toString()
{
return toXml(0);
}
private Boolean disabled = null;
private Boolean divider = null;
private String execute = null;
private String groupName = null;
private String image = null;
private String keycodeString = null;
private String noMask = null;
private Boolean selected = null;
private String style = null;
private String text = null;
private String tip = null;
private Boolean unselectable = null;
}