package com.elsea.stone.property; import java.util.Stack; import javax.xml.parsers.DocumentBuilder; import javax.xml.parsers.DocumentBuilderFactory; import javax.xml.parsers.ParserConfigurationException; import javax.xml.transform.OutputKeys; import javax.xml.transform.Transformer; import javax.xml.transform.TransformerFactory; import javax.xml.transform.dom.DOMSource; import javax.xml.transform.stream.StreamResult; import org.w3c.dom.Document; import com.elsealabs.xshot.file.FileSystem; import com.elsealabs.xshot.file.FileSystem.PATH; import com.elsealabs.xshot.program.Program; /** * PropertyPoolTemplate * * A Property Pool is a mutable collection of Property Elements with methods to * assist in Property Pool creation and alteration. * * @author Connor M. Elsea */ public class PropertyPool { private PropertyGroup parent; private Stack<PropertyGroup> history; private PropertyPoolSearch search; private PropertyElement recent; /** * Creates a new Property Pool object and performs the initial set up. * * The initial set up consists of instantiating the history stack, creating * a new Property Group object to be used as the parent group, and then * pushing this new group to the top of the currently empty history stack. * * This ensures that any added base-level Property Groups will not be met * with an empty history stack and no parent to attach themselves to. */ public PropertyPool() { history = new Stack<PropertyGroup>(); parent = new PropertyGroup(); parent.setName("parent"); history.push(parent); } /** * Create a new Property Group as a child of the most recent group. If no * groups have been created or all user-created groups have been ended,the * new group will be added as a child of the parent group. * * In order to accomplish this a new Property Group object is created and * named and is then added as a child of the Property Group located on the * top of the history stack. This new group is then pushed to the top of * the history stack. * * Every group should be ended with a call to the end method. * * p.group("settings") * .property("name", "Adam Smith"); * .end(); * * @param name The name of the group to be created * @return The current PropertyPool instance to allow chaining */ public PropertyPool group(String name) { _addGroup(name, parent); return this; } /** * Adds the new group as a child of the current group, which is the group * located on the top of the history stack, and then pushes this new group * to the top of the history stack, thus making it the new current group. * * @param group The group to be added * @param to The parent group the new group will be added to */ private void _addGroup(PropertyGroup group, PropertyGroup to) { group.setParent(history.peek()); history.peek().addChild(group); history.push(group); } /** * Creates a new Property Group object, sets its name, and fires the add * group method that takes a group object as a first argument. This method * makes other methods easier to read and understand. * * @param ofName The name of the group to be created * @param to The parent group the new group will be added to */ private void _addGroup(String ofName, PropertyGroup to) { PropertyGroup group = new PropertyGroup(); group.setName(ofName); recent = group; _addGroup(group, to); } /** * Shifts the current Property Group from the current group to the previous * group. This should be called at the end of each group's child definitions. * * p.group("settings") * .property("name", "Adam Smith"); * .end(); * * In order to accomplish this, the most recent Property Group, which is the * group on the top of the history stack, is removed. This means that the * next time property or group methods are called, they will mutate the group * prior to the one being ended, thus achieving desired functionality. * * @return The current PropertyPool instance to allow chaining */ public PropertyPool end() { if (history.peek().getParent() == null) { System.err.println("Error: Cannot end the parent element"); } else { history.pop(); } return this; } /** * Create a new Property as a child of the most recent group. If no properties * have been created or all user-created groups have been ended, the new * property will be added as a child of the parent group. * * In order to accomplish this, a new Property object is created, set up, and is * then added as a child to the Property Group at the top of the history stack. * * Usage is as follows. * * p.group("settings") * .property("name", "Adam Smith"); * .end(); * * @param name The name of the group to be created * @return The current PropertyPool instance to allow chaining */ public PropertyPool property(String name, String value) { property(name, value, value); return this; } /** * Same as the property method with two arguments except this allows you to set the * default and current values as different values. * * @param name * @param defaultValue * @param currentValue * @return */ public PropertyPool property(String name, String defaultValue, String currentValue) { Property property = new Property(); property.setName(name); property.setDefaultValue(defaultValue); property.setCurrentValue(currentValue); recent = property; history.peek().addChild(property); return this; } public PropertyPool type(String typeName) { recent.setType(typeName); return this; } /** * Creates a new XML document from the Property Poo's structure and content. * * @return An XML document representing the Property Pool */ public Document toXMLDocument() { try { DocumentBuilderFactory factory = DocumentBuilderFactory.newInstance(); DocumentBuilder builder = factory.newDocumentBuilder(); Document doc = builder.newDocument(); /* Get the base parent group */ PropertyGroup parentGroup = getParent(); /* Create XML element for the parent group and append it to the XML document */ //Element parent = doc.createElement("DOC_DIRECT_CHILD"); //doc.appendChild(parent); /* Start chain reaction of writing functions */ parentGroup.write(doc, null); return doc; } catch (ParserConfigurationException e) { e.printStackTrace(); return null; } } /** * Visualizes and prints the structure currently held by the Property Pool. */ public void show() { parent.print(0); } /** * Converts the Property Pool to XML and prints it to standard out */ public void showXML() { try { TransformerFactory transformerFactory = TransformerFactory.newInstance(); Transformer transformer = transformerFactory.newTransformer(); transformer.setOutputProperty(OutputKeys.INDENT, "yes"); transformer.setOutputProperty("{http://xml.apache.org/xslt}indent-amount", "2"); Document doc = toXMLDocument(); DOMSource source = new DOMSource(doc); StreamResult result = new StreamResult(System.out); transformer.transform(source, result); } catch (Exception ex) { ex.printStackTrace(); } } /** * Provides a stream like class for traversing and searching the contents * of the Property Pool. * * @return A Property Pool Search object */ public PropertyPoolSearch search() { if (search == null) search = new PropertyPoolSearch(this); System.out.println("Beginning search"); return search; } public void save() { PropertyPoolWriter writer = new PropertyPoolWriter(); if (writer.write(this, Program.getInstance().getFileSystem().getPath(PATH.STONE_FILE))) { System.out.println("Resolve: Wrote Stone file correctly"); } else { System.err.println("Fatal Error: Failed to write Stone file."); } } /** * Returns the parent object at the top of the property pool * * @return the parent object at the top of the property pool */ public PropertyGroup getParent() { return parent; } public PropertyElement getRecent() { return recent; } }