is
- * returned.
- */
- public Properties setProperties(String pGroupKey, Properties pProperties) {
- XMLProperties old = new XMLProperties();
- String groupKey = pGroupKey;
-
- if (groupKey.charAt(groupKey.length()) != '.') {
- groupKey += ".";
- }
- Iterator iterator = pProperties.entrySet().iterator();
-
- while (iterator.hasNext()) {
- Map.Entry entry = (Map.Entry) iterator.next();
- String key = (String) entry.getKey();
- Object obj = setPropertyValue(groupKey + key, entry.getValue());
-
- // Return removed entries
- if (obj != null) {
- old.setPropertyValue(groupKey + key, entry.getValue());
- }
- }
- return ((old.size() > 0) ? old : null);
- }
-
- /**
- * For testing only.
- */
- public static void main(String[] pArgs) throws Exception {
- // -- Print DTD
- System.out.println("DTD: \n" + DTD);
- System.out.println("--");
-
- // -- Test load
- System.out.println("Reading properties from \"" + pArgs[0] + "\"...");
- XMLProperties props = new XMLProperties();
-
- props.load(new FileInputStream(new File(pArgs[0])));
- props.list(System.out);
- System.out.println("--");
-
- // -- Test recursion
- String key = "key";
- Object old = props.setProperty(key, "AAA");
- Properties p1 = new XMLProperties(new XMLProperties(props));
- Properties p2 = new Properties(new Properties(props));
-
- System.out.println("XMLProperties: " + p1.getProperty(key) + " ==" + " Properties: " + p2.getProperty(key));
- if (old == null) {
- props.remove("key");
- } else {
- props.put("key", old); // Put old value back, to avoid confusion...
- }
- System.out.println("--");
-
- // -- Test store
- //props.store(System.out, "XML Properties file written by XMLProperties.");
- File out = new File("copy_of_" + pArgs[0]);
-
- System.out.println("Writing properties to \"" + out.getName() + "\"");
- if (!out.exists()) {
- props.store(new FileOutputStream(out), "XML Properties file written by XMLProperties.");
- } else {
- System.err.println("File \"" + out.getName() + "\" allready exists, cannot write!");
- }
-
- // -- Test utility methods
- // Write normal properties from XMLProperties
- out = new File("copy_of_" + pArgs[0].substring(0, pArgs[0].lastIndexOf(".")) + ".properties");
- System.out.println("Writing properties to \"" + out.getName() + "\"");
- if (!out.exists()) {
- storeProperties(props, new FileOutputStream(out), "Properties file written by XMLProperties.");
- } else {
- System.err.println("File \"" + out.getName() + "\" allready exists, cannot write!");
- }
- System.out.println("--");
-
- // -- Test type attribute
- System.out.println("getPropertyValue(\"one\"): " + props.getPropertyValue("one") + " class: "
- + props.getPropertyValue("one").getClass());
- System.out.println("setPropertyValue(\"now\", " + new Date() + "): " + props.setPropertyValue("now", new Date()) + " class: "
- + props.getPropertyValue("now").getClass());
- System.out.println("getPropertyValue(\"date\"): " + props.getPropertyValue("date") + " class: "
- + props.getPropertyValue("date").getClass());
- System.out.println("getPropertyValue(\"time\"): " + props.getPropertyValue("time") + " class: "
- + props.getPropertyValue("time").getClass());
- }
-
- /**
- * ContentHandler, ErrorHandler and EntityResolver implementation for the
- * SAX Parser.
- */
- protected class PropertiesHandler extends DefaultHandler {
- protected Stack mStack = null;
-
- /** Stores the characters read so far, from the characters callback */
- protected char[] mReadSoFar = null;
- protected boolean mIsValue = false;
- protected String mType = null;
- protected String mFormat = null;
- protected XMLProperties mProperties = null;
- protected Locator mLocator = null;
-
- /**
- * Creates a PropertiesHandler for the given XMLProperties.
- */
- PropertiesHandler(XMLProperties pProperties) {
- mProperties = pProperties;
- mStack = new Stack();
- }
-
- /**
- * setDocumentLocator implementation.
- */
- public void setDocumentLocator(Locator pLocator) {
-
- // System.out.println("Setting locator: " + pLocator);
- mLocator = pLocator;
- }
-
- /**
- * Calls XMLProperties.addXMLError with the given SAXParseException
- * as the argument.
- */
- public void error(SAXParseException pException) throws SAXParseException {
-
- //throw pException;
- mProperties.addXMLError(pException);
-
- /*
- System.err.println("error: " + pException.getMessage());
- System.err.println("line: " + mLocator.getLineNumber());
- System.err.println("column: " + mLocator.getColumnNumber());
- */
- }
-
- /**
- * Throws the given SAXParseException (and stops the parsing).
- */
- public void fatalError(SAXParseException pException) throws SAXParseException {
-
- throw pException;
-
- /*
- System.err.println("fatal error: " + pException.getMessage());
- System.err.println("line: " + mLocator.getLineNumber());
- System.err.println("column: " + mLocator.getColumnNumber());
- */
- }
-
- /**
- * Calls XMLProperties.addXMLWarning with the given SAXParseException
- * as the argument.
- */
- public void warning(SAXParseException pException) throws SAXParseException {
-
- // throw pException;
- mProperties.addXMLWarning(pException);
-
- /*
- System.err.println("warning: " + pException.getMessage());
- System.err.println("line: " + mLocator.getLineNumber());
- System.err.println("column: " + mLocator.getColumnNumber());
- */
- }
-
- /**
- * startElement implementation.
- */
- public void startElement(String pNamespaceURI, String pLocalName, String pQualifiedName, Attributes pAttributes) throws SAXException {
-
- /*
-
- String attributes = "";
- for (int i = 0; i < pAttributes.getLength(); i++) {
- attributes += pAttributes.getQName(i) + "=" + pAttributes.getValue(i) + (i < pAttributes.getLength() ? ", " : "");
- }
-
- System.out.println("startElement: " + pNamespaceURI
- + "." + pLocalName
- + " (" + pQualifiedName + ") "
- + attributes);
- */
- if (XMLProperties.PROPERTY.equals(pLocalName)) {
-
- // Get attibute values
- String name = pAttributes.getValue(XMLProperties.PROPERTY_NAME);
- String value = pAttributes.getValue(XMLProperties.PROPERTY_VALUE);
- String type = pAttributes.getValue(XMLProperties.PROPERTY_TYPE);
- String format = pAttributes.getValue(XMLProperties.PROPERTY_FORMAT);
-
- // Get the full name of the property
- if (!mStack.isEmpty()) {
- name = (String) mStack.peek() + "." + name;
- }
-
- // Set the property
- if (value != null) {
- mProperties.setProperty(name, value);
-
- // Store type & format
- if (!XMLProperties.DEFAULT_TYPE.equals(type)) {
- mType = type;
- mFormat = format; // Might be null (no format)
- }
- }
-
- // Push the last name on the stack
- mStack.push(name);
- } // /PROPERTY
- else if (XMLProperties.PROPERTY_VALUE.equals(pLocalName)) {
-
- // Get attibute values
- String name = (String) mStack.peek();
- String type = pAttributes.getValue(XMLProperties.PROPERTY_TYPE);
- String format = pAttributes.getValue(XMLProperties.PROPERTY_FORMAT);
-
- // Store type & format
- if (!XMLProperties.DEFAULT_TYPE.equals(type)) {
- mType = type;
- mFormat = format;
- }
- mIsValue = true;
- }
- }
-
- /**
- * endElement implementation.
- */
- public void endElement(String pNamespaceURI, String pLocalName, String pQualifiedName) throws SAXException {
-
- /*
- System.out.println("endElement: " + pNamespaceURI
- + "." + pLocalName + " (" + pQualifiedName + ")");
- */
- if (XMLProperties.PROPERTY.equals(pLocalName)) {
-
- // Just remove the last name
- String name = (String) mStack.pop();
-
- // Init typed values
- try {
- String prop = mProperties.getProperty(name);
-
- // Value may be null, if so just skip
- if (prop != null) {
- Object value = mProperties.initPropertyValue(prop, mType, mFormat);
-
- // Store format
- if ((mFormat != null) &&!XMLProperties.DEFAULT_DATE_FORMAT.equals(mFormat)) {
- mProperties.setPropertyFormat(name, mFormat);
- }
-
- //System.out.println("-->" + prop + "-->" + value);
- mProperties.setPropertyValue(name, value);
- }
-
- // Clear type & format
- mType = null;
- mFormat = null;
- } catch (Exception e) {
- e.printStackTrace(System.err);
- throw new SAXException(e);
- }
- } else if (XMLProperties.PROPERTY_VALUE.equals(pLocalName)) {
- if (mStack.isEmpty()) {
-
- // There can't be any characters here, really
- return;
- }
-
- // Get the full name of the property
- String name = (String) mStack.peek();
-
- // Set the property
- String value = new String(mReadSoFar);
-
- //System.err.println("characters: >" + value+ "<");
- if (!StringUtil.isEmpty(value)) {
-
- // If there is allready a value, both the value attribute
- // and element have been specified, this is an error
- if (mProperties.containsKey(name)) {
- throw new SAXParseException(
- "Value can only be specified either using the \"value\" attribute, OR the \"value\" element, not both.", mLocator);
- }
-
- // Finally, set the property
- mProperties.setProperty(name, value);
- }
-
- // Done value processing
- mIsValue = false;
- }
- }
-
- /**
- * characters implementation
- */
- public void characters(char[] pChars, int pStart, int pLength) throws SAXException {
- // TODO: Use StringBuilder instead?
- if (mIsValue) {
- // If nothing read so far
- if (mReadSoFar == null) {
- // Create new array and copy into
- mReadSoFar = new char[pLength];
- System.arraycopy(pChars, pStart, mReadSoFar, 0, pLength);
- }
- else {
- // Merge arrays
- mReadSoFar = (char[]) CollectionUtil.mergeArrays(mReadSoFar, 0, mReadSoFar.length, pChars, pStart, pLength);
- }
- }
- }
-
-
- /**
- * Intercepts the entity
- * "http://www.twelvemonkeys.com/xml/XMLProperties.dtd", and return
- * an InputSource based on the internal DTD of XMLProperties instead.
- *
- * @todo Maybe intercept a PUBLIC DTD and be able to have SYSTEM DTD
- * override?
- */
- public InputSource resolveEntity(String pPublicId, String pSystemId) {
- // If we are looking for the standard SYSTEM DTD, then
- // Return an InputSource based on the internal DTD.
- if (XMLProperties.SYSTEM_DTD_URI.equals(pSystemId)) {
- return new InputSource(new StringReader(XMLProperties.DTD));
- }
-
- // use the default behaviour
- return null;
- }
- }
-}
-
+/*
+ * Copyright (c) 2008, Harald Kuhr
+ * All rights reserved.
+ *
+ * Redistribution and use in source and binary forms, with or without
+ * modification, are permitted provided that the following conditions are met:
+ * * Redistributions of source code must retain the above copyright
+ * notice, this list of conditions and the following disclaimer.
+ * * Redistributions in binary form must reproduce the above copyright
+ * notice, this list of conditions and the following disclaimer in the
+ * documentation and/or other materials provided with the distribution.
+ * * Neither the name "TwelveMonkeys" nor the
+ * names of its contributors may be used to endorse or promote products
+ * derived from this software without specific prior written permission.
+ *
+ * THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS
+ * "AS IS" AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT
+ * LIMITED TO, THE IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR
+ * A PARTICULAR PURPOSE ARE DISCLAIMED. IN NO EVENT SHALL THE COPYRIGHT OWNER OR
+ * CONTRIBUTORS BE LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL,
+ * EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT LIMITED TO,
+ * PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; LOSS OF USE, DATA, OR
+ * PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND ON ANY THEORY OF
+ * LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT (INCLUDING
+ * NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE OF THIS
+ * SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE.
+ */
+
+package com.twelvemonkeys.util;
+
+import com.twelvemonkeys.lang.StringUtil;
+import com.twelvemonkeys.xml.XMLSerializer;
+import org.w3c.dom.*;
+import org.xml.sax.*;
+import org.xml.sax.helpers.DefaultHandler;
+import org.xml.sax.helpers.XMLReaderFactory;
+
+import javax.xml.parsers.DocumentBuilder;
+import javax.xml.parsers.DocumentBuilderFactory;
+import javax.xml.parsers.ParserConfigurationException;
+import java.io.*;
+import java.lang.reflect.Constructor;
+import java.lang.reflect.Modifier;
+import java.text.DateFormat;
+import java.text.SimpleDateFormat;
+import java.util.*;
+
+/**
+ * Properties subclass, that reads and writes files in a simple XML format.
+ * Can be used in-place where ever {@link java.util.Properties}
+ * is used. The major differences are that it reads
+ * and writes XML files, instead of ".properties" format files, and has
+ * support for typed values (where normal Properties only supports Strings).
+ *
+ * The greatest advantage of the XML format, is that it
+ * supports hierarchial structures or grouping of properties, in addtition to
+ * be a more standard way of storing data. The XML format also opens up for
+ * allowing for more metadata on
+ * the properties, such as type and the format information, specifying how to
+ * read and write them.
+ *
+ * This class relies on SAX for reading and parsing XML, in
+ * addition, it requires DOM for outputting XML. It is possible
+ * to configure what (SAX implementing) parser to use, by setting the system
+ * property {@code org.xml.sax.driver} to your favourite SAX parser. The
+ * default is the {@code org.apache.xerces.parsers.SAXParser}.
+ *
+ * XML Format (DTD):
+ *
+ * <!ELEMENT properties (property)*>
+ * <!ELEMENT property (value?, property*)>
+ * <!ATTLIST property
+ * name CDATA #REQUIRED
+ * value CDATA #IMPLIED
+ * type CDATA "String"
+ * format CDATA #IMPLIED
+ * >
+ * <!ELEMENT value (PCDATA)>
+ * <!ATTLIST value
+ * type CDATA "String"
+ * format CDATA #IMPLIED
+ * >
+ *
+ * See {@link #SYSTEM_DTD_URI}, {@link #DTD}.
+ *
+ * XML Format eample:
+ *
+ * <?xml version="1.0" encoding="UTF-8"?>
+ * <!DOCTYPE properties SYSTEM "http://www.twelvemonkeys.com/xml/XMLProperties.dtd">
+ * <!-- A simple XMLProperties example -->
+ * <!-- Sat Jan 05 00:16:55 CET 2002 -->
+ * <properties>
+ * <property name="one" value="1" type="Integer" />
+ * <property name="two">
+ * <property name="a" value="A" />
+ * <property name="b">
+ * <value>B is a very long value, that can span several
+ * lines
+ * <![CDATA[<this><doesn't ---> really
+ * matter<
+ * ]]>
+ * as it is escaped using CDATA.</value>
+ * </property>
+ * <property name="c" value="C">
+ * <property name="i" value="I"/>
+ * </property>
+ * </property>
+ * <property name="date" value="16. Mar 2002"
+ * type="java.util.Date" format="dd. MMM yyyy"/>
+ * <property name="none" value="" />
+ * </properties>
+ *
+ * Results in the properties {@code one=1, two.a=A, two.b=B is a very long...,
+ * two.c=C, two.c.i=I, date=Sat Mar 16 00:00:00 CET 2002
+ * } and {@code none=}. Note that there is no property named
+ * {@code two}.
+ *
+ * @see java.util.Properties
+ * @see #setPropertyValue(String,Object)
+ * @see #getPropertyValue(String)
+ * @see #load(InputStream)
+ * @see #store(OutputStream,String)
+ *
+ * @author Harald Kuhr
+ * @author last modified by $Author: haku $
+ * @version $Id: //depot/branches/personal/haraldk/twelvemonkeys/release-2/twelvemonkeys-core/src/main/java/com/twelvemonkeys/util/XMLProperties.java#1 $
+ *
+ */
+// TODO: Consider deleting this code.. Look at Properties XML format.
+public class XMLProperties extends Properties {
+
+ /** {@code "UTF-8"} */
+ public final static String UTF_8_ENCODING = "UTF-8";
+
+ /** {@code "xmlns"} */
+ public final static String XMLNS = "xmlns";
+
+ /** {@code "properties"} */
+ public final static String PROPERTIES = "properties";
+
+ /** {@code "property"} */
+ public final static String PROPERTY = "property";
+
+ /** {@code "name"} */
+ public final static String PROPERTY_NAME = "name";
+
+ /** {@code "value"} */
+ public final static String PROPERTY_VALUE = "value";
+
+ /** {@code "type"} */
+ public final static String PROPERTY_TYPE = "type";
+
+ /** {@code "format"} */
+ public final static String PROPERTY_FORMAT = "format";
+
+ /** {@code "String"} ({@link java.lang.String}) */
+ public final static String DEFAULT_TYPE = "String";
+
+ /** {@code "yyyy-MM-dd hh:mm:ss.SSS"}
+ * ({@link java.sql.Timestamp} format, excpet nanos) */
+ public final static String DEFAULT_DATE_FORMAT = "yyyy-MM-dd hh:mm:ss.SSS";
+
+ /** This is the DTD */
+ public final static String DTD =
+ "\n\n\n\n";
+
+ /** {@code "http://www.twelvemonkeys.com/xml/XMLProperties.dtd"} */
+ public final static String SYSTEM_DTD_URI = "http://www.twelvemonkeys.com/xml/XMLProperties.dtd";
+
+ /** {@code "http://www.twelvemonkeys.com/xml/XMLProperties"} */
+ public final static String NAMESPACE_URI = "http://www.twelvemonkeys.com/xml/XMLProperties";
+
+ /** {@code "http://xml.org/sax/features/validation"} */
+ public final static String SAX_VALIDATION_URI = "http://xml.org/sax/features/validation";
+
+ /** debug */
+ private boolean mValidation = true;
+ protected Vector mErrors = null;
+ protected Vector mWarnings = null;
+
+ // protected Hashtable mTypes = new Hashtable();
+ protected Hashtable mFormats = new Hashtable();
+ protected static DateFormat sDefaultFormat = new SimpleDateFormat(DEFAULT_DATE_FORMAT);
+
+ /**
+ * Creates an empty property list with no default values.
+ */
+ public XMLProperties() {}
+
+ /**
+ * Creates an empty property list with the specified defaults.
+ *
+ * @param pDefaults the defaults.
+ */
+ public XMLProperties(Properties pDefaults) {
+
+ // Sets the protected defaults variable
+ super(pDefaults);
+ }
+
+ void addXMLError(SAXParseException pException) {
+
+ if (mErrors == null) {
+ mErrors = new Vector();
+ }
+ mErrors.add(pException);
+ }
+
+ /**
+ * Gets the non-fatal XML errors (SAXParseExceptions) resulting from a
+ * load.
+ *
+ * @return An array of SAXParseExceptions, or null if none occured.
+ *
+ * @see XMLProperties.PropertiesHandler
+ * @see #load(InputStream)
+ */
+ public SAXParseException[] getXMLErrors() {
+
+ if (mErrors == null) {
+ return null;
+ }
+ return (SAXParseException[]) mErrors.toArray(new SAXParseException[mErrors.size()]);
+ }
+
+ void addXMLWarning(SAXParseException pException) {
+
+ if (mWarnings == null) {
+ mWarnings = new Vector();
+ }
+ mWarnings.add(pException);
+ }
+
+ /**
+ * Gets the XML warnings (SAXParseExceptions) resulting from a load.
+ *
+ * @return An array of SAXParseExceptions, or null if none occured.
+ *
+ * @see XMLProperties.PropertiesHandler
+ * @see #load(InputStream)
+ */
+ public SAXParseException[] getXMLWarnings() {
+
+ if (mWarnings == null) {
+ return null;
+ }
+ return (SAXParseException[]) mWarnings.toArray(new SAXParseException[mWarnings.size()]);
+ }
+
+ /**
+ * Reads a property list (key and element pairs) from the input stream. The
+ * stream is assumed to be using the UFT-8 character encoding, and be in
+ * valid, well-formed XML format.
+ *
+ * After loading, any errors or warnings from the SAX parser, are available
+ * as array of SAXParseExceptions from the getXMLErrors and getXMLWarnings
+ * methods.
+ *
+ * @param pInput the input stream to load from.
+ *
+ * @exception IOException if an error occurred when reading from the input
+ * stream. Any SAXExceptions are also wrapped in IOExceptions.
+ *
+ * @see Properties#load(InputStream)
+ * @see #DTD
+ * @see #SYSTEM_DTD_URI
+ * @see XMLProperties.PropertiesHandler
+ * @see #getXMLErrors
+ * @see #getXMLWarnings
+ */
+ public synchronized void load(InputStream pInput) throws IOException {
+ // Get parser instance
+ XMLReader parser;
+
+ // Try to instantiate System default parser
+ String driver = System.getProperty("org.xml.sax.driver");
+
+ if (driver == null) {
+
+ // Instantiate the org.apache.xerces.parsers.SAXParser as default
+ driver = "org.apache.xerces.parsers.SAXParser";
+ }
+ try {
+ parser = XMLReaderFactory.createXMLReader(driver);
+ parser.setFeature(SAX_VALIDATION_URI, mValidation);
+ } catch (SAXNotRecognizedException saxnre) {
+
+ // It should be okay to throw RuntimeExptions, as you will need an
+ // XML parser
+ throw new RuntimeException("Error configuring XML parser \"" + driver + "\": " + saxnre.getClass().getName() + ": "
+ + saxnre.getMessage());
+ } catch (SAXException saxe) {
+ throw new RuntimeException("Error creating XML parser \"" + driver + "\": " + saxe.getClass().getName() + ": " + saxe.getMessage());
+ }
+
+ // Register handler
+ PropertiesHandler handler = new PropertiesHandler(this);
+
+ parser.setContentHandler(handler);
+ parser.setErrorHandler(handler);
+ parser.setDTDHandler(handler);
+ parser.setEntityResolver(handler);
+
+ // Read and parse XML
+ try {
+ parser.parse(new InputSource(pInput));
+ } catch (SAXParseException saxpe) {
+
+ // Wrap SAXException in IOException to be consistent
+ throw new IOException("Error parsing XML: " + saxpe.getClass().getName() + ": " + saxpe.getMessage() + " Line: "
+ + saxpe.getLineNumber() + " Column: " + saxpe.getColumnNumber());
+ } catch (SAXException saxe) {
+
+ // Wrap SAXException in IOException to be consistent
+ // Doesn't realy matter, as the SAXExceptions seems to be pretty
+ // meaningless themselves...
+ throw new IOException("Error parsing XML: " + saxe.getClass().getName() + ": " + saxe.getMessage());
+ }
+ }
+
+ /**
+ * Initializes the value of a property.
+ *
+ * @todo move init code to the parser?
+ *
+ * @throws ClassNotFoundException if there is no class found for the given
+ * type
+ * @throws IllegalArgumentException if the value given, is not parseable
+ * as the given type
+ */
+ protected Object initPropertyValue(String pValue, String pType, String pFormat) throws ClassNotFoundException {
+
+ // System.out.println("pValue=" + pValue + " pType=" + pType
+ // + " pFormat=" + pFormat);
+ // No value to convert
+ if (pValue == null) {
+ return null;
+ }
+
+ // No conversion needed for Strings
+ if ((pType == null) || pType.equals("String") || pType.equals("java.lang.String")) {
+ return pValue;
+ }
+ Object value;
+
+ if (pType.equals("Date") || pType.equals("java.util.Date")) {
+
+ // Special parser needed
+ try {
+
+ // Parse date through StringUtil
+ if (pFormat == null) {
+ value = StringUtil.toDate(pValue, sDefaultFormat);
+ } else {
+ value = StringUtil.toDate(pValue, new SimpleDateFormat(pFormat));
+ }
+ } catch (IllegalArgumentException e) {
+
+ // Not parseable...
+ throw e;
+ }
+
+ // Return
+ return value;
+ } else if (pType.equals("java.sql.Timestamp")) {
+
+ // Special parser needed
+ try {
+
+ // Parse timestamp through StringUtil
+ value = StringUtil.toTimestamp(pValue);
+ } catch (IllegalArgumentException e) {
+
+ // Not parseable...
+ throw new RuntimeException(e.getMessage());
+ }
+
+ // Return
+ return value;
+ } else {
+ int dot = pType.indexOf(".");
+
+ if (dot < 0) {
+ pType = "java.lang." + pType;
+ }
+
+ // Get class
+ Class cl = Class.forName(pType);
+
+ // Try to create instance from (String)
+ value = createInstance(cl, pValue);
+ if (value == null) {
+
+ // createInstance failed for some reason
+ // Try to invoke the static method valueof(String)
+ value = invokeStaticMethod(cl, "valueOf", pValue);
+
+ // If the value is still null, well, then I cannot help...
+ }
+ }
+
+ // Return
+ return value;
+ }
+
+ /**
+ * Creates an object from the given class' single argument constructor.
+ *
+ * @return The object created from the constructor.
+ * If the constructor could not be invoked for any reason, null is
+ * returned.
+ */
+ private Object createInstance(Class pClass, Object pParam) {
+ Object value;
+
+ try {
+
+ // Create param and argument arrays
+ Class[] param = { pParam.getClass() };
+ Object[] arg = { pParam };
+
+ // Get constructor
+ Constructor constructor = pClass.getDeclaredConstructor(param);
+
+ // Invoke and create instance
+ value = constructor.newInstance(arg);
+ } catch (Exception e) {
+ return null;
+ }
+ return value;
+ }
+
+ /**
+ * Creates an object from any given static method, given the parameter
+ *
+ * @return The object returned by the static method.
+ * If the return type of the method is a primitive type, it is wrapped in
+ * the corresponding wrapper object (int is wrapped in an Integer).
+ * If the return type of the method is void, null is returned.
+ * If the method could not be invoked for any reason, null is returned.
+ */
+ private Object invokeStaticMethod(Class pClass, String pMethod, Object pParam) {
+ Object value = null;
+
+ try {
+
+ // Create param and argument arrays
+ Class[] param = { pParam.getClass() };
+ Object[] arg = { pParam };
+
+ // Get method
+ // *** If more than one such method is found in the class, and one
+ // of these methods has a RETURN TYPE that is more specific than
+ // any of the others, that method is reflected; otherwise one of
+ // the methods is chosen ARBITRARILY.
+ // java/lang/Class.html#getMethod(java.lang.String, java.lang.Class[])
+ java.lang.reflect.Method method = pClass.getMethod(pMethod, param);
+
+ // Invoke public static method
+ if (Modifier.isPublic(method.getModifiers()) && Modifier.isStatic(method.getModifiers())) {
+ value = method.invoke(null, arg);
+ }
+ } catch (Exception e) {
+ return null;
+ }
+ return value;
+ }
+
+ /**
+ * Gets the format of a property. This value is used for formatting the
+ * value before it is stored as xml.
+ *
+ * @param pKey a key in this hashtable.
+ *
+ * @return the format for the specified key or null if it does not
+ * have one.
+ */
+ public String getPropertyFormat(String pKey) {
+
+ // Get format
+ return StringUtil.valueOf(mFormats.get(pKey));
+ }
+
+ /**
+ * Sets the format of a property. This value is used for formatting the
+ * value before it is stored as xml.
+ *
+ * @param pKey a key in this hashtable.
+ * @param pFormat a string representation of the format.
+ *
+ * @return the previous format for the specified key or null if it did not
+ * have one.
+ */
+ public synchronized String setPropertyFormat(String pKey, String pFormat) {
+
+ // Insert format
+ return StringUtil.valueOf(mFormats.put(pKey, pFormat));
+ }
+
+ /**
+ * Calls the Hashtable method put. Provided for parallelism with the
+ * getPropertyValue method. Enforces use of strings for property keys.
+ * The value returned is the result of the Hashtable call to put.
+ *
+ * @param pKey the key to be placed into this property list.
+ * @param pValue the value corresponding to key.
+ *
+ * @return the previous value of the specified key in this property list,
+ * or null if it did not have one.
+ *
+ * @see #getPropertyValue(String)
+ */
+ public synchronized Object setPropertyValue(String pKey, Object pValue) {
+
+ // Insert value
+ return put(pKey, pValue);
+ }
+
+ /**
+ * Searches for the property with the specified key in this property list.
+ * If the key is not found in this property list, the default property
+ * list, and its defaults, recursively, are then checked. The method
+ * returns null if the property is not found.
+ *
+ * @param pKey the property key.
+ *
+ * @return the value in this property list with the specified key value.
+ *
+ * @see #setPropertyValue(String, Object)
+ * @see #getPropertyValue(String, Object)
+ * @see Properties#defaults
+ */
+ public synchronized Object getPropertyValue(String pKey) {
+ return getPropertyValue(pKey, null);
+ }
+
+ /**
+ * Searches for the property with the specified key in this property list.
+ * If the key is not found in this property list, the default property
+ * list, and its defaults, recursively, are then checked. The method
+ * returns the default value argument if the property is not found.
+ *
+ * @param pKey the property key.
+ * @param pDefaultValue the default value.
+ *
+ * @return the value in this property list with the specified key value.
+ *
+ * @see #getPropertyValue(String)
+ * @see Properties#defaults
+ */
+ public Object getPropertyValue(String pKey, Object pDefaultValue) {
+
+ Object value = super.get(pKey); // super.get() is EXTREMELEY IMPORTANT
+
+ if (value != null) {
+ return value;
+ }
+ if (defaults instanceof XMLProperties) {
+ return (((XMLProperties) defaults).getPropertyValue(pKey));
+ }
+ return ((defaults != null) ? defaults.getProperty(pKey) : pDefaultValue);
+ }
+
+ /**
+ * Overloaded get method, that always returns Strings.
+ * Due to the way the store and list methods of
+ * java.util.Properties works (it calls get and casts to String, instead
+ * of calling getProperty), this methods returns
+ * StringUtil.valueOf(super.get), to avoid ClassCastExcpetions.
+ *
+ * If you need the old functionality back,
+ * getPropertyValue returns super.get directly.
+ * A cleaner approach would be to override the list and store
+ * methods, but it's too much work for nothing...
+ *
+ * @param pKey a key in this hashtable
+ *
+ * @return the value to which the key is mapped in this hashtable,
+ * converted to a string; null if the key is not mapped to any value in
+ * this hashtable.
+ *
+ * @see #getPropertyValue(String)
+ * @see Properties#getProperty(String)
+ * @see Hashtable#get(Object)
+ * @see StringUtil#valueOf(Object)
+ */
+ public Object get(Object pKey) {
+
+ //System.out.println("get(" + pKey + "): " + super.get(pKey));
+ Object value = super.get(pKey);
+
+ // ---
+ if ((value != null) && (value instanceof Date)) { // Hmm.. This is true for subclasses too...
+
+ // Special threatment of Date
+ String format = getPropertyFormat(StringUtil.valueOf(pKey));
+
+ if (format != null) {
+ value = new SimpleDateFormat(format).format(value);
+ } else {
+ value = sDefaultFormat.format(value);
+ }
+ return value;
+ }
+
+ // ---
+ // Simply return value
+ return StringUtil.valueOf(value);
+ }
+
+ /**
+ * Searches for the property with the specified key in this property list.
+ * If the key is not found in this property list, the default property list,
+ * and its defaults, recursively, are then checked. The method returns the
+ * default value argument if the property is not found.
+ *
+ * @param pKey the hashtable key.
+ * @param pDefaultValue a default value.
+ *
+ * @return the value in this property list with the specified key value.
+ * @see #setProperty
+ * @see #defaults
+ */
+ public String getProperty(String pKey, String pDefaultValue) {
+
+ // Had to override this because Properties uses super.get()...
+ String value = (String) get(pKey); // Safe cast, see get(Object)
+
+ if (value != null) {
+ return value;
+ }
+ return ((defaults != null)
+ ? defaults.getProperty(pKey)
+ : pDefaultValue);
+ }
+
+ /**
+ * Searches for the property with the specified key in this property list.
+ * If the key is not found in this property list, the default property list,
+ * and its defaults, recursively, are then checked. The method returns
+ * {@code null} if the property is not found.
+ *
+ * @param pKey the property key.
+ * @return the value in this property list with the specified key value.
+ * @see #setProperty
+ * @see #defaults
+ */
+ public String getProperty(String pKey) {
+
+ // Had to override this because Properties uses super.get()...
+ return getProperty(pKey, null);
+ }
+
+ /**
+ * Writes this property list (key and element pairs) in this
+ * {@code Properties}
+ * table to the output stream in a format suitable for loading into a
+ * Properties table using the load method. This implementation writes
+ * the list in XML format. The stream is written using the UTF-8 character
+ * encoding.
+ *
+ * @param pOutput the output stream to write to.
+ * @param pHeader a description of the property list.
+ *
+ * @exception IOException if writing this property list to the specified
+ * output stream throws an IOException.
+ *
+ * @see java.util.Properties#store(OutputStream,String)
+ */
+ public synchronized void store(OutputStream pOutput, String pHeader) throws IOException {
+ storeXML(this, pOutput, pHeader);
+ }
+
+ /**
+ * Utility method that stores the property list in normal properties
+ * format. This method writes the list of Properties (key and element
+ * pairs) in the given {@code Properties}
+ * table to the output stream in a format suitable for loading into a
+ * Properties table using the load method. The stream is written using the
+ * ISO 8859-1 character encoding.
+ *
+ * @param pProperties the Properties table to store
+ * @param pOutput the output stream to write to.
+ * @param pHeader a description of the property list.
+ *
+ * @exception IOException if writing this property list to the specified
+ * output stream throws an IOException.
+ *
+ * @see java.util.Properties#store(OutputStream,String)
+ */
+ public static void storeProperties(Map pProperties, OutputStream pOutput, String pHeader) throws IOException {
+ // Create new properties
+ Properties props = new Properties();
+
+ // Copy all elements from the pProperties (shallow)
+ Iterator iterator = pProperties.entrySet().iterator();
+
+ while (iterator.hasNext()) {
+ Map.Entry entry = (Map.Entry) iterator.next();
+
+ props.setProperty((String) entry.getKey(), StringUtil.valueOf(entry.getValue()));
+ }
+
+ // Store normal properties
+ props.store(pOutput, pHeader);
+ }
+
+ /**
+ * Utility method that stores the property list in XML format. This method
+ * writes the list of Properties (key and element pairs) in the given
+ * {@code Properties}
+ * table to the output stream in a format suitable for loading into a
+ * XMLProperties table using the load method. Useful for converting
+ * Properties into XMLProperties.
+ * The stream is written using the UTF-8 character
+ * encoding.
+ *
+ * @param pProperties the Properties table to store.
+ * @param pOutput the output stream to write to.
+ * @param pHeader a description of the property list.
+ *
+ * @exception IOException if writing this property list to the specified
+ * output stream throws an IOException.
+ *
+ * @see #store(OutputStream,String)
+ * @see java.util.Properties#store(OutputStream,String)
+ *
+ * @todo Find correct way of setting namespace URI's
+ * @todo Store type and format information
+ */
+ public static void storeXML(Map pProperties, OutputStream pOutput, String pHeader) throws IOException {
+ // Build XML tree (Document) and write
+ // Find the implementation
+ DocumentBuilderFactory factory = DocumentBuilderFactory.newInstance();
+ factory.setNamespaceAware(true);
+ DocumentBuilder builder;
+ try {
+ builder = factory.newDocumentBuilder();
+ }
+ catch (ParserConfigurationException e) {
+ throw (IOException) new IOException(e.getMessage()).initCause(e);
+ }
+ DOMImplementation dom = builder.getDOMImplementation();
+
+ Document document = dom.createDocument(null, PROPERTIES, dom.createDocumentType(PROPERTIES, null, SYSTEM_DTD_URI));
+ Element root = document.getDocumentElement();
+
+ // This is probably not the correct way of setting a default namespace
+ root.setAttribute(XMLNS, NAMESPACE_URI);
+
+ // Create and insert the normal Properties headers as XML comments
+ if (pHeader != null) {
+ document.insertBefore(document.createComment(" " + pHeader + " "), root);
+ }
+ document.insertBefore(document.createComment(" " + new Date() + " "), root);
+
+ // Insert elements from the Properties
+ Iterator iterator = pProperties.entrySet().iterator();
+
+ while (iterator.hasNext()) {
+ Map.Entry entry = (Map.Entry) iterator.next();
+ String key = (String) entry.getKey();
+ Object value = entry.getValue();
+ String format = null;
+
+ if (pProperties instanceof XMLProperties) {
+ format = ((XMLProperties) pProperties).getPropertyFormat(key);
+ }
+ insertElement(document, key, value, format);
+ }
+
+ // Create serializer and output document
+ //XMLSerializer serializer = new XMLSerializer(pOutput, new OutputFormat(document, UTF_8_ENCODING, true));
+ XMLSerializer serializer = new XMLSerializer(pOutput, UTF_8_ENCODING);
+
+ serializer.serialize(document);
+ }
+
+ /**
+ * Inserts elements to the given document one by one, and creates all its
+ * parents if needed.
+ *
+ * @param pDocument the document to insert to.
+ * @param pName the name of the property element.
+ * @param pValue the value of the property element.
+ * @param pFormat
+ *
+ * @todo I guess this implementation could use some optimisaztion, as
+ * we do a lot of unneccessary looping.
+ */
+ private static void insertElement(Document pDocument, String pName, Object pValue, String pFormat) {
+
+ // Get names of all elements we need
+ String[] names = StringUtil.toStringArray(pName, ".");
+
+ // Get value formatted as string
+ String value = null;
+
+ if (pValue != null) {
+ // ---
+ if (pValue instanceof Date) {
+
+ // Special threatment of Date
+ if (pFormat != null) {
+ value = new SimpleDateFormat(pFormat).format(pValue);
+ }
+ else {
+ value = sDefaultFormat.format(pValue);
+ }
+ }
+ else {
+ value = String.valueOf(pValue);
+ }
+
+ // ---
+ }
+
+ // Loop through document from root, and insert parents as needed
+ Element element = pDocument.getDocumentElement();
+
+ for (int i = 0; i < names.length; i++) {
+ boolean found = false;
+
+ // Get children
+ NodeList children = element.getElementsByTagName(PROPERTY);
+ Element child = null;
+
+ // Search for correct name
+ for (int j = 0; j < children.getLength(); j++) {
+ child = (Element) children.item(j);
+ if (names[i].equals(child.getAttribute(PROPERTY_NAME))) {
+ // Found
+ found = true;
+ element = child;
+ break; // Next name
+ }
+ }
+
+ // Test if the node was not found, otherwise we need to insert
+ if (!found) {
+ // Not found
+ child = pDocument.createElement(PROPERTY);
+ child.setAttribute(PROPERTY_NAME, names[i]);
+
+ // Insert it
+ element.appendChild(child);
+ element = child;
+ }
+
+ // If it's the destination node, set the value
+ if ((i + 1) == names.length) {
+
+ // If the value string contains special data,
+ // use a CDATA block instead of the "value" attribute
+ if (StringUtil.contains(value, "\n") || StringUtil.contains(value, "\t") || StringUtil.contains(value, "\"")
+ || StringUtil.contains(value, "&") || StringUtil.contains(value, "<") || StringUtil.contains(value, ">")) {
+
+ // Create value element
+ Element valueElement = pDocument.createElement(PROPERTY_VALUE);
+
+ // Set type attribute
+ String className = pValue.getClass().getName();
+
+ className = StringUtil.replace(className, "java.lang.", "");
+ if (!DEFAULT_TYPE.equals(className)) {
+ valueElement.setAttribute(PROPERTY_TYPE, className);
+ }
+
+ // Set format attribute
+ if (pFormat != null) {
+ valueElement.setAttribute(PROPERTY_FORMAT, pFormat);
+ }
+
+ // Crate cdata section
+ CDATASection cdata = pDocument.createCDATASection(value);
+
+ // Append to document tree
+ valueElement.appendChild(cdata);
+ child.appendChild(valueElement);
+ }
+ else {
+ // Just set normal attribute value
+ child.setAttribute(PROPERTY_VALUE, value);
+
+ // Set type attribute
+ String className = pValue.getClass().getName();
+
+ className = StringUtil.replace(className, "java.lang.", "");
+ if (!DEFAULT_TYPE.equals(className)) {
+ child.setAttribute(PROPERTY_TYPE, className);
+ }
+
+ // If format is set, store in attribute
+ if (pFormat != null) {
+ child.setAttribute(PROPERTY_FORMAT, pFormat);
+ }
+ }
+ }
+ }
+ }
+
+ /**
+ * Gets all properties in a properties group.
+ * If no properties exists in the specified group, {@code null} is
+ * returned.
+ *
+ * @param pGroupKey the group key
+ *
+ * @return a new Properties continaing all properties in the group. Keys in
+ * the new Properties wil not contain the group key.
+ * If no properties exists in the specified group, {@code null} is
+ * returned.
+ */
+ public Properties getProperties(String pGroupKey) {
+ // Stupid impl...
+ XMLProperties props = new XMLProperties();
+ String groupKey = pGroupKey;
+
+ if (groupKey.charAt(groupKey.length()) != '.') {
+ groupKey += ".";
+ }
+
+ Iterator iterator = entrySet().iterator();
+
+ while (iterator.hasNext()) {
+ Map.Entry entry = (Map.Entry) iterator.next();
+ String key = (String) entry.getKey();
+
+ if (key.startsWith(groupKey)) {
+ String subKey = key.substring(key.indexOf(groupKey));
+
+ props.setPropertyValue(subKey, entry.getValue());
+ }
+ }
+
+ return ((props.size() > 0) ? props : null);
+ }
+
+ /**
+ * Sets the properties in the given properties group.
+ * Existing properties in the same group, will not be removed, unless they
+ * are replaced by new values.
+ * Any existing properties in the same group that was replaced, are
+ * returned. If no properties are replaced, null is
+ * returned.
+ *
+ * @param pGroupKey the group key
+ * @param pProperties the properties to set into this group
+ *
+ * @return Any existing properties in the same group that was replaced.
+ * If no properties are replaced, null is
+ * returned.
+ */
+ public Properties setProperties(String pGroupKey, Properties pProperties) {
+ XMLProperties old = new XMLProperties();
+ String groupKey = pGroupKey;
+
+ if (groupKey.charAt(groupKey.length()) != '.') {
+ groupKey += ".";
+ }
+ Iterator iterator = pProperties.entrySet().iterator();
+
+ while (iterator.hasNext()) {
+ Map.Entry entry = (Map.Entry) iterator.next();
+ String key = (String) entry.getKey();
+ Object obj = setPropertyValue(groupKey + key, entry.getValue());
+
+ // Return removed entries
+ if (obj != null) {
+ old.setPropertyValue(groupKey + key, entry.getValue());
+ }
+ }
+ return ((old.size() > 0) ? old : null);
+ }
+
+ /**
+ * For testing only.
+ */
+ public static void main(String[] pArgs) throws Exception {
+ // -- Print DTD
+ System.out.println("DTD: \n" + DTD);
+ System.out.println("--");
+
+ // -- Test load
+ System.out.println("Reading properties from \"" + pArgs[0] + "\"...");
+ XMLProperties props = new XMLProperties();
+
+ props.load(new FileInputStream(new File(pArgs[0])));
+ props.list(System.out);
+ System.out.println("--");
+
+ // -- Test recursion
+ String key = "key";
+ Object old = props.setProperty(key, "AAA");
+ Properties p1 = new XMLProperties(new XMLProperties(props));
+ Properties p2 = new Properties(new Properties(props));
+
+ System.out.println("XMLProperties: " + p1.getProperty(key) + " ==" + " Properties: " + p2.getProperty(key));
+ if (old == null) {
+ props.remove("key");
+ } else {
+ props.put("key", old); // Put old value back, to avoid confusion...
+ }
+ System.out.println("--");
+
+ // -- Test store
+ //props.store(System.out, "XML Properties file written by XMLProperties.");
+ File out = new File("copy_of_" + pArgs[0]);
+
+ System.out.println("Writing properties to \"" + out.getName() + "\"");
+ if (!out.exists()) {
+ props.store(new FileOutputStream(out), "XML Properties file written by XMLProperties.");
+ } else {
+ System.err.println("File \"" + out.getName() + "\" allready exists, cannot write!");
+ }
+
+ // -- Test utility methods
+ // Write normal properties from XMLProperties
+ out = new File("copy_of_" + pArgs[0].substring(0, pArgs[0].lastIndexOf(".")) + ".properties");
+ System.out.println("Writing properties to \"" + out.getName() + "\"");
+ if (!out.exists()) {
+ storeProperties(props, new FileOutputStream(out), "Properties file written by XMLProperties.");
+ } else {
+ System.err.println("File \"" + out.getName() + "\" allready exists, cannot write!");
+ }
+ System.out.println("--");
+
+ // -- Test type attribute
+ System.out.println("getPropertyValue(\"one\"): " + props.getPropertyValue("one") + " class: "
+ + props.getPropertyValue("one").getClass());
+ System.out.println("setPropertyValue(\"now\", " + new Date() + "): " + props.setPropertyValue("now", new Date()) + " class: "
+ + props.getPropertyValue("now").getClass());
+ System.out.println("getPropertyValue(\"date\"): " + props.getPropertyValue("date") + " class: "
+ + props.getPropertyValue("date").getClass());
+ System.out.println("getPropertyValue(\"time\"): " + props.getPropertyValue("time") + " class: "
+ + props.getPropertyValue("time").getClass());
+ }
+
+ /**
+ * ContentHandler, ErrorHandler and EntityResolver implementation for the
+ * SAX Parser.
+ */
+ protected class PropertiesHandler extends DefaultHandler {
+ protected Stack mStack = null;
+
+ /** Stores the characters read so far, from the characters callback */
+ protected char[] mReadSoFar = null;
+ protected boolean mIsValue = false;
+ protected String mType = null;
+ protected String mFormat = null;
+ protected XMLProperties mProperties = null;
+ protected Locator mLocator = null;
+
+ /**
+ * Creates a PropertiesHandler for the given XMLProperties.
+ */
+ PropertiesHandler(XMLProperties pProperties) {
+ mProperties = pProperties;
+ mStack = new Stack();
+ }
+
+ /**
+ * setDocumentLocator implementation.
+ */
+ public void setDocumentLocator(Locator pLocator) {
+
+ // System.out.println("Setting locator: " + pLocator);
+ mLocator = pLocator;
+ }
+
+ /**
+ * Calls XMLProperties.addXMLError with the given SAXParseException
+ * as the argument.
+ */
+ public void error(SAXParseException pException) throws SAXParseException {
+
+ //throw pException;
+ mProperties.addXMLError(pException);
+
+ /*
+ System.err.println("error: " + pException.getMessage());
+ System.err.println("line: " + mLocator.getLineNumber());
+ System.err.println("column: " + mLocator.getColumnNumber());
+ */
+ }
+
+ /**
+ * Throws the given SAXParseException (and stops the parsing).
+ */
+ public void fatalError(SAXParseException pException) throws SAXParseException {
+
+ throw pException;
+
+ /*
+ System.err.println("fatal error: " + pException.getMessage());
+ System.err.println("line: " + mLocator.getLineNumber());
+ System.err.println("column: " + mLocator.getColumnNumber());
+ */
+ }
+
+ /**
+ * Calls XMLProperties.addXMLWarning with the given SAXParseException
+ * as the argument.
+ */
+ public void warning(SAXParseException pException) throws SAXParseException {
+
+ // throw pException;
+ mProperties.addXMLWarning(pException);
+
+ /*
+ System.err.println("warning: " + pException.getMessage());
+ System.err.println("line: " + mLocator.getLineNumber());
+ System.err.println("column: " + mLocator.getColumnNumber());
+ */
+ }
+
+ /**
+ * startElement implementation.
+ */
+ public void startElement(String pNamespaceURI, String pLocalName, String pQualifiedName, Attributes pAttributes) throws SAXException {
+
+ /*
+
+ String attributes = "";
+ for (int i = 0; i < pAttributes.getLength(); i++) {
+ attributes += pAttributes.getQName(i) + "=" + pAttributes.getValue(i) + (i < pAttributes.getLength() ? ", " : "");
+ }
+
+ System.out.println("startElement: " + pNamespaceURI
+ + "." + pLocalName
+ + " (" + pQualifiedName + ") "
+ + attributes);
+ */
+ if (XMLProperties.PROPERTY.equals(pLocalName)) {
+
+ // Get attibute values
+ String name = pAttributes.getValue(XMLProperties.PROPERTY_NAME);
+ String value = pAttributes.getValue(XMLProperties.PROPERTY_VALUE);
+ String type = pAttributes.getValue(XMLProperties.PROPERTY_TYPE);
+ String format = pAttributes.getValue(XMLProperties.PROPERTY_FORMAT);
+
+ // Get the full name of the property
+ if (!mStack.isEmpty()) {
+ name = (String) mStack.peek() + "." + name;
+ }
+
+ // Set the property
+ if (value != null) {
+ mProperties.setProperty(name, value);
+
+ // Store type & format
+ if (!XMLProperties.DEFAULT_TYPE.equals(type)) {
+ mType = type;
+ mFormat = format; // Might be null (no format)
+ }
+ }
+
+ // Push the last name on the stack
+ mStack.push(name);
+ } // /PROPERTY
+ else if (XMLProperties.PROPERTY_VALUE.equals(pLocalName)) {
+
+ // Get attibute values
+ String name = (String) mStack.peek();
+ String type = pAttributes.getValue(XMLProperties.PROPERTY_TYPE);
+ String format = pAttributes.getValue(XMLProperties.PROPERTY_FORMAT);
+
+ // Store type & format
+ if (!XMLProperties.DEFAULT_TYPE.equals(type)) {
+ mType = type;
+ mFormat = format;
+ }
+ mIsValue = true;
+ }
+ }
+
+ /**
+ * endElement implementation.
+ */
+ public void endElement(String pNamespaceURI, String pLocalName, String pQualifiedName) throws SAXException {
+
+ /*
+ System.out.println("endElement: " + pNamespaceURI
+ + "." + pLocalName + " (" + pQualifiedName + ")");
+ */
+ if (XMLProperties.PROPERTY.equals(pLocalName)) {
+
+ // Just remove the last name
+ String name = (String) mStack.pop();
+
+ // Init typed values
+ try {
+ String prop = mProperties.getProperty(name);
+
+ // Value may be null, if so just skip
+ if (prop != null) {
+ Object value = mProperties.initPropertyValue(prop, mType, mFormat);
+
+ // Store format
+ if ((mFormat != null) &&!XMLProperties.DEFAULT_DATE_FORMAT.equals(mFormat)) {
+ mProperties.setPropertyFormat(name, mFormat);
+ }
+
+ //System.out.println("-->" + prop + "-->" + value);
+ mProperties.setPropertyValue(name, value);
+ }
+
+ // Clear type & format
+ mType = null;
+ mFormat = null;
+ } catch (Exception e) {
+ e.printStackTrace(System.err);
+ throw new SAXException(e);
+ }
+ } else if (XMLProperties.PROPERTY_VALUE.equals(pLocalName)) {
+ if (mStack.isEmpty()) {
+
+ // There can't be any characters here, really
+ return;
+ }
+
+ // Get the full name of the property
+ String name = (String) mStack.peek();
+
+ // Set the property
+ String value = new String(mReadSoFar);
+
+ //System.err.println("characters: >" + value+ "<");
+ if (!StringUtil.isEmpty(value)) {
+
+ // If there is allready a value, both the value attribute
+ // and element have been specified, this is an error
+ if (mProperties.containsKey(name)) {
+ throw new SAXParseException(
+ "Value can only be specified either using the \"value\" attribute, OR the \"value\" element, not both.", mLocator);
+ }
+
+ // Finally, set the property
+ mProperties.setProperty(name, value);
+ }
+
+ // Done value processing
+ mIsValue = false;
+ }
+ }
+
+ /**
+ * characters implementation
+ */
+ public void characters(char[] pChars, int pStart, int pLength) throws SAXException {
+ // TODO: Use StringBuilder instead?
+ if (mIsValue) {
+ // If nothing read so far
+ if (mReadSoFar == null) {
+ // Create new array and copy into
+ mReadSoFar = new char[pLength];
+ System.arraycopy(pChars, pStart, mReadSoFar, 0, pLength);
+ }
+ else {
+ // Merge arrays
+ mReadSoFar = (char[]) CollectionUtil.mergeArrays(mReadSoFar, 0, mReadSoFar.length, pChars, pStart, pLength);
+ }
+ }
+ }
+
+
+ /**
+ * Intercepts the entity
+ * "http://www.twelvemonkeys.com/xml/XMLProperties.dtd", and return
+ * an InputSource based on the internal DTD of XMLProperties instead.
+ *
+ * @todo Maybe intercept a PUBLIC DTD and be able to have SYSTEM DTD
+ * override?
+ */
+ public InputSource resolveEntity(String pPublicId, String pSystemId) {
+ // If we are looking for the standard SYSTEM DTD, then
+ // Return an InputSource based on the internal DTD.
+ if (XMLProperties.SYSTEM_DTD_URI.equals(pSystemId)) {
+ return new InputSource(new StringReader(XMLProperties.DTD));
+ }
+
+ // use the default behaviour
+ return null;
+ }
+ }
+}
+
diff --git a/twelvemonkeys-sandbox/src/test/java/com/twelvemonkeys/io/enc/DeflateEncoderTestCase.java b/sandbox/common/src/test/java/com/twelvemonkeys/io/enc/DeflateEncoderTestCase.java
similarity index 100%
rename from twelvemonkeys-sandbox/src/test/java/com/twelvemonkeys/io/enc/DeflateEncoderTestCase.java
rename to sandbox/common/src/test/java/com/twelvemonkeys/io/enc/DeflateEncoderTestCase.java
diff --git a/twelvemonkeys-sandbox/src/test/java/com/twelvemonkeys/io/enc/InflateDecoderTestCase.java b/sandbox/common/src/test/java/com/twelvemonkeys/io/enc/InflateDecoderTestCase.java
similarity index 100%
rename from twelvemonkeys-sandbox/src/test/java/com/twelvemonkeys/io/enc/InflateDecoderTestCase.java
rename to sandbox/common/src/test/java/com/twelvemonkeys/io/enc/InflateDecoderTestCase.java
diff --git a/twelvemonkeys-servlet/pom.xml b/servlet/pom.xml
similarity index 70%
rename from twelvemonkeys-servlet/pom.xml
rename to servlet/pom.xml
index 4a2a966a..dd981f87 100644
--- a/twelvemonkeys-servlet/pom.xml
+++ b/servlet/pom.xml
@@ -2,34 +2,46 @@
- 4.0.0
- com.twelvemonkeys
- twelvemonkeys-servlet
- 2.3-ece
- TwelveMonkeys Servlet
-
com.twelvemonkeys
- twelvemonkeys-parent
- 2.0
+ twelvemonkeys
+ 3.0-SNAPSHOT
-
- 2.2
-
+ 4.0.0
+ com.twelvemonkeys.servlet
+ servlet
+ 3.0-SNAPSHOT
+ TwelveMonkeys :: Servlet
- com.twelvemonkeys
- twelvemonkeys-core
- ${core.version}
- compile
+ com.twelvemonkeys.common
+ common-lang
+ ${project.version}
+
+
+ com.twelvemonkeys.common
+ common-io
+ ${project.version}
+
+
+ com.twelvemonkeys.common
+ common-image
+ ${project.version}
- com.twelvemonkeys
- twelvemonkeys-core
- ${core.version}
+ com.twelvemonkeys.common
+ common-lang
+ ${project.version}
+ tests
+ test
+
+
+ com.twelvemonkeys.common
+ common-io
+ ${project.version}
tests
test
diff --git a/twelvemonkeys-servlet/src/main/java/com/twelvemonkeys/servlet/AbstractServletMapAdapter.java b/servlet/src/main/java/com/twelvemonkeys/servlet/AbstractServletMapAdapter.java
similarity index 100%
rename from twelvemonkeys-servlet/src/main/java/com/twelvemonkeys/servlet/AbstractServletMapAdapter.java
rename to servlet/src/main/java/com/twelvemonkeys/servlet/AbstractServletMapAdapter.java
diff --git a/twelvemonkeys-servlet/src/main/java/com/twelvemonkeys/servlet/BrowserHelperFilter.java b/servlet/src/main/java/com/twelvemonkeys/servlet/BrowserHelperFilter.java
similarity index 100%
rename from twelvemonkeys-servlet/src/main/java/com/twelvemonkeys/servlet/BrowserHelperFilter.java
rename to servlet/src/main/java/com/twelvemonkeys/servlet/BrowserHelperFilter.java
diff --git a/twelvemonkeys-servlet/src/main/java/com/twelvemonkeys/servlet/DebugServlet.java b/servlet/src/main/java/com/twelvemonkeys/servlet/DebugServlet.java
similarity index 100%
rename from twelvemonkeys-servlet/src/main/java/com/twelvemonkeys/servlet/DebugServlet.java
rename to servlet/src/main/java/com/twelvemonkeys/servlet/DebugServlet.java
diff --git a/twelvemonkeys-servlet/src/main/java/com/twelvemonkeys/servlet/GenericFilter.java b/servlet/src/main/java/com/twelvemonkeys/servlet/GenericFilter.java
similarity index 100%
rename from twelvemonkeys-servlet/src/main/java/com/twelvemonkeys/servlet/GenericFilter.java
rename to servlet/src/main/java/com/twelvemonkeys/servlet/GenericFilter.java
diff --git a/twelvemonkeys-servlet/src/main/java/com/twelvemonkeys/servlet/GenericServlet.java b/servlet/src/main/java/com/twelvemonkeys/servlet/GenericServlet.java
similarity index 100%
rename from twelvemonkeys-servlet/src/main/java/com/twelvemonkeys/servlet/GenericServlet.java
rename to servlet/src/main/java/com/twelvemonkeys/servlet/GenericServlet.java
diff --git a/twelvemonkeys-servlet/src/main/java/com/twelvemonkeys/servlet/HttpServlet.java b/servlet/src/main/java/com/twelvemonkeys/servlet/HttpServlet.java
similarity index 100%
rename from twelvemonkeys-servlet/src/main/java/com/twelvemonkeys/servlet/HttpServlet.java
rename to servlet/src/main/java/com/twelvemonkeys/servlet/HttpServlet.java
diff --git a/twelvemonkeys-servlet/src/main/java/com/twelvemonkeys/servlet/InitParam.java b/servlet/src/main/java/com/twelvemonkeys/servlet/InitParam.java
similarity index 100%
rename from twelvemonkeys-servlet/src/main/java/com/twelvemonkeys/servlet/InitParam.java
rename to servlet/src/main/java/com/twelvemonkeys/servlet/InitParam.java
diff --git a/twelvemonkeys-servlet/src/main/java/com/twelvemonkeys/servlet/OutputStreamAdapter.java b/servlet/src/main/java/com/twelvemonkeys/servlet/OutputStreamAdapter.java
similarity index 100%
rename from twelvemonkeys-servlet/src/main/java/com/twelvemonkeys/servlet/OutputStreamAdapter.java
rename to servlet/src/main/java/com/twelvemonkeys/servlet/OutputStreamAdapter.java
diff --git a/twelvemonkeys-servlet/src/main/java/com/twelvemonkeys/servlet/ProxyServlet.java b/servlet/src/main/java/com/twelvemonkeys/servlet/ProxyServlet.java
similarity index 100%
rename from twelvemonkeys-servlet/src/main/java/com/twelvemonkeys/servlet/ProxyServlet.java
rename to servlet/src/main/java/com/twelvemonkeys/servlet/ProxyServlet.java
diff --git a/twelvemonkeys-servlet/src/main/java/com/twelvemonkeys/servlet/SerlvetHeadersMapAdapter.java b/servlet/src/main/java/com/twelvemonkeys/servlet/SerlvetHeadersMapAdapter.java
similarity index 100%
rename from twelvemonkeys-servlet/src/main/java/com/twelvemonkeys/servlet/SerlvetHeadersMapAdapter.java
rename to servlet/src/main/java/com/twelvemonkeys/servlet/SerlvetHeadersMapAdapter.java
diff --git a/twelvemonkeys-servlet/src/main/java/com/twelvemonkeys/servlet/SerlvetParametersMapAdapter.java b/servlet/src/main/java/com/twelvemonkeys/servlet/SerlvetParametersMapAdapter.java
similarity index 100%
rename from twelvemonkeys-servlet/src/main/java/com/twelvemonkeys/servlet/SerlvetParametersMapAdapter.java
rename to servlet/src/main/java/com/twelvemonkeys/servlet/SerlvetParametersMapAdapter.java
diff --git a/twelvemonkeys-servlet/src/main/java/com/twelvemonkeys/servlet/ServletConfigException.java b/servlet/src/main/java/com/twelvemonkeys/servlet/ServletConfigException.java
similarity index 100%
rename from twelvemonkeys-servlet/src/main/java/com/twelvemonkeys/servlet/ServletConfigException.java
rename to servlet/src/main/java/com/twelvemonkeys/servlet/ServletConfigException.java
diff --git a/twelvemonkeys-servlet/src/main/java/com/twelvemonkeys/servlet/ServletConfigMapAdapter.java b/servlet/src/main/java/com/twelvemonkeys/servlet/ServletConfigMapAdapter.java
similarity index 100%
rename from twelvemonkeys-servlet/src/main/java/com/twelvemonkeys/servlet/ServletConfigMapAdapter.java
rename to servlet/src/main/java/com/twelvemonkeys/servlet/ServletConfigMapAdapter.java
diff --git a/twelvemonkeys-servlet/src/main/java/com/twelvemonkeys/servlet/ServletResponseStreamDelegate.java b/servlet/src/main/java/com/twelvemonkeys/servlet/ServletResponseStreamDelegate.java
similarity index 100%
rename from twelvemonkeys-servlet/src/main/java/com/twelvemonkeys/servlet/ServletResponseStreamDelegate.java
rename to servlet/src/main/java/com/twelvemonkeys/servlet/ServletResponseStreamDelegate.java
diff --git a/twelvemonkeys-servlet/src/main/java/com/twelvemonkeys/servlet/ServletUtil.java b/servlet/src/main/java/com/twelvemonkeys/servlet/ServletUtil.java
similarity index 100%
rename from twelvemonkeys-servlet/src/main/java/com/twelvemonkeys/servlet/ServletUtil.java
rename to servlet/src/main/java/com/twelvemonkeys/servlet/ServletUtil.java
diff --git a/twelvemonkeys-servlet/src/main/java/com/twelvemonkeys/servlet/ThrottleFilter.java b/servlet/src/main/java/com/twelvemonkeys/servlet/ThrottleFilter.java
similarity index 100%
rename from twelvemonkeys-servlet/src/main/java/com/twelvemonkeys/servlet/ThrottleFilter.java
rename to servlet/src/main/java/com/twelvemonkeys/servlet/ThrottleFilter.java
diff --git a/twelvemonkeys-servlet/src/main/java/com/twelvemonkeys/servlet/TimingFilter.java b/servlet/src/main/java/com/twelvemonkeys/servlet/TimingFilter.java
similarity index 100%
rename from twelvemonkeys-servlet/src/main/java/com/twelvemonkeys/servlet/TimingFilter.java
rename to servlet/src/main/java/com/twelvemonkeys/servlet/TimingFilter.java
diff --git a/twelvemonkeys-servlet/src/main/java/com/twelvemonkeys/servlet/TrimWhiteSpaceFilter.java b/servlet/src/main/java/com/twelvemonkeys/servlet/TrimWhiteSpaceFilter.java
similarity index 100%
rename from twelvemonkeys-servlet/src/main/java/com/twelvemonkeys/servlet/TrimWhiteSpaceFilter.java
rename to servlet/src/main/java/com/twelvemonkeys/servlet/TrimWhiteSpaceFilter.java
diff --git a/twelvemonkeys-servlet/src/main/java/com/twelvemonkeys/servlet/cache/AbstractCacheRequest.java b/servlet/src/main/java/com/twelvemonkeys/servlet/cache/AbstractCacheRequest.java
similarity index 100%
rename from twelvemonkeys-servlet/src/main/java/com/twelvemonkeys/servlet/cache/AbstractCacheRequest.java
rename to servlet/src/main/java/com/twelvemonkeys/servlet/cache/AbstractCacheRequest.java
diff --git a/twelvemonkeys-servlet/src/main/java/com/twelvemonkeys/servlet/cache/AbstractCacheResponse.java b/servlet/src/main/java/com/twelvemonkeys/servlet/cache/AbstractCacheResponse.java
similarity index 100%
rename from twelvemonkeys-servlet/src/main/java/com/twelvemonkeys/servlet/cache/AbstractCacheResponse.java
rename to servlet/src/main/java/com/twelvemonkeys/servlet/cache/AbstractCacheResponse.java
diff --git a/twelvemonkeys-servlet/src/main/java/com/twelvemonkeys/servlet/cache/CacheException.java b/servlet/src/main/java/com/twelvemonkeys/servlet/cache/CacheException.java
similarity index 100%
rename from twelvemonkeys-servlet/src/main/java/com/twelvemonkeys/servlet/cache/CacheException.java
rename to servlet/src/main/java/com/twelvemonkeys/servlet/cache/CacheException.java
diff --git a/twelvemonkeys-servlet/src/main/java/com/twelvemonkeys/servlet/cache/CacheFilter.java b/servlet/src/main/java/com/twelvemonkeys/servlet/cache/CacheFilter.java
similarity index 100%
rename from twelvemonkeys-servlet/src/main/java/com/twelvemonkeys/servlet/cache/CacheFilter.java
rename to servlet/src/main/java/com/twelvemonkeys/servlet/cache/CacheFilter.java
diff --git a/twelvemonkeys-servlet/src/main/java/com/twelvemonkeys/servlet/cache/CacheRequest.java b/servlet/src/main/java/com/twelvemonkeys/servlet/cache/CacheRequest.java
similarity index 100%
rename from twelvemonkeys-servlet/src/main/java/com/twelvemonkeys/servlet/cache/CacheRequest.java
rename to servlet/src/main/java/com/twelvemonkeys/servlet/cache/CacheRequest.java
diff --git a/twelvemonkeys-servlet/src/main/java/com/twelvemonkeys/servlet/cache/CacheResponse.java b/servlet/src/main/java/com/twelvemonkeys/servlet/cache/CacheResponse.java
similarity index 100%
rename from twelvemonkeys-servlet/src/main/java/com/twelvemonkeys/servlet/cache/CacheResponse.java
rename to servlet/src/main/java/com/twelvemonkeys/servlet/cache/CacheResponse.java
diff --git a/twelvemonkeys-servlet/src/main/java/com/twelvemonkeys/servlet/cache/CacheResponseWrapper.java b/servlet/src/main/java/com/twelvemonkeys/servlet/cache/CacheResponseWrapper.java
similarity index 100%
rename from twelvemonkeys-servlet/src/main/java/com/twelvemonkeys/servlet/cache/CacheResponseWrapper.java
rename to servlet/src/main/java/com/twelvemonkeys/servlet/cache/CacheResponseWrapper.java
diff --git a/twelvemonkeys-servlet/src/main/java/com/twelvemonkeys/servlet/cache/CachedEntity.java b/servlet/src/main/java/com/twelvemonkeys/servlet/cache/CachedEntity.java
similarity index 100%
rename from twelvemonkeys-servlet/src/main/java/com/twelvemonkeys/servlet/cache/CachedEntity.java
rename to servlet/src/main/java/com/twelvemonkeys/servlet/cache/CachedEntity.java
diff --git a/twelvemonkeys-servlet/src/main/java/com/twelvemonkeys/servlet/cache/CachedEntityImpl.java b/servlet/src/main/java/com/twelvemonkeys/servlet/cache/CachedEntityImpl.java
similarity index 100%
rename from twelvemonkeys-servlet/src/main/java/com/twelvemonkeys/servlet/cache/CachedEntityImpl.java
rename to servlet/src/main/java/com/twelvemonkeys/servlet/cache/CachedEntityImpl.java
diff --git a/twelvemonkeys-servlet/src/main/java/com/twelvemonkeys/servlet/cache/CachedResponse.java b/servlet/src/main/java/com/twelvemonkeys/servlet/cache/CachedResponse.java
similarity index 100%
rename from twelvemonkeys-servlet/src/main/java/com/twelvemonkeys/servlet/cache/CachedResponse.java
rename to servlet/src/main/java/com/twelvemonkeys/servlet/cache/CachedResponse.java
diff --git a/twelvemonkeys-servlet/src/main/java/com/twelvemonkeys/servlet/cache/CachedResponseImpl.java b/servlet/src/main/java/com/twelvemonkeys/servlet/cache/CachedResponseImpl.java
similarity index 100%
rename from twelvemonkeys-servlet/src/main/java/com/twelvemonkeys/servlet/cache/CachedResponseImpl.java
rename to servlet/src/main/java/com/twelvemonkeys/servlet/cache/CachedResponseImpl.java
diff --git a/twelvemonkeys-servlet/src/main/java/com/twelvemonkeys/servlet/cache/ClientCacheRequest.java b/servlet/src/main/java/com/twelvemonkeys/servlet/cache/ClientCacheRequest.java
similarity index 100%
rename from twelvemonkeys-servlet/src/main/java/com/twelvemonkeys/servlet/cache/ClientCacheRequest.java
rename to servlet/src/main/java/com/twelvemonkeys/servlet/cache/ClientCacheRequest.java
diff --git a/twelvemonkeys-servlet/src/main/java/com/twelvemonkeys/servlet/cache/ClientCacheResponse.java b/servlet/src/main/java/com/twelvemonkeys/servlet/cache/ClientCacheResponse.java
similarity index 100%
rename from twelvemonkeys-servlet/src/main/java/com/twelvemonkeys/servlet/cache/ClientCacheResponse.java
rename to servlet/src/main/java/com/twelvemonkeys/servlet/cache/ClientCacheResponse.java
diff --git a/twelvemonkeys-servlet/src/main/java/com/twelvemonkeys/servlet/cache/HTTPCache.java b/servlet/src/main/java/com/twelvemonkeys/servlet/cache/HTTPCache.java
similarity index 100%
rename from twelvemonkeys-servlet/src/main/java/com/twelvemonkeys/servlet/cache/HTTPCache.java
rename to servlet/src/main/java/com/twelvemonkeys/servlet/cache/HTTPCache.java
diff --git a/twelvemonkeys-servlet/src/main/java/com/twelvemonkeys/servlet/cache/ResponseResolver.java b/servlet/src/main/java/com/twelvemonkeys/servlet/cache/ResponseResolver.java
similarity index 100%
rename from twelvemonkeys-servlet/src/main/java/com/twelvemonkeys/servlet/cache/ResponseResolver.java
rename to servlet/src/main/java/com/twelvemonkeys/servlet/cache/ResponseResolver.java
diff --git a/twelvemonkeys-servlet/src/main/java/com/twelvemonkeys/servlet/cache/SerlvetCacheResponseWrapper.java b/servlet/src/main/java/com/twelvemonkeys/servlet/cache/SerlvetCacheResponseWrapper.java
similarity index 100%
rename from twelvemonkeys-servlet/src/main/java/com/twelvemonkeys/servlet/cache/SerlvetCacheResponseWrapper.java
rename to servlet/src/main/java/com/twelvemonkeys/servlet/cache/SerlvetCacheResponseWrapper.java
diff --git a/twelvemonkeys-servlet/src/main/java/com/twelvemonkeys/servlet/cache/ServletCacheRequest.java b/servlet/src/main/java/com/twelvemonkeys/servlet/cache/ServletCacheRequest.java
similarity index 100%
rename from twelvemonkeys-servlet/src/main/java/com/twelvemonkeys/servlet/cache/ServletCacheRequest.java
rename to servlet/src/main/java/com/twelvemonkeys/servlet/cache/ServletCacheRequest.java
diff --git a/twelvemonkeys-servlet/src/main/java/com/twelvemonkeys/servlet/cache/ServletCacheResponse.java b/servlet/src/main/java/com/twelvemonkeys/servlet/cache/ServletCacheResponse.java
similarity index 100%
rename from twelvemonkeys-servlet/src/main/java/com/twelvemonkeys/servlet/cache/ServletCacheResponse.java
rename to servlet/src/main/java/com/twelvemonkeys/servlet/cache/ServletCacheResponse.java
diff --git a/twelvemonkeys-servlet/src/main/java/com/twelvemonkeys/servlet/cache/ServletResponseResolver.java b/servlet/src/main/java/com/twelvemonkeys/servlet/cache/ServletResponseResolver.java
similarity index 100%
rename from twelvemonkeys-servlet/src/main/java/com/twelvemonkeys/servlet/cache/ServletResponseResolver.java
rename to servlet/src/main/java/com/twelvemonkeys/servlet/cache/ServletResponseResolver.java
diff --git a/twelvemonkeys-servlet/src/main/java/com/twelvemonkeys/servlet/cache/WritableCachedResponse.java b/servlet/src/main/java/com/twelvemonkeys/servlet/cache/WritableCachedResponse.java
similarity index 100%
rename from twelvemonkeys-servlet/src/main/java/com/twelvemonkeys/servlet/cache/WritableCachedResponse.java
rename to servlet/src/main/java/com/twelvemonkeys/servlet/cache/WritableCachedResponse.java
diff --git a/twelvemonkeys-servlet/src/main/java/com/twelvemonkeys/servlet/cache/WritableCachedResponseImpl.java b/servlet/src/main/java/com/twelvemonkeys/servlet/cache/WritableCachedResponseImpl.java
similarity index 100%
rename from twelvemonkeys-servlet/src/main/java/com/twelvemonkeys/servlet/cache/WritableCachedResponseImpl.java
rename to servlet/src/main/java/com/twelvemonkeys/servlet/cache/WritableCachedResponseImpl.java
diff --git a/twelvemonkeys-servlet/src/main/java/com/twelvemonkeys/servlet/cache/todo.txt b/servlet/src/main/java/com/twelvemonkeys/servlet/cache/todo.txt
similarity index 100%
rename from twelvemonkeys-servlet/src/main/java/com/twelvemonkeys/servlet/cache/todo.txt
rename to servlet/src/main/java/com/twelvemonkeys/servlet/cache/todo.txt
diff --git a/twelvemonkeys-servlet/src/main/java/com/twelvemonkeys/servlet/fileupload/FileSizeExceededException.java b/servlet/src/main/java/com/twelvemonkeys/servlet/fileupload/FileSizeExceededException.java
similarity index 100%
rename from twelvemonkeys-servlet/src/main/java/com/twelvemonkeys/servlet/fileupload/FileSizeExceededException.java
rename to servlet/src/main/java/com/twelvemonkeys/servlet/fileupload/FileSizeExceededException.java
diff --git a/twelvemonkeys-servlet/src/main/java/com/twelvemonkeys/servlet/fileupload/FileUploadException.java b/servlet/src/main/java/com/twelvemonkeys/servlet/fileupload/FileUploadException.java
similarity index 100%
rename from twelvemonkeys-servlet/src/main/java/com/twelvemonkeys/servlet/fileupload/FileUploadException.java
rename to servlet/src/main/java/com/twelvemonkeys/servlet/fileupload/FileUploadException.java
diff --git a/twelvemonkeys-servlet/src/main/java/com/twelvemonkeys/servlet/fileupload/FileUploadFilter.java b/servlet/src/main/java/com/twelvemonkeys/servlet/fileupload/FileUploadFilter.java
similarity index 100%
rename from twelvemonkeys-servlet/src/main/java/com/twelvemonkeys/servlet/fileupload/FileUploadFilter.java
rename to servlet/src/main/java/com/twelvemonkeys/servlet/fileupload/FileUploadFilter.java
diff --git a/twelvemonkeys-servlet/src/main/java/com/twelvemonkeys/servlet/fileupload/HttpFileUploadRequest.java b/servlet/src/main/java/com/twelvemonkeys/servlet/fileupload/HttpFileUploadRequest.java
similarity index 100%
rename from twelvemonkeys-servlet/src/main/java/com/twelvemonkeys/servlet/fileupload/HttpFileUploadRequest.java
rename to servlet/src/main/java/com/twelvemonkeys/servlet/fileupload/HttpFileUploadRequest.java
diff --git a/twelvemonkeys-servlet/src/main/java/com/twelvemonkeys/servlet/fileupload/HttpFileUploadRequestWrapper.java b/servlet/src/main/java/com/twelvemonkeys/servlet/fileupload/HttpFileUploadRequestWrapper.java
similarity index 100%
rename from twelvemonkeys-servlet/src/main/java/com/twelvemonkeys/servlet/fileupload/HttpFileUploadRequestWrapper.java
rename to servlet/src/main/java/com/twelvemonkeys/servlet/fileupload/HttpFileUploadRequestWrapper.java
diff --git a/twelvemonkeys-servlet/src/main/java/com/twelvemonkeys/servlet/fileupload/UploadedFile.java b/servlet/src/main/java/com/twelvemonkeys/servlet/fileupload/UploadedFile.java
similarity index 100%
rename from twelvemonkeys-servlet/src/main/java/com/twelvemonkeys/servlet/fileupload/UploadedFile.java
rename to servlet/src/main/java/com/twelvemonkeys/servlet/fileupload/UploadedFile.java
diff --git a/twelvemonkeys-servlet/src/main/java/com/twelvemonkeys/servlet/fileupload/UploadedFileImpl.java b/servlet/src/main/java/com/twelvemonkeys/servlet/fileupload/UploadedFileImpl.java
similarity index 100%
rename from twelvemonkeys-servlet/src/main/java/com/twelvemonkeys/servlet/fileupload/UploadedFileImpl.java
rename to servlet/src/main/java/com/twelvemonkeys/servlet/fileupload/UploadedFileImpl.java
diff --git a/twelvemonkeys-servlet/src/main/java/com/twelvemonkeys/servlet/gzip/GZIPFilter.java b/servlet/src/main/java/com/twelvemonkeys/servlet/gzip/GZIPFilter.java
similarity index 100%
rename from twelvemonkeys-servlet/src/main/java/com/twelvemonkeys/servlet/gzip/GZIPFilter.java
rename to servlet/src/main/java/com/twelvemonkeys/servlet/gzip/GZIPFilter.java
diff --git a/twelvemonkeys-servlet/src/main/java/com/twelvemonkeys/servlet/gzip/GZIPResponseWrapper.java b/servlet/src/main/java/com/twelvemonkeys/servlet/gzip/GZIPResponseWrapper.java
similarity index 100%
rename from twelvemonkeys-servlet/src/main/java/com/twelvemonkeys/servlet/gzip/GZIPResponseWrapper.java
rename to servlet/src/main/java/com/twelvemonkeys/servlet/gzip/GZIPResponseWrapper.java
diff --git a/twelvemonkeys-servlet/src/main/java/com/twelvemonkeys/servlet/image/AWTImageFilterAdapter.java b/servlet/src/main/java/com/twelvemonkeys/servlet/image/AWTImageFilterAdapter.java
similarity index 100%
rename from twelvemonkeys-servlet/src/main/java/com/twelvemonkeys/servlet/image/AWTImageFilterAdapter.java
rename to servlet/src/main/java/com/twelvemonkeys/servlet/image/AWTImageFilterAdapter.java
diff --git a/twelvemonkeys-servlet/src/main/java/com/twelvemonkeys/servlet/image/BufferedImageOpAdapter.java b/servlet/src/main/java/com/twelvemonkeys/servlet/image/BufferedImageOpAdapter.java
similarity index 100%
rename from twelvemonkeys-servlet/src/main/java/com/twelvemonkeys/servlet/image/BufferedImageOpAdapter.java
rename to servlet/src/main/java/com/twelvemonkeys/servlet/image/BufferedImageOpAdapter.java
diff --git a/twelvemonkeys-servlet/src/main/java/com/twelvemonkeys/servlet/image/ColorServlet.java b/servlet/src/main/java/com/twelvemonkeys/servlet/image/ColorServlet.java
similarity index 100%
rename from twelvemonkeys-servlet/src/main/java/com/twelvemonkeys/servlet/image/ColorServlet.java
rename to servlet/src/main/java/com/twelvemonkeys/servlet/image/ColorServlet.java
diff --git a/twelvemonkeys-servlet/src/main/java/com/twelvemonkeys/servlet/image/ComposeFilter.java b/servlet/src/main/java/com/twelvemonkeys/servlet/image/ComposeFilter.java
similarity index 100%
rename from twelvemonkeys-servlet/src/main/java/com/twelvemonkeys/servlet/image/ComposeFilter.java
rename to servlet/src/main/java/com/twelvemonkeys/servlet/image/ComposeFilter.java
diff --git a/twelvemonkeys-servlet/src/main/java/com/twelvemonkeys/servlet/image/ContentNegotiationFilter.java b/servlet/src/main/java/com/twelvemonkeys/servlet/image/ContentNegotiationFilter.java
similarity index 100%
rename from twelvemonkeys-servlet/src/main/java/com/twelvemonkeys/servlet/image/ContentNegotiationFilter.java
rename to servlet/src/main/java/com/twelvemonkeys/servlet/image/ContentNegotiationFilter.java
diff --git a/twelvemonkeys-servlet/src/main/java/com/twelvemonkeys/servlet/image/CropFilter.java b/servlet/src/main/java/com/twelvemonkeys/servlet/image/CropFilter.java
similarity index 100%
rename from twelvemonkeys-servlet/src/main/java/com/twelvemonkeys/servlet/image/CropFilter.java
rename to servlet/src/main/java/com/twelvemonkeys/servlet/image/CropFilter.java
diff --git a/twelvemonkeys-servlet/src/main/java/com/twelvemonkeys/servlet/image/ImageFilter.java b/servlet/src/main/java/com/twelvemonkeys/servlet/image/ImageFilter.java
similarity index 100%
rename from twelvemonkeys-servlet/src/main/java/com/twelvemonkeys/servlet/image/ImageFilter.java
rename to servlet/src/main/java/com/twelvemonkeys/servlet/image/ImageFilter.java
diff --git a/twelvemonkeys-servlet/src/main/java/com/twelvemonkeys/servlet/image/ImageServletException.java b/servlet/src/main/java/com/twelvemonkeys/servlet/image/ImageServletException.java
similarity index 100%
rename from twelvemonkeys-servlet/src/main/java/com/twelvemonkeys/servlet/image/ImageServletException.java
rename to servlet/src/main/java/com/twelvemonkeys/servlet/image/ImageServletException.java
diff --git a/twelvemonkeys-servlet/src/main/java/com/twelvemonkeys/servlet/image/ImageServletResponse.java b/servlet/src/main/java/com/twelvemonkeys/servlet/image/ImageServletResponse.java
similarity index 100%
rename from twelvemonkeys-servlet/src/main/java/com/twelvemonkeys/servlet/image/ImageServletResponse.java
rename to servlet/src/main/java/com/twelvemonkeys/servlet/image/ImageServletResponse.java
diff --git a/twelvemonkeys-servlet/src/main/java/com/twelvemonkeys/servlet/image/ImageServletResponseImpl.java b/servlet/src/main/java/com/twelvemonkeys/servlet/image/ImageServletResponseImpl.java
similarity index 100%
rename from twelvemonkeys-servlet/src/main/java/com/twelvemonkeys/servlet/image/ImageServletResponseImpl.java
rename to servlet/src/main/java/com/twelvemonkeys/servlet/image/ImageServletResponseImpl.java
diff --git a/twelvemonkeys-servlet/src/main/java/com/twelvemonkeys/servlet/image/NullImageFilter.java b/servlet/src/main/java/com/twelvemonkeys/servlet/image/NullImageFilter.java
similarity index 100%
rename from twelvemonkeys-servlet/src/main/java/com/twelvemonkeys/servlet/image/NullImageFilter.java
rename to servlet/src/main/java/com/twelvemonkeys/servlet/image/NullImageFilter.java
diff --git a/twelvemonkeys-servlet/src/main/java/com/twelvemonkeys/servlet/image/RotateFilter.java b/servlet/src/main/java/com/twelvemonkeys/servlet/image/RotateFilter.java
similarity index 100%
rename from twelvemonkeys-servlet/src/main/java/com/twelvemonkeys/servlet/image/RotateFilter.java
rename to servlet/src/main/java/com/twelvemonkeys/servlet/image/RotateFilter.java
diff --git a/twelvemonkeys-servlet/src/main/java/com/twelvemonkeys/servlet/image/ScaleFilter.java b/servlet/src/main/java/com/twelvemonkeys/servlet/image/ScaleFilter.java
similarity index 100%
rename from twelvemonkeys-servlet/src/main/java/com/twelvemonkeys/servlet/image/ScaleFilter.java
rename to servlet/src/main/java/com/twelvemonkeys/servlet/image/ScaleFilter.java
diff --git a/twelvemonkeys-servlet/src/main/java/com/twelvemonkeys/servlet/image/SourceRenderFilter.java b/servlet/src/main/java/com/twelvemonkeys/servlet/image/SourceRenderFilter.java
similarity index 100%
rename from twelvemonkeys-servlet/src/main/java/com/twelvemonkeys/servlet/image/SourceRenderFilter.java
rename to servlet/src/main/java/com/twelvemonkeys/servlet/image/SourceRenderFilter.java
diff --git a/twelvemonkeys-servlet/src/main/java/com/twelvemonkeys/servlet/image/TextRenderer.java b/servlet/src/main/java/com/twelvemonkeys/servlet/image/TextRenderer.java
similarity index 100%
rename from twelvemonkeys-servlet/src/main/java/com/twelvemonkeys/servlet/image/TextRenderer.java
rename to servlet/src/main/java/com/twelvemonkeys/servlet/image/TextRenderer.java
diff --git a/twelvemonkeys-servlet/src/main/java/com/twelvemonkeys/servlet/image/package_info.java b/servlet/src/main/java/com/twelvemonkeys/servlet/image/package_info.java
similarity index 100%
rename from twelvemonkeys-servlet/src/main/java/com/twelvemonkeys/servlet/image/package_info.java
rename to servlet/src/main/java/com/twelvemonkeys/servlet/image/package_info.java
diff --git a/twelvemonkeys-servlet/src/main/java/com/twelvemonkeys/servlet/jsp/droplet/Droplet.java b/servlet/src/main/java/com/twelvemonkeys/servlet/jsp/droplet/Droplet.java
similarity index 100%
rename from twelvemonkeys-servlet/src/main/java/com/twelvemonkeys/servlet/jsp/droplet/Droplet.java
rename to servlet/src/main/java/com/twelvemonkeys/servlet/jsp/droplet/Droplet.java
diff --git a/twelvemonkeys-servlet/src/main/java/com/twelvemonkeys/servlet/jsp/droplet/JspFragment.java b/servlet/src/main/java/com/twelvemonkeys/servlet/jsp/droplet/JspFragment.java
similarity index 100%
rename from twelvemonkeys-servlet/src/main/java/com/twelvemonkeys/servlet/jsp/droplet/JspFragment.java
rename to servlet/src/main/java/com/twelvemonkeys/servlet/jsp/droplet/JspFragment.java
diff --git a/twelvemonkeys-servlet/src/main/java/com/twelvemonkeys/servlet/jsp/droplet/Oparam.java b/servlet/src/main/java/com/twelvemonkeys/servlet/jsp/droplet/Oparam.java
similarity index 100%
rename from twelvemonkeys-servlet/src/main/java/com/twelvemonkeys/servlet/jsp/droplet/Oparam.java
rename to servlet/src/main/java/com/twelvemonkeys/servlet/jsp/droplet/Oparam.java
diff --git a/twelvemonkeys-servlet/src/main/java/com/twelvemonkeys/servlet/jsp/droplet/Param.java b/servlet/src/main/java/com/twelvemonkeys/servlet/jsp/droplet/Param.java
similarity index 100%
rename from twelvemonkeys-servlet/src/main/java/com/twelvemonkeys/servlet/jsp/droplet/Param.java
rename to servlet/src/main/java/com/twelvemonkeys/servlet/jsp/droplet/Param.java
diff --git a/twelvemonkeys-servlet/src/main/java/com/twelvemonkeys/servlet/jsp/droplet/package_info.java b/servlet/src/main/java/com/twelvemonkeys/servlet/jsp/droplet/package_info.java
similarity index 100%
rename from twelvemonkeys-servlet/src/main/java/com/twelvemonkeys/servlet/jsp/droplet/package_info.java
rename to servlet/src/main/java/com/twelvemonkeys/servlet/jsp/droplet/package_info.java
diff --git a/twelvemonkeys-servlet/src/main/java/com/twelvemonkeys/servlet/jsp/droplet/taglib/IncludeTag.java b/servlet/src/main/java/com/twelvemonkeys/servlet/jsp/droplet/taglib/IncludeTag.java
similarity index 100%
rename from twelvemonkeys-servlet/src/main/java/com/twelvemonkeys/servlet/jsp/droplet/taglib/IncludeTag.java
rename to servlet/src/main/java/com/twelvemonkeys/servlet/jsp/droplet/taglib/IncludeTag.java
diff --git a/twelvemonkeys-servlet/src/main/java/com/twelvemonkeys/servlet/jsp/droplet/taglib/NestingHandler.java b/servlet/src/main/java/com/twelvemonkeys/servlet/jsp/droplet/taglib/NestingHandler.java
similarity index 100%
rename from twelvemonkeys-servlet/src/main/java/com/twelvemonkeys/servlet/jsp/droplet/taglib/NestingHandler.java
rename to servlet/src/main/java/com/twelvemonkeys/servlet/jsp/droplet/taglib/NestingHandler.java
diff --git a/twelvemonkeys-servlet/src/main/java/com/twelvemonkeys/servlet/jsp/droplet/taglib/NestingValidator.java b/servlet/src/main/java/com/twelvemonkeys/servlet/jsp/droplet/taglib/NestingValidator.java
similarity index 100%
rename from twelvemonkeys-servlet/src/main/java/com/twelvemonkeys/servlet/jsp/droplet/taglib/NestingValidator.java
rename to servlet/src/main/java/com/twelvemonkeys/servlet/jsp/droplet/taglib/NestingValidator.java
diff --git a/twelvemonkeys-servlet/src/main/java/com/twelvemonkeys/servlet/jsp/droplet/taglib/OparamTag.java b/servlet/src/main/java/com/twelvemonkeys/servlet/jsp/droplet/taglib/OparamTag.java
similarity index 100%
rename from twelvemonkeys-servlet/src/main/java/com/twelvemonkeys/servlet/jsp/droplet/taglib/OparamTag.java
rename to servlet/src/main/java/com/twelvemonkeys/servlet/jsp/droplet/taglib/OparamTag.java
diff --git a/twelvemonkeys-servlet/src/main/java/com/twelvemonkeys/servlet/jsp/droplet/taglib/ParamTag.java b/servlet/src/main/java/com/twelvemonkeys/servlet/jsp/droplet/taglib/ParamTag.java
similarity index 100%
rename from twelvemonkeys-servlet/src/main/java/com/twelvemonkeys/servlet/jsp/droplet/taglib/ParamTag.java
rename to servlet/src/main/java/com/twelvemonkeys/servlet/jsp/droplet/taglib/ParamTag.java
diff --git a/twelvemonkeys-servlet/src/main/java/com/twelvemonkeys/servlet/jsp/droplet/taglib/ValueOfTEI.java b/servlet/src/main/java/com/twelvemonkeys/servlet/jsp/droplet/taglib/ValueOfTEI.java
similarity index 100%
rename from twelvemonkeys-servlet/src/main/java/com/twelvemonkeys/servlet/jsp/droplet/taglib/ValueOfTEI.java
rename to servlet/src/main/java/com/twelvemonkeys/servlet/jsp/droplet/taglib/ValueOfTEI.java
diff --git a/twelvemonkeys-servlet/src/main/java/com/twelvemonkeys/servlet/jsp/droplet/taglib/ValueOfTag.java b/servlet/src/main/java/com/twelvemonkeys/servlet/jsp/droplet/taglib/ValueOfTag.java
similarity index 100%
rename from twelvemonkeys-servlet/src/main/java/com/twelvemonkeys/servlet/jsp/droplet/taglib/ValueOfTag.java
rename to servlet/src/main/java/com/twelvemonkeys/servlet/jsp/droplet/taglib/ValueOfTag.java
diff --git a/twelvemonkeys-servlet/src/main/java/com/twelvemonkeys/servlet/jsp/droplet/taglib/package.html b/servlet/src/main/java/com/twelvemonkeys/servlet/jsp/droplet/taglib/package.html
similarity index 100%
rename from twelvemonkeys-servlet/src/main/java/com/twelvemonkeys/servlet/jsp/droplet/taglib/package.html
rename to servlet/src/main/java/com/twelvemonkeys/servlet/jsp/droplet/taglib/package.html
diff --git a/twelvemonkeys-servlet/src/main/java/com/twelvemonkeys/servlet/jsp/package_info.java b/servlet/src/main/java/com/twelvemonkeys/servlet/jsp/package_info.java
similarity index 100%
rename from twelvemonkeys-servlet/src/main/java/com/twelvemonkeys/servlet/jsp/package_info.java
rename to servlet/src/main/java/com/twelvemonkeys/servlet/jsp/package_info.java
diff --git a/twelvemonkeys-servlet/src/main/java/com/twelvemonkeys/servlet/jsp/taglib/BodyReaderTag.java b/servlet/src/main/java/com/twelvemonkeys/servlet/jsp/taglib/BodyReaderTag.java
similarity index 100%
rename from twelvemonkeys-servlet/src/main/java/com/twelvemonkeys/servlet/jsp/taglib/BodyReaderTag.java
rename to servlet/src/main/java/com/twelvemonkeys/servlet/jsp/taglib/BodyReaderTag.java
diff --git a/twelvemonkeys-servlet/src/main/java/com/twelvemonkeys/servlet/jsp/taglib/CSVToTableTag.java b/servlet/src/main/java/com/twelvemonkeys/servlet/jsp/taglib/CSVToTableTag.java
similarity index 100%
rename from twelvemonkeys-servlet/src/main/java/com/twelvemonkeys/servlet/jsp/taglib/CSVToTableTag.java
rename to servlet/src/main/java/com/twelvemonkeys/servlet/jsp/taglib/CSVToTableTag.java
diff --git a/twelvemonkeys-servlet/src/main/java/com/twelvemonkeys/servlet/jsp/taglib/ExBodyTagSupport.java b/servlet/src/main/java/com/twelvemonkeys/servlet/jsp/taglib/ExBodyTagSupport.java
similarity index 100%
rename from twelvemonkeys-servlet/src/main/java/com/twelvemonkeys/servlet/jsp/taglib/ExBodyTagSupport.java
rename to servlet/src/main/java/com/twelvemonkeys/servlet/jsp/taglib/ExBodyTagSupport.java
diff --git a/twelvemonkeys-servlet/src/main/java/com/twelvemonkeys/servlet/jsp/taglib/ExTag.java b/servlet/src/main/java/com/twelvemonkeys/servlet/jsp/taglib/ExTag.java
similarity index 100%
rename from twelvemonkeys-servlet/src/main/java/com/twelvemonkeys/servlet/jsp/taglib/ExTag.java
rename to servlet/src/main/java/com/twelvemonkeys/servlet/jsp/taglib/ExTag.java
diff --git a/twelvemonkeys-servlet/src/main/java/com/twelvemonkeys/servlet/jsp/taglib/ExTagSupport.java b/servlet/src/main/java/com/twelvemonkeys/servlet/jsp/taglib/ExTagSupport.java
similarity index 100%
rename from twelvemonkeys-servlet/src/main/java/com/twelvemonkeys/servlet/jsp/taglib/ExTagSupport.java
rename to servlet/src/main/java/com/twelvemonkeys/servlet/jsp/taglib/ExTagSupport.java
diff --git a/twelvemonkeys-servlet/src/main/java/com/twelvemonkeys/servlet/jsp/taglib/LastModifiedTEI.java b/servlet/src/main/java/com/twelvemonkeys/servlet/jsp/taglib/LastModifiedTEI.java
similarity index 100%
rename from twelvemonkeys-servlet/src/main/java/com/twelvemonkeys/servlet/jsp/taglib/LastModifiedTEI.java
rename to servlet/src/main/java/com/twelvemonkeys/servlet/jsp/taglib/LastModifiedTEI.java
diff --git a/twelvemonkeys-servlet/src/main/java/com/twelvemonkeys/servlet/jsp/taglib/LastModifiedTag.java b/servlet/src/main/java/com/twelvemonkeys/servlet/jsp/taglib/LastModifiedTag.java
similarity index 100%
rename from twelvemonkeys-servlet/src/main/java/com/twelvemonkeys/servlet/jsp/taglib/LastModifiedTag.java
rename to servlet/src/main/java/com/twelvemonkeys/servlet/jsp/taglib/LastModifiedTag.java
diff --git a/twelvemonkeys-servlet/src/main/java/com/twelvemonkeys/servlet/jsp/taglib/TrimWhiteSpaceTag.java b/servlet/src/main/java/com/twelvemonkeys/servlet/jsp/taglib/TrimWhiteSpaceTag.java
similarity index 100%
rename from twelvemonkeys-servlet/src/main/java/com/twelvemonkeys/servlet/jsp/taglib/TrimWhiteSpaceTag.java
rename to servlet/src/main/java/com/twelvemonkeys/servlet/jsp/taglib/TrimWhiteSpaceTag.java
diff --git a/twelvemonkeys-servlet/src/main/java/com/twelvemonkeys/servlet/jsp/taglib/XMLTransformTag.java b/servlet/src/main/java/com/twelvemonkeys/servlet/jsp/taglib/XMLTransformTag.java
similarity index 100%
rename from twelvemonkeys-servlet/src/main/java/com/twelvemonkeys/servlet/jsp/taglib/XMLTransformTag.java
rename to servlet/src/main/java/com/twelvemonkeys/servlet/jsp/taglib/XMLTransformTag.java
diff --git a/twelvemonkeys-servlet/src/main/java/com/twelvemonkeys/servlet/jsp/taglib/logic/ConditionalTagBase.java b/servlet/src/main/java/com/twelvemonkeys/servlet/jsp/taglib/logic/ConditionalTagBase.java
similarity index 100%
rename from twelvemonkeys-servlet/src/main/java/com/twelvemonkeys/servlet/jsp/taglib/logic/ConditionalTagBase.java
rename to servlet/src/main/java/com/twelvemonkeys/servlet/jsp/taglib/logic/ConditionalTagBase.java
diff --git a/twelvemonkeys-servlet/src/main/java/com/twelvemonkeys/servlet/jsp/taglib/logic/EqualTag.java b/servlet/src/main/java/com/twelvemonkeys/servlet/jsp/taglib/logic/EqualTag.java
similarity index 100%
rename from twelvemonkeys-servlet/src/main/java/com/twelvemonkeys/servlet/jsp/taglib/logic/EqualTag.java
rename to servlet/src/main/java/com/twelvemonkeys/servlet/jsp/taglib/logic/EqualTag.java
diff --git a/twelvemonkeys-servlet/src/main/java/com/twelvemonkeys/servlet/jsp/taglib/logic/IteratorProviderTEI.java b/servlet/src/main/java/com/twelvemonkeys/servlet/jsp/taglib/logic/IteratorProviderTEI.java
similarity index 100%
rename from twelvemonkeys-servlet/src/main/java/com/twelvemonkeys/servlet/jsp/taglib/logic/IteratorProviderTEI.java
rename to servlet/src/main/java/com/twelvemonkeys/servlet/jsp/taglib/logic/IteratorProviderTEI.java
diff --git a/twelvemonkeys-servlet/src/main/java/com/twelvemonkeys/servlet/jsp/taglib/logic/IteratorProviderTag.java b/servlet/src/main/java/com/twelvemonkeys/servlet/jsp/taglib/logic/IteratorProviderTag.java
similarity index 100%
rename from twelvemonkeys-servlet/src/main/java/com/twelvemonkeys/servlet/jsp/taglib/logic/IteratorProviderTag.java
rename to servlet/src/main/java/com/twelvemonkeys/servlet/jsp/taglib/logic/IteratorProviderTag.java
diff --git a/twelvemonkeys-servlet/src/main/java/com/twelvemonkeys/servlet/jsp/taglib/logic/NotEqualTag.java b/servlet/src/main/java/com/twelvemonkeys/servlet/jsp/taglib/logic/NotEqualTag.java
similarity index 100%
rename from twelvemonkeys-servlet/src/main/java/com/twelvemonkeys/servlet/jsp/taglib/logic/NotEqualTag.java
rename to servlet/src/main/java/com/twelvemonkeys/servlet/jsp/taglib/logic/NotEqualTag.java
diff --git a/twelvemonkeys-servlet/src/main/java/com/twelvemonkeys/servlet/jsp/taglib/package_info.java b/servlet/src/main/java/com/twelvemonkeys/servlet/jsp/taglib/package_info.java
similarity index 100%
rename from twelvemonkeys-servlet/src/main/java/com/twelvemonkeys/servlet/jsp/taglib/package_info.java
rename to servlet/src/main/java/com/twelvemonkeys/servlet/jsp/taglib/package_info.java
diff --git a/twelvemonkeys-servlet/src/main/java/com/twelvemonkeys/servlet/log4j/Log4JContextWrapper.java b/servlet/src/main/java/com/twelvemonkeys/servlet/log4j/Log4JContextWrapper.java
similarity index 100%
rename from twelvemonkeys-servlet/src/main/java/com/twelvemonkeys/servlet/log4j/Log4JContextWrapper.java
rename to servlet/src/main/java/com/twelvemonkeys/servlet/log4j/Log4JContextWrapper.java
diff --git a/twelvemonkeys-servlet/src/main/java/com/twelvemonkeys/servlet/package_info.java b/servlet/src/main/java/com/twelvemonkeys/servlet/package_info.java
similarity index 100%
rename from twelvemonkeys-servlet/src/main/java/com/twelvemonkeys/servlet/package_info.java
rename to servlet/src/main/java/com/twelvemonkeys/servlet/package_info.java
diff --git a/twelvemonkeys-servlet/src/test/java/com/twelvemonkeys/servlet/FilterAbstractTestCase.java b/servlet/src/test/java/com/twelvemonkeys/servlet/FilterAbstractTestCase.java
similarity index 100%
rename from twelvemonkeys-servlet/src/test/java/com/twelvemonkeys/servlet/FilterAbstractTestCase.java
rename to servlet/src/test/java/com/twelvemonkeys/servlet/FilterAbstractTestCase.java
diff --git a/twelvemonkeys-servlet/src/test/java/com/twelvemonkeys/servlet/GenericFilterTestCase.java b/servlet/src/test/java/com/twelvemonkeys/servlet/GenericFilterTestCase.java
similarity index 100%
rename from twelvemonkeys-servlet/src/test/java/com/twelvemonkeys/servlet/GenericFilterTestCase.java
rename to servlet/src/test/java/com/twelvemonkeys/servlet/GenericFilterTestCase.java
diff --git a/twelvemonkeys-servlet/src/test/java/com/twelvemonkeys/servlet/ServletConfigExceptionTestCase.java b/servlet/src/test/java/com/twelvemonkeys/servlet/ServletConfigExceptionTestCase.java
similarity index 100%
rename from twelvemonkeys-servlet/src/test/java/com/twelvemonkeys/servlet/ServletConfigExceptionTestCase.java
rename to servlet/src/test/java/com/twelvemonkeys/servlet/ServletConfigExceptionTestCase.java
diff --git a/twelvemonkeys-servlet/src/test/java/com/twelvemonkeys/servlet/ServletConfigMapAdapterTestCase.java b/servlet/src/test/java/com/twelvemonkeys/servlet/ServletConfigMapAdapterTestCase.java
similarity index 100%
rename from twelvemonkeys-servlet/src/test/java/com/twelvemonkeys/servlet/ServletConfigMapAdapterTestCase.java
rename to servlet/src/test/java/com/twelvemonkeys/servlet/ServletConfigMapAdapterTestCase.java
diff --git a/twelvemonkeys-servlet/src/test/java/com/twelvemonkeys/servlet/ServletHeadersMapAdapterTestCase.java b/servlet/src/test/java/com/twelvemonkeys/servlet/ServletHeadersMapAdapterTestCase.java
similarity index 100%
rename from twelvemonkeys-servlet/src/test/java/com/twelvemonkeys/servlet/ServletHeadersMapAdapterTestCase.java
rename to servlet/src/test/java/com/twelvemonkeys/servlet/ServletHeadersMapAdapterTestCase.java
diff --git a/twelvemonkeys-servlet/src/test/java/com/twelvemonkeys/servlet/ServletParametersMapAdapterTestCase.java b/servlet/src/test/java/com/twelvemonkeys/servlet/ServletParametersMapAdapterTestCase.java
similarity index 100%
rename from twelvemonkeys-servlet/src/test/java/com/twelvemonkeys/servlet/ServletParametersMapAdapterTestCase.java
rename to servlet/src/test/java/com/twelvemonkeys/servlet/ServletParametersMapAdapterTestCase.java
diff --git a/twelvemonkeys-servlet/src/test/java/com/twelvemonkeys/servlet/ServletResponseAbsrtactTestCase.java b/servlet/src/test/java/com/twelvemonkeys/servlet/ServletResponseAbsrtactTestCase.java
similarity index 100%
rename from twelvemonkeys-servlet/src/test/java/com/twelvemonkeys/servlet/ServletResponseAbsrtactTestCase.java
rename to servlet/src/test/java/com/twelvemonkeys/servlet/ServletResponseAbsrtactTestCase.java
diff --git a/twelvemonkeys-servlet/src/test/java/com/twelvemonkeys/servlet/TrimWhiteSpaceFilterTestCase.java b/servlet/src/test/java/com/twelvemonkeys/servlet/TrimWhiteSpaceFilterTestCase.java
similarity index 100%
rename from twelvemonkeys-servlet/src/test/java/com/twelvemonkeys/servlet/TrimWhiteSpaceFilterTestCase.java
rename to servlet/src/test/java/com/twelvemonkeys/servlet/TrimWhiteSpaceFilterTestCase.java
diff --git a/twelvemonkeys-servlet/src/test/java/com/twelvemonkeys/servlet/cache/HTTPCacheTestCase.java b/servlet/src/test/java/com/twelvemonkeys/servlet/cache/HTTPCacheTestCase.java
similarity index 100%
rename from twelvemonkeys-servlet/src/test/java/com/twelvemonkeys/servlet/cache/HTTPCacheTestCase.java
rename to servlet/src/test/java/com/twelvemonkeys/servlet/cache/HTTPCacheTestCase.java
diff --git a/twelvemonkeys-servlet/src/test/java/com/twelvemonkeys/servlet/image/ImageServletResponseImplTestCase.java b/servlet/src/test/java/com/twelvemonkeys/servlet/image/ImageServletResponseImplTestCase.java
similarity index 96%
rename from twelvemonkeys-servlet/src/test/java/com/twelvemonkeys/servlet/image/ImageServletResponseImplTestCase.java
rename to servlet/src/test/java/com/twelvemonkeys/servlet/image/ImageServletResponseImplTestCase.java
index 231ec787..fd5456c8 100755
--- a/twelvemonkeys-servlet/src/test/java/com/twelvemonkeys/servlet/image/ImageServletResponseImplTestCase.java
+++ b/servlet/src/test/java/com/twelvemonkeys/servlet/image/ImageServletResponseImplTestCase.java
@@ -1,5 +1,6 @@
package com.twelvemonkeys.servlet.image;
+import com.twelvemonkeys.image.ImageUtil;
import com.twelvemonkeys.io.FileUtil;
import com.twelvemonkeys.servlet.OutputStreamAdapter;
import org.jmock.Mock;
@@ -165,11 +166,31 @@ public class ImageServletResponseImplTestCase extends MockObjectTestCase {
assertTrue("Content has no data", out.size() > 0);
// Test that image data is still readable
+ /*
+ File tempFile = File.createTempFile("imageservlet-test-", ".jpeg");
+ FileOutputStream stream = new FileOutputStream(tempFile);
+ out.writeTo(stream);
+ stream.close();
+ System.err.println("open " + tempFile);
+ */
+
BufferedImage outImage = ImageIO.read(new ByteArrayInputStream(out.toByteArray()));
assertNotNull(outImage);
assertEquals(IMAGE_DIMENSION_PNG.width, outImage.getWidth());
assertEquals(IMAGE_DIMENSION_PNG.height, outImage.getHeight());
- assertSimilarImage(ImageIO.read(mContext.getResource("/" + IMAGE_NAME_PNG)), outImage, 96f);
+
+ BufferedImage image = flatten(ImageIO.read(mContext.getResource("/" + IMAGE_NAME_PNG)), Color.BLACK);
+
+ /*
+ tempFile = File.createTempFile("imageservlet-test-", ".png");
+ stream = new FileOutputStream(tempFile);
+ ImageIO.write(image, "PNG", stream);
+ stream.close();
+ System.err.println("open " + tempFile);
+ */
+
+ // JPEG compression trashes the image completely...
+ assertSimilarImage(image, outImage, 144f);
}
@Test
@@ -200,11 +221,46 @@ public class ImageServletResponseImplTestCase extends MockObjectTestCase {
assertTrue("Content has no data", out.size() > 0);
// Test that image data is still readable
+ /*
+ File tempFile = File.createTempFile("imageservlet-test-", ".jpeg");
+ FileOutputStream stream = new FileOutputStream(tempFile);
+ out.writeTo(stream);
+ stream.close();
+ System.err.println("open " + tempFile);
+ */
+
BufferedImage outImage = ImageIO.read(new ByteArrayInputStream(out.toByteArray()));
assertNotNull(outImage);
assertEquals(IMAGE_DIMENSION_GIF.width, outImage.getWidth());
assertEquals(IMAGE_DIMENSION_GIF.height, outImage.getHeight());
- assertSimilarImage(ImageIO.read(mContext.getResource("/" + IMAGE_NAME_GIF)), outImage, 96f);
+
+ BufferedImage image = flatten(ImageIO.read(mContext.getResource("/" + IMAGE_NAME_GIF)), Color.WHITE);
+
+ /*
+ tempFile = File.createTempFile("imageservlet-test-", ".png");
+ stream = new FileOutputStream(tempFile);
+ ImageIO.write(image, "PNG", stream);
+ stream.close();
+ System.err.println("open " + tempFile);
+ */
+
+ assertSimilarImage(image, outImage, 96f);
+ }
+
+ private static BufferedImage flatten(final BufferedImage pImage, final Color pBackgroundColor) {
+ BufferedImage image = ImageUtil.toBuffered(pImage, BufferedImage.TYPE_INT_ARGB);
+
+ Graphics2D g = image.createGraphics();
+ try {
+ g.setComposite(AlphaComposite.DstOver);
+ g.setColor(pBackgroundColor);
+ g.fillRect(0, 0, pImage.getWidth(), pImage.getHeight());
+ }
+ finally {
+ g.dispose();
+ }
+
+ return image;
}
/**
@@ -261,6 +317,7 @@ public class ImageServletResponseImplTestCase extends MockObjectTestCase {
assertNotNull(outImage);
assertEquals(image.getWidth(), outImage.getWidth());
assertEquals(image.getHeight(), outImage.getHeight());
+ assertSimilarImage(image, outImage, 0);
}
// TODO: Test with AOI attributes (rename thes to source-region?)
diff --git a/twelvemonkeys-servlet/src/test/resources/com/twelvemonkeys/servlet/image/12monkeys-splash.png b/servlet/src/test/resources/com/twelvemonkeys/servlet/image/12monkeys-splash.png
similarity index 100%
rename from twelvemonkeys-servlet/src/test/resources/com/twelvemonkeys/servlet/image/12monkeys-splash.png
rename to servlet/src/test/resources/com/twelvemonkeys/servlet/image/12monkeys-splash.png
diff --git a/twelvemonkeys-servlet/src/test/resources/com/twelvemonkeys/servlet/image/foo.txt b/servlet/src/test/resources/com/twelvemonkeys/servlet/image/foo.txt
similarity index 100%
rename from twelvemonkeys-servlet/src/test/resources/com/twelvemonkeys/servlet/image/foo.txt
rename to servlet/src/test/resources/com/twelvemonkeys/servlet/image/foo.txt
diff --git a/twelvemonkeys-servlet/src/test/resources/com/twelvemonkeys/servlet/image/tux.gif b/servlet/src/test/resources/com/twelvemonkeys/servlet/image/tux.gif
similarity index 100%
rename from twelvemonkeys-servlet/src/test/resources/com/twelvemonkeys/servlet/image/tux.gif
rename to servlet/src/test/resources/com/twelvemonkeys/servlet/image/tux.gif
diff --git a/twelvemonkeys-core/pom.xml b/twelvemonkeys-core/pom.xml
deleted file mode 100644
index 9ec80281..00000000
--- a/twelvemonkeys-core/pom.xml
+++ /dev/null
@@ -1,74 +0,0 @@
-
-
- 4.0.0
- twelvemonkeys-core
- com.twelvemonkeys
- 2.3-SNAPSHOT
- TwelveMonkeys Core
-
- The TwelveMonkeys Core library. Contains common utility classes.
-
-
-
- com.twelvemonkeys
- twelvemonkeys-parent
- 2.0
-
-
-
-
- jmagick
- jmagick
- 6.2.4
- provided
- true
-
-
-
- junit
- junit
- 4.3.1
- test
-
-
-
- jmock
- jmock-cglib
- 1.0.1
- test
-
-
-
-
-
-
- maven-source-plugin
-
-
-
- maven-resources-plugin
-
- UTF-8
-
-
-
-
- org.apache.maven.plugins
- maven-jar-plugin
- 2.2
-
-
-
- ${project.name}
- TwelveMonkeys
- ${project.version}
- http://github.com/haraldk/TwelveMonkeys
-
-
-
-
-
-
-
\ No newline at end of file
diff --git a/twelvemonkeys-core/todo.txt b/twelvemonkeys-core/todo.txt
deleted file mode 100644
index 42ea4984..00000000
--- a/twelvemonkeys-core/todo.txt
+++ /dev/null
@@ -1,21 +0,0 @@
-- Remove util.BASE64, make clients use io.Base64.
-
-- Rename core to common?
-- Split up into three sub modules?
- - common-core
- - lang
- - util
- - common-io
- - io
- - io.enc
- - io.ole2 (or move to separate module?)
- - net (or move to separate module?)
- - xml (or move to separate module?)
- - common-image
- - image
- - common-sandbox
- - all unreleased/experimental stuff
-
-- Test cases for the OLE2Compound stuff
-- Test cases for encoder/decoders
-- Test cases in general
diff --git a/twelvemonkeys-imageio/core/pom.xml b/twelvemonkeys-imageio/core/pom.xml
deleted file mode 100644
index 89735963..00000000
--- a/twelvemonkeys-imageio/core/pom.xml
+++ /dev/null
@@ -1,16 +0,0 @@
-
-
- 4.0.0
- com.twelvemonkeys.imageio
- twelvemonkeys-imageio-core
- 2.3-SNAPSHOT
- TwelveMonkeys ImageIO Core
-
-
- twelvemonkeys-imageio
- com.twelvemonkeys
- 2.3-SNAPSHOT
-
-
\ No newline at end of file
diff --git a/twelvemonkeys-imageio/ico/todo.txt b/twelvemonkeys-imageio/ico/todo.txt
deleted file mode 100755
index 173c7fa3..00000000
--- a/twelvemonkeys-imageio/ico/todo.txt
+++ /dev/null
@@ -1 +0,0 @@
-- Support all DIB formats?
diff --git a/twelvemonkeys-imageio/pom.xml b/twelvemonkeys-imageio/pom.xml
deleted file mode 100644
index 97bd4e9e..00000000
--- a/twelvemonkeys-imageio/pom.xml
+++ /dev/null
@@ -1,141 +0,0 @@
-
-
- 4.0.0
- com.twelvemonkeys
- twelvemonkeys-imageio
- 2.3-SNAPSHOT
- pom
- TwelveMonkeys ImageIO
-
-
- com.twelvemonkeys
- twelvemonkeys-parent
- 2.0
-
-
-
-
- Harald Kuhr
- harald.kuhr@gmail.com
-
- owner
- developer
-
-
-
-
-
-
- core
- metadata
-
-
- ico
- iff
- pdf
- pict
- psd
- thumbsdb
-
-
- batik
- jmagick
-
- reference
-
-
-
- 2.3-SNAPSHOT
- 2.3-SNAPSHOT
-
-
-
-
- com.twelvemonkeys
- twelvemonkeys-core
- ${core.version}
- compile
-
-
- com.twelvemonkeys
- twelvemonkeys-core
- ${core.version}
- tests
- test
-
-
-
- junit
- junit
- 4.3.1
- test
-
-
-
- jmock
- jmock-cglib
- 1.0.1
- test
-
-
-
-
-
-
- com.twelvemonkeys.imageio
- twelvemonkeys-imageio-core
- ${imageio.core.version}
- compile
-
-
-
- com.twelvemonkeys.imageio
- twelvemonkeys-imageio-core
- ${imageio.core.version}
- tests
- test
-
-
-
- com.twelvemonkeys.imageio
- twelvemonkeys-imageio-metadata
- ${imageio.core.version}
- compile
-
-
-
-
-
-
-
- maven-source-plugin
-
-
-
- maven-resources-plugin
-
- UTF-8
-
-
-
-
- org.apache.maven.plugins
- maven-jar-plugin
- 2.2
-
-
-
- ${project.name}
- TwelveMonkeys
- ${project.version}
- https://twelvemonkeys-imageio.dev.java.net/
-
-
-
-
-
-
-
-