/*
* Jacareto Copyright (c) 2002-2005
* Applied Computer Science Research Group, Darmstadt University of
* Technology, Institute of Mathematics & Computer Science,
* Ludwigsburg University of Education, and Computer Based
* Learning Research Group, Aachen University. All rights reserved.
*
* Jacareto is free software; you can redistribute it and/or
* modify it under the terms of the GNU General Public
* License as published by the Free Software Foundation; either
* version 2 of the License, or (at your option) any later version.
*
* Jacareto is distributed in the hope that it will be useful,
* but WITHOUT ANY WARRANTY; without even the implied warranty of
* MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU
* General Public License for more details.
*
* You should have received a copy of the GNU General Public
* License along with Jacareto; if not, write to the Free
* Software Foundation, Inc., 675 Mass Ave, Cambridge, MA 02139, USA.
*
*/
package jacareto.system;
import jacareto.toolkit.EnhancedHashtable;
import jacareto.toolkit.io.FileUtilities;
import org.apache.regexp.RE;
import org.apache.regexp.RESyntaxException;
import org.jdom.Attribute;
import org.jdom.Document;
import org.jdom.Element;
import org.jdom.JDOMException;
import org.jdom.input.SAXBuilder;
import java.io.File;
import java.io.FileInputStream;
import java.io.IOException;
import java.io.InputStream;
import java.util.Enumeration;
import java.util.Hashtable;
import java.util.Iterator;
import java.util.Properties;
import java.util.StringTokenizer;
/**
* Enhanced hashtable which can read the customization out of a xml file and stores it in a
* hashtable. The xml file must have the following form:
* <pre>
* <customization>
* <elem name="MyElement0" value="Value0"/>
* <group name="MyGroup1">
* <elem name="MyElement1" value="Value1"/>
* <elem name="MyElement2" value="Value2"/>
* </group>
* <group name="MyGroup2">
* <group name="MyGroup3">
* <elem name="MyElement3" value="Value3"/>
* <elem name="MyElement4" value="Value4"/>
* </group>
* <elem name="MyElement5" value="Value5"/>
* </group>
* </customization>
* </pre>
* This example code will add the following (key,value) pairs to the customization (as strings):
* <pre>
* ("MyElement0", "Value0")
* ("MyGroup1.MyElement1", "Value1")
* ("MyGroup1.MyElement2", "Value2")
* ("MyGroup2.MyGroup3.MyElement3", "Value3")
* ("MyGroup2.MyGroup3.MyElement4", "Value4")
* ("MyGroup2.MyElement5", "Value5")
* </pre>
* Another valid xml element is a map. A map consists of (key,value) pairs. If the xml file
* contains a map, then an instance of type {@link jacareto.toolkit.EnhancedHashtable} will be
* added to the customization containing the specified (key,value) pairs. Example xml file:
* <pre>
* <customization>
* <group name="MyGroup1">
* <elem name="MyElement1" value="Value1"/>
* <map name="Map1">
* <mapelem key="k1" value="v1"/>
* <mapelem key="k2" value="v2"/>
* <mapelem key="k3" value="v3"/>
* </map>
* </group>
* </customization>
* </pre>
*
* <p>
* This xml file will produce two customization elements: a normal element ("MyGroup1.MyElement1",
* "Value1") and a hashtable containing the three (key,value) pairs (k1,v1), (k2,v2) and (k3,v3).
* The hashtable will be added to the customization belonging to the key "MyGroup1.Map1".
* </p>
*
* <p>
* No text of a "name" attribute may contain a ".", because this character is used to keep the
* hierarchy. For "key" attributes of map elements it is allowed to contain dots.
* </p>
*
* @author <a href="mailto:cspannagel@web.de">Christian Spannagel</a>
* @version 1.02
*/
public class Customization extends EnhancedHashtable {
/** The attributes of the root element. */
private Hashtable rootAttributes;
/**
* Creates an empty customization object.
*/
public Customization () {
super();
rootAttributes = new Hashtable();
}
/**
* Creates a customization object and reads the data out of the specified filename.
*
* @param filename the filename of the xml file
*
* @throws IOException if the given file cannot be parsed
*/
public Customization (String filename) throws IOException {
this();
read (filename);
}
/**
* Creates a customization object and and reads all elements from the given properties
* instance.
*
* @param properties the properties
*/
public Customization (Properties properties) {
this();
putAll (properties);
}
/**
* Creates a customization object and reads the data from the specified input stream.
*
* @param inputStream the input stream
*
* @throws IOException if the given input stream cannot be parsed
*/
public Customization (InputStream inputStream) throws IOException {
this();
read (inputStream);
}
/**
* Reads the customization from a xml file or a properties file and stores it to the hashtable.
* The hashtable will not be cleared before (if you want to add the xml file customization to
* an empty hashtable, clear the hashtable first).
*
* @param filename the filename of the xml file
*
* @throws IOException if the given file cannot be parsed
*/
public void read (String filename) throws IOException {
try {
if (FileUtilities.getExtension (filename).equals ("xml")) {
RE re = new RE("(/|\\\\)");
filename = re.subst (filename, File.separator);
SAXBuilder builder = new SAXBuilder();
Document xmlEventRecordDocument = builder.build (new File(filename));
Element rootElement = xmlEventRecordDocument.getRootElement ();
addXMLTree (rootElement, "");
} else {
Properties properties = new Properties();
properties.load (new FileInputStream(filename));
putAll (properties);
}
} catch (JDOMException ex) {
throw new IOException("Customization: Could not parse the file " + filename + ".\n" +
ex.getMessage ());
} catch (IOException ex) {
throw new IOException("Customization: Could not parse the file " + filename + ".\n" +
ex.getMessage ());
} catch (RESyntaxException ignored) {
;
}
}
/**
* Reads the customization from an input stream and stores it to the hashtable. The hashtable
* will not be cleared before (if you want to add the xml file customization to an empty
* hashtable, clear the hashtable first).
*
* @param inputStream the filename of the xml file
*
* @throws IOException if the given input stream cannot be parsed
*/
public void read (InputStream inputStream) throws IOException {
try {
SAXBuilder builder = new SAXBuilder();
Document xmlEventRecordDocument = builder.build (inputStream);
Element rootElement = xmlEventRecordDocument.getRootElement ();
addXMLTree (rootElement, "");
} catch (JDOMException ex) {
throw new IOException("Customization: Could not parse a given input stream.");
}
}
/**
* Writes the customization to a xml file with the given filename.
*
* @param filename the filename of the target file
*
* @throws IOException if the customization cannot be written
*/
public void write (String filename) throws IOException {
write (filename, "customization");
}
/**
* Adds a (key, value) pair to the XML tree given by the root element.
*
* @param root the root element
* @param key the key
* @param value the value belonging to the key
*/
protected void writeXMLElement (Element root, String key, Object value) {
Element element = root;
StringTokenizer st = new StringTokenizer(key, ".", false);
while (st.hasMoreTokens ()) {
String elemName = st.nextToken ();
Element child = null;
if (st.hasMoreTokens ()) {
Iterator it = element.getChildren ().iterator ();
while ((child == null) && it.hasNext ()) {
Element groupChild = (Element) it.next ();
if (groupChild.getName ().equals ("group") &&
groupChild.getAttributeValue ("name", "").equals (elemName)) {
child = groupChild;
}
}
if (child == null) {
child = new Element("group");
child.setAttribute ("name", elemName);
element.addContent (child);
}
element = child;
} else {
if (! (value instanceof EnhancedHashtable)) {
child = new Element("elem");
child.setAttribute ("name", elemName);
child.setAttribute ("value", value.toString ());
} else {
child = new Element("map");
child.setAttribute ("name", elemName);
EnhancedHashtable valueTable = (EnhancedHashtable) value;
Enumeration enumeration = valueTable.keys ();
while (enumeration.hasMoreElements ()) {
Object valueKey = enumeration.nextElement ();
Element mapChild = new Element("mapelem");
mapChild.setAttribute ("key", valueKey.toString ());
mapChild.setAttribute ("value", valueTable.get (valueKey).toString ());
child.addContent (mapChild);
}
}
element.addContent (child);
}
}
}
/**
* Adds the tree whose root is the specified element to the hashtable. The name of the element
* must be contained in the set {customization, group}.
*
* @param root the root element
* @param prefix the prefix for all keys read out of the file
*/
private void addXMLTree (Element root, String prefix) {
String rootName = root.getName ();
if (rootName.equals ("customization") || rootName.equals ("group") ||
rootName.equals ("module")) {
if (rootName.equals ("module")) {
rootAttributes.put ("name", root.getAttributeValue ("name"));
rootAttributes.put ("author", root.getAttributeValue ("author"));
rootAttributes.put ("version", root.getAttributeValue ("version"));
}
Iterator i = root.getChildren ().iterator ();
while (i.hasNext ()) {
Element element = (Element) i.next ();
Attribute nameAttribute = element.getAttribute ("name");
if (nameAttribute == null) {
System.err.println ("XMLFile error: name missing!");
System.exit (1);
}
String elementName = prefix + (prefix.equals ("") ? "" : ".") +
nameAttribute.getValue ();
if (element.getName ().equals ("group")) {
addXMLTree (element, elementName);
} else if (element.getName ().equals ("map")) {
EnhancedHashtable map = new EnhancedHashtable();
if (containsKey (elementName)) {
Object alreadyContained = get (elementName);
if (alreadyContained instanceof EnhancedHashtable) {
map = (EnhancedHashtable) alreadyContained;
}
}
Iterator itMap = element.getChildren ().iterator ();
while (itMap.hasNext ()) {
Element mapElement = (Element) itMap.next ();
if (mapElement.getName ().equals ("mapelem")) {
Attribute keyAttribute = mapElement.getAttribute ("key");
Attribute valueAttribute = mapElement.getAttribute ("value");
if ((keyAttribute == null) || (valueAttribute == null)) {
System.err.println ("XMLFile Error (Map): key or value missing!");
System.exit (1);
}
map.put (keyAttribute.getValue (), valueAttribute.getValue ());
}
}
put (elementName, map);
} else if (element.getName ().equals ("elem")) {
Attribute valueAttribute = element.getAttribute ("value");
if (valueAttribute == null) {
System.err.println ("XMLFile Error: value missing!");
System.exit (1);
}
put (elementName, valueAttribute.getValue ());
}
}
}
}
/**
* Returns the attributes of the customization. If the customization is read from a xml file,
* those attributes are the attributes of the root element.
*
* @return DOCUMENT ME!
*/
public Hashtable getAttributes () {
return rootAttributes;
}
}
|