+ * package com.arsdigita.exampleApp;
+ *
+ * public final class ExampleConfig extends AbstractConfig {
+ *
+ * private Parameter m_string = new StringParameter
+ * ("example.string", Parameter.OPTIONAL, "default");
+ * private Parameter m_integer = new IntegerParameter
+ * ("example.integer", Parameter.OPTIONAL, new Integer(0));
+ * private Parameter m_boolean = new BooleanParameter
+ * ("example.boolean", Parameter.OPTIONAL, Boolean.TRUE);
+ *
+ * public ExampleConfig() {
+ * register(m_string);
+ * register(m_integer);
+ * register(m_boolean);
+ * loadInfo();
+ * }
+ *
+ * public String getString() {
+ * return (String) get(m_string);
+ * }
+ *
+ * public int getInteger() {
+ * return ((Integer) get(m_integer)).intValue();
+ * }
+ *
+ * public boolean getBoolean() {
+ * return Boolean.TRUE.equals(get(m_boolean));
+ * }
+ *
+ * }
+ *
+ *
+ * When this pattern is followed, the resulting subclass of abstract
+ * config may be used by developers writing java code to access the
+ * values of customizable configuration parameters in a convenient and
+ * type safe manner. In addition, the very same class is also usable
+ * by the ccm configuration tools to allow customization and
+ * validation of the new parameters.
+ *
+ * @author Justin Ross <jross@redhat.com>
+ * @version $Id$
+ */
+public abstract class AbstractConfig extends AbstractParameterContext {
+
+ private static final Logger s_log = Logger.getLogger
+ (AbstractConfig.class);
+
+ /**
+ * Default constructor for subclasses.
+ */
+ protected AbstractConfig() {}
+
+ /**
+ * Loads this AbstractConfig object with values from the default
+ * configuration registry. Any errors encountered during
+ * unmarshaling and loading of configuration values are added to
+ * the errors ErrorList. This method should not be
+ * called from the constructor of a config object since the ccm
+ * configuration tools need to be able to construct empty config
+ * objects.
+ *
+ * @param errors The ErrorList used to record errors during
+ * unmarshaling and loading.
+ *
+ * @see ConfigRegistry
+ */
+ public final void load(ErrorList errors) {
+ ConfigRegistry reg = new ConfigRegistry();
+ reg.load(this, errors);
+ }
+
+ /**
+ * Invokes the {@link #load(ErrorList)} method with a new and
+ * empty ErrorList for accumulating errors, and returns that
+ * ErrorList. This method can be used in combination with the
+ * {@link ErrorList#check()} method to load and assert that this
+ * configuration object is valid in one simple idiom. For example:
+ *
+ * + * + * @return Errors that may have been encountered during + * configuration loading. + * + * @see #load(ErrorList) + */ + public final ErrorList load() { + ErrorList errs = new ErrorList(); + load(errs); + return errs; + } + + /** + * @deprecated Use @{link #load()} instead. + */ + public final ErrorList load(final String resource) { + return load(); + } + + /** + * @deprecated Use @{link #load()} instead. + */ + public final ErrorList require(final String resource) { + return load(); + } +} diff --git a/ccm-core/src/main/java/com/arsdigita/runtime/CCMResourceManager.java b/ccm-core/src/main/java/com/arsdigita/runtime/CCMResourceManager.java new file mode 100644 index 000000000..068bfd553 --- /dev/null +++ b/ccm-core/src/main/java/com/arsdigita/runtime/CCMResourceManager.java @@ -0,0 +1,365 @@ +/* + * Copyright (C) 2004 Red Hat Inc. All Rights Reserved. + * Copyright (C) 2009 Peter Boy (pb@zes.uni-bremen.de) All Rights Reserved. + * + * This library is free software; you can redistribute it and/or + * modify it under the terms of the GNU Lesser General Public License + * as published by the Free Software Foundation; either version 2.1 of + * the License, or (at your option) any later version. + * + * This library is distributed in the hope that it will be useful, + * but WITHOUT ANY WARRANTY; without even the implied warranty of + * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU + * Lesser General Public License for more details. + * + * You should have received a copy of the GNU Lesser General Public + * License along with this library; if not, write to the Free Software + * Foundation, Inc., 59 Temple Place, Suite 330, Boston, MA 02111-1307 USA + * + */ +package com.arsdigita.runtime; + +import com.arsdigita.util.UncheckedWrapperException; + +import java.io.File; +import java.net.MalformedURLException; +import java.net.URL; + +import org.apache.log4j.Logger; + +/** + *+ * ExampleConfig conf = new ExampleConfig(); + * conf.load().check(); + * ... + *
CCMResourceManager Runtime environment repository object, stores essential + * properties of the runtime environment and provides them on request.
+ * + *Currently, is is limited to the
+ *It provides public methods to make the properties available.
+ * + *The singleton is initialised
+ *Currently as a fall back mechanism the environmant Variable CCM_HOME is + * evaluated and used a last resort, if no initialisation has been done when + * a getter is first called.
+ * + *It is essential for the proper working of CCM that CCMResourceManager is + * initialised before any operation starts, as it is the case with the Startup + * class of the runtime package, which is responsible for organising database + * access.
+ * + * + *Subject to change!
+ * + * A similiar task is performed by com.arsdigita.util.ResourceManager + * + * @author Justin Ross <jross@redhat.com> + * rewritten by + * @author pboy <pboy@barkhof.uni-bremen.de> + * @version $Id$ + */ +public final class CCMResourceManager { + + private static final Logger s_log = Logger.getLogger(CCMResourceManager.class); + + private static CCMResourceManager s_ccm; + + /** + * Full Pathname of the application base directory + * (document root in apache terminology) + */ + private static File m_baseDir; + + + /** + * Location of the registry in the applications directory tree + * as offset from the base directory + */ + // currently not used, should be refactored as File object for the sake of + // operating system independency! + // public static final String registryPath = "/WEB-INF/conf/registry"; + // public static final File registryPath = null; // currently not used, work in progress + + /* ************ Section singleton handlers ***************** */ + + /** + * Sets the singleton configuration property for the runtime + * environment. + * + */ + public static final synchronized void setBaseDirectory(String baseDirName) { + if (s_ccm == null) { + s_ccm = new CCMResourceManager(); + s_ccm.storeBaseDir(baseDirName); + } + else { + // baseDir already set, silently discard + s_log.info("baseDir already set as " + m_baseDir + ". Discarded."); + } + } + + /** + * Returns the singleton configuration property for the runtime + * environment. + * + * @return TheRuntimeConfig record; it cannot be null
+ */
+ public static final synchronized File getBaseDirectory() {
+ if (s_ccm == null) {
+ // should never happen, setBaseDirectory has to be executed first
+ // we try to resolve the problem in fetchBaseDir by search for
+ // a runtime environment variable (the old way).
+ s_ccm = new CCMResourceManager();
+ }
+
+ return s_ccm.fetchBaseDir();
+ }
+
+ /* ************ Section singleton handlers END ************** */
+
+
+ /* ************ Constructors Section ************** */
+
+ /**
+ * Following the singleton pattern: Private constructor to prevent other
+ * clients from instantiating the class (and the compiler from generating
+ * a default public constructor).
+ */
+ private CCMResourceManager() { }
+
+ /* ************ Constructors Section END ************** */
+
+
+ /* ************ Public getter/setter Section *************** */
+
+ /**
+ * Retrieve the homeDir as URL.
+ *
+ * Note! API changed. @see getHomeDirectory()
+ * May be removed in the future!
+ *
+ * @return Directory location in the servers file system as URL object.
+ */
+ public static final URL getHomeURL() {
+ try {
+ return CCMResourceManager.getHomeDirectory().toURL();
+ } catch (MalformedURLException e) {
+ throw new UncheckedWrapperException(e);
+ }
+ }
+
+ /**
+ * Retrieve the homeDir, which is the location of the servlet server's
+ * servlet container directory in the file system of the server machine,
+ * as File object.
+ *
+ * Note! API changed!
+ *
+ * Originally it is used to determine all file object locations of a
+ * CCM installation, during the installation step as well as
+ * while running the application inside a servlet container. The CCM installation
+ * of a servlet container used to use a non-standard layout. It is based upon a
+ * system wide environment variable CCM_HOME to determine the home directory.
+ *
+ * The dependency from a system wide environment variable prevents a servlet
+ * container to run multiple instances of CCM. In addition to it CCM will
+ * be migrated to be installable in a standard way to a standard container.
+ * Therefore all file locations will be given relative to the applications
+ * directory (the baseDirectory @see m_baseDir).
+ *
+ *
+ * Method getHomeDirectory() is preserved during the transition phase.
+ * It may be removed in the future! Or it may be moved to
+ * c.ad.packaging for assistence of the installation step only.
+ *
+ * MODIFIED:
+ * CCM_HOME is now interpreted as the path to the applications base
+ * directory (web application context).
+ *
+ * @return Directory location in the servers file system as File object.
+ */
+ static final File getHomeDirectory() {
+
+ String home = System.getProperty("ccm.home");
+
+ if (home == null) {
+ throw new IllegalStateException
+ ("The ccm.home system property is null or not defined");
+ }
+
+ // make a guess, wether it is old style (i.e. referring to the containers
+ // base directory and therefor does not contain the webapps part) or
+ // new style referring to the apps base directory (and therefor containing
+ // the webapps part)
+ if (home.indexOf("webapps") > 0 ){
+ // should be new style
+ }
+ else {
+ // presumably old style, add path to standard context name
+ home += "/webapps/ROOT";
+ }
+
+ File file = new File(home);
+
+// No need to require that home exists (indeed, during install it will not).
+// Should be created by invoking method if not.
+// if (!file.exists()) {
+// throw new IllegalStateException
+// ("The file given in the ccm.home system property " +
+// "does not exist");
+// }
+
+ if (!file.isDirectory()) {
+ throw new IllegalStateException
+ ("The file: " + home + " given in the ccm.home system property"
+ + " is not a directory");
+ }
+
+ return file;
+ }
+
+ /**
+ * Provide the configDirectory as URL.
+ *
+ * @see getConfigDirectory() for details.
+ *
+ * Note! API changed!
+ *
+ * @return Directory location in the servers file system as URL object.
+ */
+ public static final URL getConfigURL() {
+ try {
+ return CCMResourceManager.getConfigDirectory().toURL();
+ } catch (MalformedURLException e) {
+ throw new UncheckedWrapperException(e);
+ }
+ }
+
+ /**
+ * Retrieve the configDir, which is the location of the configuration
+ * database root (registry) in the file system tree of the server machine,
+ * as File object.
+
+ *
+ * @return Directory location in the servers file system as File object.
+ */
+ public static final File getConfigDirectory() {
+
+ // Keep this in sync with informational attribut @see registryPath !
+ File confdir = new File(new File(new File(CCMResourceManager.getBaseDirectory(),
+ "WEB-INF"),"conf"), "registry");
+
+ if (!confdir.exists()) {
+ if (!confdir.mkdirs()) {
+ throw new IllegalStateException
+ ("Could not create configuration directory: " + confdir);
+ }
+ }
+ if (!confdir.isDirectory()) {
+ throw new IllegalStateException
+ ("Configuration directory value is not a directory: " + confdir);
+ }
+
+ return confdir;
+ }
+
+ /**
+ * getWorkDirectory retrieves and eventually creates an internal directory
+ * in the servlet container for temporary files (work files), where subsystems
+ * may create subdirectories for internal use (e.g. Lucene search enginge or
+ * the PublishToFile machinery).
+ *
+ * The containers work file directory could be used as well, but may be
+ * inappropriate in case of confidential data.
+ *
+ * @return Directory location in the servers file system as File object.
+ */
+ public static final File getWorkDirectory() {
+ File file = new File(new File(CCMResourceManager.getBaseDirectory(),
+ "WEB-INF"),"work");
+ if (!file.exists()) {
+ if (!file.mkdirs()) {
+ throw new IllegalStateException
+ ("Could not create work directory: " + file);
+ }
+ }
+ if (!file.isDirectory()) {
+ throw new IllegalStateException
+ ("Work directory value is not a directory: " + file);
+ }
+ return file;
+ }
+
+ /* ************ Public getter/setter Section END *************** */
+
+
+ /* ************ Private Worker Section *************** */
+
+ /**
+ * Stores the passed in String as File object.
+ *
+ * @param baseDirName String containing the path, must not be null
+ */
+ private final void storeBaseDir(String baseDirName) {
+
+ s_log.debug("storeBaseDir: BaseDir name is given as " + baseDirName );
+ m_baseDir = new File(baseDirName);
+
+ // eventually: check if dir exists, create it if not.
+ if (!m_baseDir.exists()) {
+ if (!m_baseDir.mkdirs()) {
+ throw new IllegalStateException
+ ("Could not create base directory: " + m_baseDir);
+ }
+ }
+ if (!m_baseDir.isDirectory()) {
+ throw new IllegalStateException
+ ("Base directory value is not a directory: " + m_baseDir);
+ }
+
+ }
+
+ /**
+ * Retrieves the stored BaseDir File object.
+ *
+ * @return Base directory location in the servers file system as File object.
+ */
+ private final File fetchBaseDir() {
+
+ if (m_baseDir == null) {
+ // should never happen, but we try to cope with it anyway by
+ // falling back to getHomeDirectory() and the system wide
+ // environment variable.
+ // During transition phase only! Must be removed when the new
+ // standard compliant installation method is fully in place
+ // MODIFIED
+ // HomeDirectory now specifies the applications context dir.
+ m_baseDir = CCMResourceManager.getHomeDirectory();
+
+ // eventually: check if dir exists, create it if not.
+ if (!m_baseDir.exists()) {
+ if (!m_baseDir.mkdirs()) {
+ throw new IllegalStateException
+ ("Could not create base directory: " + m_baseDir);
+ }
+ }
+ if (!m_baseDir.isDirectory()) {
+ throw new IllegalStateException
+ ("Base directory value is not a directory: " + m_baseDir);
+ }
+ }
+
+ return m_baseDir;
+
+ }
+
+ /* ************ Private Worker Section END *************** */
+
+
+}
diff --git a/ccm-core/src/main/java/com/arsdigita/runtime/ConfigRegistry.java b/ccm-core/src/main/java/com/arsdigita/runtime/ConfigRegistry.java
new file mode 100644
index 000000000..ea1316340
--- /dev/null
+++ b/ccm-core/src/main/java/com/arsdigita/runtime/ConfigRegistry.java
@@ -0,0 +1,348 @@
+/*
+ * Copyright (C) 2003-2004 Red Hat Inc. All Rights Reserved.
+ *
+ * This library is free software; you can redistribute it and/or
+ * modify it under the terms of the GNU Lesser General Public License
+ * as published by the Free Software Foundation; either version 2.1 of
+ * the License, or (at your option) any later version.
+ *
+ * This library is distributed in the hope that it will be useful,
+ * but WITHOUT ANY WARRANTY; without even the implied warranty of
+ * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU
+ * Lesser General Public License for more details.
+ *
+ * You should have received a copy of the GNU Lesser General Public
+ * License along with this library; if not, write to the Free Software
+ * Foundation, Inc., 59 Temple Place, Suite 330, Boston, MA 02111-1307 USA
+ *
+ */
+package com.arsdigita.runtime;
+
+import com.arsdigita.util.Classes;
+import com.arsdigita.util.JavaPropertyReader;
+import com.arsdigita.util.UncheckedWrapperException;
+import com.arsdigita.util.parameter.ErrorList;
+import com.arsdigita.util.parameter.ParameterContext;
+import com.arsdigita.util.parameter.ParameterReader;
+import com.arsdigita.xml.XML;
+
+import java.io.IOException;
+import java.io.InputStream;
+import java.net.URL;
+import java.net.URLClassLoader;
+import java.util.ArrayList;
+import java.util.HashMap;
+import java.util.Iterator;
+import java.util.List;
+import java.util.Map;
+import java.util.Properties;
+
+import org.xml.sax.Attributes;
+import org.xml.sax.helpers.DefaultHandler;
+
+import org.apache.log4j.Logger;
+
+/**
+ * The ConfigRegistry class maps between config classes (subclasses of
+ * {@link com.arsdigita.runtime.AbstractConfig}), and a location used
+ * for persisting the values in a config class.
+ *
+ * The ConfigRegistry also stores the set of configured packages for a
+ * particular CCMResourceManager instance.
+ * Additionally it stores a list of URLs for parent configurations that are
+ * used for defaulting values not present in the local configuration.
+ * This mapping is maintained and extended by CCMResourceManager developers through
+ * the use of an XML configuration file placed in the src tree for a
+ * particular package. If a particular package is configured, the
+ * ConfigRegistry class will look in the classpath for a registry
+ * configuration file named package-key.config, and parse the
+ * file according to the following specification:
+ *
+ * + * <?xml version="1.0" encoding="utf-8"?> + * <registry> + * ... + * <config class="CLASSNAME" + * storage="FILENAME"/> + * ... + * </registry> + *
url, and use
+ * loader when searching the classpath for registry
+ * configuration files.
+ *
+ * @param url The base url for registry locations.
+ * @param loader The ClassLoader to use for retrieving registry
+ * configuration files.
+ **/
+ public ConfigRegistry(URL url, ClassLoader loader) {
+ m_url = url;
+ m_loader = loader;
+ addContext(RegistryConfig.class, "registry.properties");
+ initialize(m_url, new ErrorList());
+ }
+
+ /**
+ * Convenience class which invokes {@link #ConfigRegistry(URL, ClassLoader)}
+ * defaulting the loader to the current context class loader.
+ *
+ * @see Thread#getContextClassLoader()
+ *
+ * @param url The base url for registry locations.
+ */
+ public ConfigRegistry(URL url) {
+ this(url, Thread.currentThread().getContextClassLoader());
+ }
+
+ /**
+ * Convenience class which invokes {@link #ConfigRegistry(URL, ClassLoader)}
+ * defaulting the URL to
+ * new File(System.getProperty("ccm.conf")).toURL(). The value
+ * of the ccm.conf system property may or may not include a trailing slash.
+ *
+ * @param loader The ClassLoader to use when searching for
+ * registry configuration files.
+ */
+ public ConfigRegistry(ClassLoader loader) {
+ this(CCMResourceManager.getConfigURL(), loader);
+ }
+
+ /**
+ * Invokes {@link #ConfigRegistry(URL)} defaulting the URL to new
+ * File(System.getProperty("ccm.conf")).toURL(). The value of the
+ * ccm.conf system property may or may not include a trailing slash.
+ */
+ public ConfigRegistry() {
+ this(CCMResourceManager.getConfigURL());
+ }
+
+ /**
+ *
+ * @param url Base url for registry location(s).
+ * @param errs Errorlist
+ */
+ private void initialize(URL url, ErrorList errs) {
+
+ ClassLoader ldr = new URLClassLoader(new URL[]{url}, null);
+
+ RegistryConfig rc = new RegistryConfig();
+ load(rc, errs, ldr);
+
+ String[] packages = rc.getPackages();
+ URL[] parents = rc.getParents();
+
+ for (int i = 0; i < packages.length; i++) {
+ if (!m_packages.contains(packages[i])) {
+ initialize(packages[i]);
+ }
+ }
+
+ for (int i = parents.length - 1; i >= 0; i--) {
+ initialize(parents[i], errs);
+ }
+
+ m_loaders.add(ldr);
+ }
+
+ /**
+ * This method is not supported API.
+ */
+ public final void initialize(String key) {
+ s_log.debug(String.format("Initalizing for key '%s'", key));
+ if (m_packages.contains(key)) {
+ throw new IllegalArgumentException("already loaded: " + key);
+ }
+
+ InputStream is = m_loader.getResourceAsStream(key + ".config");
+ if (is != null) {
+ try {
+ XML.parse(is, new ConfigRegistryParser());
+ m_packages.add(key);
+ } finally {
+ try {
+ is.close();
+ } catch (IOException e) {
+ throw new UncheckedWrapperException(e);
+ }
+ }
+ }
+ }
+
+ /**
+ * Returns the list of configured packages for this ConfigRegistry.
+ *
+ * @return A list of package keys represented as Strings.
+ **/
+ public List getPackages() {
+ return m_packages;
+ }
+
+ /**
+ * Returns a list of config classes for this ConfigRegistry.
+ *
+ * @return A list of Class objects.
+ **/
+ public List getContexts() {
+ return m_contexts;
+ }
+
+ /**
+ * Returns the relative location used to store values for the
+ * given config class.
+ *
+ * @param context a subclass of {@link
+ * com.arsdigita.runtime.AbstractConfig}
+ *
+ * @return the relative storage location for context
+ *
+ * @throws IllegalArgumentException if this ConfigRegistry does
+ * not contain a mapping for context
+ **/
+ public String getStorage(Class context) {
+ if (!m_contexts.contains(context)) {
+ throw new IllegalArgumentException("no such context: " + context
+ + "; available contexts="
+ + m_contexts
+ + "; context->storage map: "
+ + m_storage);
+ }
+ return (String) m_storage.get(context);
+ }
+
+ private void addContext(Class context, String storage) {
+ s_log.debug(String.format("Adding context '%s', storage '%s'...",
+ context.getName(), storage));
+ m_contexts.add(context);
+ m_storage.put(context, storage);
+ }
+
+ /**
+ * Returns true if this ConfigRegistry contains a mapping for
+ * context
+ *
+ * @param context a subclass of {@link
+ * com.arsdigita.runtime.AbstractConfig}
+ *
+ * @return true if this ConfigRegistry contains a mapping for
+ * context
+ **/
+ public boolean isConfigured(Class context) {
+ return m_contexts.contains(context);
+ }
+
+ /**
+ * Loads the given config object from the correct location based
+ * on its class. Defaults all values based on the value of the
+ * waf.config.parents parameter. Any errors
+ * encountered during loading are reported in the given ErrorList.
+ *
+ * @param ctx the config object to load
+ * @param errs used to accumulate errors during loading
+ *
+ * @throws IllegalArgumentException if this ConfigRegistry does
+ * not contain a mapping for ctx.getClass()
+ **/
+ public void load(ParameterContext ctx, ErrorList errs) {
+ for (Iterator it = m_loaders.iterator(); it.hasNext();) {
+ ClassLoader ldr = (ClassLoader) it.next();
+ load(ctx, errs, ldr);
+ }
+ }
+
+ /**
+ * Searches through this ConfigRegistry and its parents for the
+ * given resource. If it is not found it is also searched for in
+ * the classpath specified by the loader passed to this
+ * ConfigRegistry on construction. This may be used to load
+ * configuration information that is not stored in a config
+ * object.
+ *
+ * @param resource the path to the resource
+ *
+ * @return an input stream containing the contents of the resource
+ * or null if the resource is not found
+ */
+ public InputStream load(String resource) {
+ for (int i = m_loaders.size() - 1; i >= 0; i--) {
+ ClassLoader ldr = (ClassLoader) m_loaders.get(i);
+ InputStream is = ldr.getResourceAsStream(resource);
+ if (is != null) {
+ return is;
+ }
+ }
+
+ return m_loader.getResourceAsStream(resource);
+ }
+
+ private void load(ParameterContext ctx, ErrorList errs, ClassLoader ldr) {
+ Properties props = getProperties(ldr, getStorage(ctx.getClass()));
+ ParameterReader reader = new JavaPropertyReader(props);
+ ctx.load(reader, errs);
+ }
+
+ private Properties getProperties(ClassLoader ldr, String resource) {
+ Properties props = new Properties();
+ InputStream is = ldr.getResourceAsStream(resource);
+ if (is != null) {
+ try {
+ props.load(is);
+ } catch (IOException e) {
+ throw new UncheckedWrapperException(e);
+ } finally {
+ try {
+ is.close();
+ } catch (IOException e) {
+ throw new UncheckedWrapperException(e);
+ }
+ }
+ }
+ return props;
+ }
+
+ private class ConfigRegistryParser extends DefaultHandler {
+
+ @Override
+ public void startElement(String uri, String localName, String qn,
+ Attributes attrs) {
+ if (localName.equals("config")) {
+ String klass = attrs.getValue(uri, "class");
+ String storage = attrs.getValue(uri, "storage");
+ // XXX: Is there a better way to handle errors that
+ // includes line number information?
+ if ((klass == null) || (storage == null)) {
+ throw new IllegalArgumentException(
+ "class and storage attributes are required");
+ }
+
+ Class context = Classes.loadClass(klass);
+ addContext(context, storage);
+ }
+ }
+ }
+}
diff --git a/ccm-core/src/main/java/com/arsdigita/runtime/RegistryConfig.java b/ccm-core/src/main/java/com/arsdigita/runtime/RegistryConfig.java
new file mode 100644
index 000000000..2b6c14236
--- /dev/null
+++ b/ccm-core/src/main/java/com/arsdigita/runtime/RegistryConfig.java
@@ -0,0 +1,140 @@
+/*
+ * Copyright (C) 2003-2004 Red Hat Inc. All Rights Reserved.
+ *
+ * This library is free software; you can redistribute it and/or
+ * modify it under the terms of the GNU Lesser General Public License
+ * as published by the Free Software Foundation; either version 2.1 of
+ * the License, or (at your option) any later version.
+ *
+ * This library is distributed in the hope that it will be useful,
+ * but WITHOUT ANY WARRANTY; without even the implied warranty of
+ * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU
+ * Lesser General Public License for more details.
+ *
+ * You should have received a copy of the GNU Lesser General Public
+ * License along with this library; if not, write to the Free Software
+ * Foundation, Inc., 59 Temple Place, Suite 330, Boston, MA 02111-1307 USA
+ *
+ */
+package com.arsdigita.runtime;
+
+// import com.arsdigita.runtime.AbstractConfig;
+import com.arsdigita.util.StringUtils;
+import com.arsdigita.util.parameter.ErrorList;
+import com.arsdigita.util.parameter.Parameter;
+import com.arsdigita.util.parameter.ParameterError;
+import com.arsdigita.util.parameter.StringParameter;
+import java.net.MalformedURLException;
+import java.net.URL;
+
+
+/**
+ * A config class used by the registry itself.
+ *
+ * Contains the parameters:
+ * waf.config.packages: comma separated package-key list of installed packages
+ * waf.config.parents :
+ *
+ * @author Rafael H. Schloming <rhs@mit.edu>
+ * @version $Id$
+ */
+public class RegistryConfig extends AbstractConfig {
+
+ /**
+ * Helper method to unmarshal parameter values.
+ * @param str A String of comma separated values
+ * @return StringArray of the values
+ */
+ private static String[] array(String str) {
+ if (str == null) {
+ return null;
+ } else {
+ return StringUtils.split(str, ',');
+ }
+ }
+
+ /**
+ * List of installed packages.
+ *
+ * Provided as a comma separated package-key list of installed packages.
+ * The parameter overwrites the default marshal and unmarshal methods to
+ * allow the String parameter to hold a list of values.
+ *
+ * TODO: Replace the type String parameter by StringArray parameter which
+ * provides exactly the required functionality (doesn't it?).
+ */
+ private Parameter m_packages = new StringParameter
+ ("waf.config.packages", Parameter.OPTIONAL, new String[0]) {
+ @Override
+ protected Object unmarshal(String value, ErrorList errs) {
+ return array(value);
+ }
+
+ @Override
+ protected String marshal(Object obj) {
+ return StringUtils.join((String[]) obj, ',');
+ }
+ };
+
+ /**
+ * List of parameter values, purpose currently unkown.
+ *
+ * The parameter overwrites the default marshal and unmarshal methods to
+ * allow the String parameter to hold a list of values.
+ */
+ private Parameter m_parents = new StringParameter
+ ("waf.config.parents", Parameter.OPTIONAL, new URL[0]) {
+ @Override
+ protected Object unmarshal(String value, ErrorList errs) {
+ String[] strs = array(value);
+ URL[] result = new URL[strs.length];
+ for (int i = 0; i < result.length; i++) {
+ try {
+ result[i] = new URL(strs[i]);
+ } catch (MalformedURLException e) {
+ errs.add(new ParameterError(this, e));
+ }
+ }
+ if (!errs.isEmpty()) {
+ return null;
+ }
+ return result;
+ }
+
+ protected String marshal(Object obj) {
+ URL[] urls = (URL[]) obj;
+ String[] strs = new String[urls.length];
+ for (int i = 0; i < strs.length; i++) {
+ strs[i] = urls[i].toExternalForm();
+ }
+ return StringUtils.join(strs, ',');
+ }
+ };
+
+ /**
+ * Constructs a new and empty config object.
+ */
+ public RegistryConfig() {
+ register(m_packages);
+ register(m_parents);
+ }
+
+ /**
+ * Returns the value of the waf.config.packages parameter.
+ *
+ * @return the value of the waf.config.packages parameter
+ */
+ public String[] getPackages() {
+ return (String[]) get(m_packages);
+ }
+
+ /**
+ * Returns the value of the waf.config.parents parameter.
+ *
+ * @return the value of the waf.config.parents parameter
+ */
+ public URL[] getParents() {
+ return (URL[]) get(m_parents);
+ }
+
+}
diff --git a/ccm-core/src/main/java/com/arsdigita/util/Assert.java b/ccm-core/src/main/java/com/arsdigita/util/Assert.java
new file mode 100644
index 000000000..648ab6b32
--- /dev/null
+++ b/ccm-core/src/main/java/com/arsdigita/util/Assert.java
@@ -0,0 +1,716 @@
+/*
+ * Copyright (C) 2001-2004 Red Hat Inc. All Rights Reserved.
+ *
+ * This library is free software; you can redistribute it and/or
+ * modify it under the terms of the GNU Lesser General Public License
+ * as published by the Free Software Foundation; either version 2.1 of
+ * the License, or (at your option) any later version.
+ *
+ * This library is distributed in the hope that it will be useful,
+ * but WITHOUT ANY WARRANTY; without even the implied warranty of
+ * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU
+ * Lesser General Public License for more details.
+ *
+ * You should have received a copy of the GNU Lesser General Public
+ * License along with this library; if not, write to the Free Software
+ * Foundation, Inc., 59 Temple Place, Suite 330, Boston, MA 02111-1307 USA
+ *
+ */
+package com.arsdigita.util;
+
+import org.apache.log4j.Logger;
+
+/**
+ * Utility functions for assertions.
+ *
+ * The static methods in this class provide a standard way of + * asserting certain conditions.
+ * + *Though it is not right now, this class should be final. + * Do not subclass it. In a future revision of this software, this + * class will be made final.
+ * + * @author David Lutterkort <dlutter@redhat.com> + * @author Uday Mathur + * @author Justin Ross <jross@redhat.com> + * @version $Id$ + */ +public class Assert { + + /** Class specific logger instance. */ + private static final Logger s_log = Logger.getLogger(Assert.class); + + private static final String DEFAULT_MESSAGE = "Assertion failure"; + + /** + * Configuration by a system wide / servlet container wide environment + * variable is evil and no longer supported API. + * Currently Assertion is set alway on. The non-deprecated methods of this + * class don't check for isEnabled anyway. So there is no effect. + * If configurability is really needed, configuration by config registry + * during system setup (eg. @link com.arsdigita.runtime.CCMResourceManager) + * has to be implemented + */ + private static boolean s_enabled = true; +// private static boolean s_enabled; +// +// static { +// final String enabled = System.getProperty +// (Assert.class.getName() + ".enabled"); +// +// if (enabled == null) { +// //s_enabled = false; +// //default value is true +// s_enabled = true; +// } else { +// // s_enabled = enabled.equals("true"); +// // Test: set to true anyway. +// // TODO: read from config registry, any dependency from a +// // environment variable is deprecated! +// s_enabled = true; +// } +// } + + /** + * Tells whether asserts are turned on. Use this to wrap code + * that should be optimized away if assertions are disabled. + * + * By default, assertions are disabled + */ + public static final boolean isEnabled() { + return s_enabled; + } + + static final void setEnabled(final boolean enabled) { + s_enabled = enabled; + } + + /** + * Throws an error. + * + * @param message AString describing the failure
+ * condition
+ * @throws AssertionError
+ */
+ public static final void fail(final String message) {
+ error(message);
+
+ throw new AssertionError(message);
+ }
+
+ /**
+ * Throws an error.
+ *
+ * @throws AssertionError
+ */
+ public static final void fail() {
+ error(DEFAULT_MESSAGE);
+
+ throw new AssertionError(DEFAULT_MESSAGE);
+ }
+
+ /**
+ * Asserts that an arbitrary condition is true and throws an
+ * error with message message if the condition is
+ * false.
+ *
+ * @param condition The condition asserted
+ * @param message An error message
+ * @throws AssertionError if the condition is false
+ */
+ public static final void isTrue(final boolean condition,
+ final String message) {
+ if (!condition) {
+ error(message);
+
+ throw new AssertionError(message);
+ }
+ }
+
+ /**
+ * Asserts that an arbitrary condition is true and throws an
+ * error with message message if the condition is
+ * false.
+ *
+ * @param condition The condition asserted
+ * @param message An error message
+ * @throws AssertionError if the condition is false
+ * @deprecated use Assert.isTrue(condition, message) instead
+ * (we will follow the standard naming scheme)
+ */
+ public static final void truth(final boolean condition,
+ final String message) {
+ Assert.isTrue(condition, message);
+
+
+ // if (!condition) {
+ // error(message);
+ //
+ // throw new AssertionError(message);
+ // }
+ }
+
+ /**
+ * Asserts that an arbitrary condition is true and throws an
+ * error with message message if the condition is
+ * false.
+ *
+ * @param condition The condition asserted
+ * @throws AssertionError if the condition is false
+ */
+ public static final void isTrue(final boolean condition) {
+ if (!condition) {
+ error(DEFAULT_MESSAGE);
+
+ throw new AssertionError(DEFAULT_MESSAGE);
+ }
+ }
+
+ /**
+ * Asserts that an arbitrary condition is true and throws an
+ * error with message message if the condition is
+ * false.
+ *
+ * @param condition The condition asserted
+ * @throws AssertionError if the condition is false
+ * @deprecated use Assert.isTrue(final boolean condition) instead
+ * (we will follow the standard naming scheme)
+ */
+ public static final void truth(final boolean condition) {
+ Assert.isTrue(condition);
+
+ // if (!condition) {
+ // error(DEFAULT_MESSAGE);
+ //
+ // throw new AssertionError(DEFAULT_MESSAGE);
+ // }
+ }
+
+ /**
+ * Asserts that an arbitrary condition is false and throws an
+ * error if the condition is true.
+ *
+ * @param condition The condition asserted
+ * @param message An error message
+ * @throws AssertionError if the condition is false
+ */
+ public static final void isFalse(final boolean condition,
+ final String message) {
+ if (condition) {
+ error(message);
+
+ throw new AssertionError(message);
+ }
+ }
+
+ /**
+ * Asserts that an arbitrary condition is false and throws an
+ * error if the condition is true.
+ *
+ * @param condition The condition asserted
+ * @throws AssertionError if the condition is false
+ */
+ public static final void isFalse(final boolean condition) {
+ if (condition) {
+ error(DEFAULT_MESSAGE);
+
+ throw new AssertionError(DEFAULT_MESSAGE);
+ }
+ }
+
+ /**
+ * Asserts that an object is not null.
+ *
+ * @param object The object that must not be null
+ * @param clacc The Class of parameter
+ * object
+ * @throws AssertionError if the object is null
+ */
+ public static void exists(final Object object,
+ final Class clacc) {
+ if (object == null) {
+ final String message = clacc.getName() + " is null";
+
+ error(message);
+
+ throw new AssertionError(message);
+ }
+ }
+
+ /**
+ * Asserts that an object is not null.
+ *
+ * @param object The Object that must not be null
+ * @param label A text to describe Object
+ *
+ * @throws AssertionError if the object is null
+ */
+ public static void exists(final Object object,
+ final String label) {
+ if (object == null) {
+ final String message =
+ label != null && label.trim().length() > 0
+ ? "Value of " + label + " is null."
+ : DEFAULT_MESSAGE ;
+
+ error(message);
+
+ throw new AssertionError(message);
+ }
+ }
+
+ /**
+ * Asserts that an object is not null.
+ *
+ * @param object The object that must not be null
+ * @throws AssertionError if the object is null
+ */
+ public static final void exists(final Object object) {
+ if (object == null) {
+ error(DEFAULT_MESSAGE);
+
+ throw new AssertionError(DEFAULT_MESSAGE);
+ }
+ }
+
+ /**
+ * Verifies that Lockable is locked and throws an
+ * error if it is not.
+ *
+ * @param lockable The object that must be locked
+ * @see com.arsdigita.util.Lockable
+ */
+ public static final void isLocked(final Lockable lockable) {
+ if (lockable != null && !lockable.isLocked()) {
+ final String message = "Illegal access: " + lockable +
+ " is not locked";
+
+ error(message);
+
+ throw new AssertionError(message);
+ }
+ }
+
+ /**
+ * Verifies that lockable is not locked and
+ * throws an error if it is.
+ *
+ * @param lockable The object that must not be locked
+ * @see com.arsdigita.util.Lockable
+ */
+ public static final void isUnlocked(final Lockable lockable) {
+ if (lockable != null && lockable.isLocked()) {
+ final String message = "Illegal access: " + lockable + " is locked.";
+
+ error(message);
+
+ throw new AssertionError(message);
+ }
+ }
+
+ /**
+ * Verifies that two values are equal (according to their equals
+ * method, unless value1 is null, then according to
+ * ==).
+ *
+ * @param value1 The first value to be compared
+ * @param value2 The second
+ * @throws AssertionError if the arguments are unequal
+ */
+ public static final void isEqual(final Object value1,
+ final Object value2) {
+ if (value1 == null) {
+ if (value1 != value2) {
+ final String message = value1 + " does not equal " + value2;
+
+ error(message);
+
+ throw new AssertionError(message);
+ }
+ } else {
+ if (!value1.equals(value2)) {
+ final String message = value1 + " does not equal " + value2;
+
+ error(message);
+
+ throw new AssertionError(message);
+ }
+ }
+ }
+
+ /**
+ * Verifies that two values are equal (according to their equals
+ * method, unless value1 is null, then according to
+ * ==).
+ *
+ * @param value1 The first value to be compared
+ * @param value2 The second
+ * @throws AssertionError if the arguments are unequal
+ */
+ public static final void isEqual(final Object value1,
+ final Object value2,
+ final String message) {
+ if (value1 == null) {
+ if (value1 != value2) {
+ error(message);
+
+ throw new AssertionError(message);
+ }
+ } else {
+ if (!value1.equals(value2)) {
+ error(message);
+
+ throw new AssertionError(message);
+ }
+ }
+ }
+
+ /**
+ * Verifies that two values are not equal (according to their
+ * equals method, unless value1 is null, then
+ * according to ==).
+ *
+ * @param value1 The first value to be compared
+ * @param value2 The second
+ * @throws AssertionError if the arguments are isNotEqual
+ */
+ public static final void isNotEqual(final Object value1,
+ final Object value2) {
+ if (value1 == null) {
+ if (value1 == value2) {
+ final String message = value1 + " equals " + value2;
+
+ error(message);
+
+ throw new AssertionError(message);
+ }
+ } else {
+ if (value1.equals(value2)) {
+ final String message = value1 + " equals " + value2;
+
+ error(message);
+
+ throw new AssertionError(message);
+ }
+ }
+ }
+
+ // Utility methods
+
+ private static void error(final String message) {
+ // Log the stack trace too, since we still have code that
+ // manages to hide exceptions.
+ s_log.error(message, new AssertionError(message));
+ }
+
+ ///////////////////////////////////////////////////////////////////////////
+ // //
+ // The methods below are all deprecated. //
+ // //
+ ///////////////////////////////////////////////////////////////////////////
+
+ /**
+ * @deprecated in favor of {@link #isEnabled()}
+ *
+ * pboy Jan.09: not used by any package in trunk
+ */
+ // public static final boolean ASSERT_ON = true;
+
+ /**
+ * Indicates state of the ASSERT_ON flag.
+ *
+ * pboy Jan.09: not used by any package in trunk
+ *
+ * @deprecated Use {@link #isEnabled()} instead
+ */
+ // public static final boolean isAssertOn() {
+ // return isEnabled();
+ // }
+
+// /**
+// * Tells whether asserts are turned on. Use this to wrap code
+// * that should be optimized away if asserts are disabled.
+// *
+// * @deprecated Use {@link #isEnabled()} instead
+// */
+// public static final boolean isAssertEnabled() {
+// return isEnabled();
+// }
+
+ /**
+ * Assert that an arbitrary condition is true, and throw an
+ * exception if the condition is false.
+ *
+ * @param cond condition to assert
+ * @throws java.lang.IllegalStateException condition was false
+ * @deprecated Use {@link #truth(boolean, String)} instead
+ */
+/* public static final void isTrue(boolean cond) {
+ isTrue(cond, "");
+ }
+*/
+ /**
+ * Assert that an arbitrary condition is true, and throw an
+ * exception with message msg if the condition is
+ * false.
+ *
+ * @param cond condition to assert
+ * @param msg failure message
+ * @throws java.lang.IllegalStateException condition was false
+ * @deprecated Use {@link #truth(boolean,String)} instead
+ */
+/* public static final void assertTrue(boolean cond, String msg) {
+ if (!cond) {
+ error(msg);
+
+ throw new IllegalStateException(msg);
+ }
+ }
+*/
+ /**
+ * Asserts that an arbitrary condition is true and throws an
+ * error with message message if the condition is
+ * false.
+ *
+ * @param condition The condition asserted
+ * @param message An error message
+ * @throws AssertionError if the condition is false
+ * @deprecated use Assert.isTrue(condition, message) instead
+ * (we will follow the standard naming scheme)
+ */
+/* public static final void truth(final boolean condition,
+ final String message) {
+ Assert.isTrue(condition, message);
+
+ // if (!condition) {
+ // error(message);
+ //
+ // throw new AssertionError(message);
+ // }
+ }
+*/
+
+ /**
+ * Asserts that an arbitrary condition is true and throws an
+ * error with message message if the condition is
+ * false.
+ *
+ * @param condition The condition asserted
+ * @throws AssertionError if the condition is false
+ * @deprecated use Assert.isTrue(final boolean condition) instead
+ * (we will follow the standard naming scheme)
+ */
+/* public static final void truth(final boolean condition) {
+ Assert.isTrue(condition);
+
+ // if (!condition) {
+ // error(DEFAULT_MESSAGE);
+ //
+ // throw new AssertionError(DEFAULT_MESSAGE);
+ // }
+ }
+*/
+
+ /**
+ * Asserts that an arbitrary condition is false and throws an
+ * error if the condition is true.
+ *
+ * @param condition The condition asserted
+ * @param message An error message
+ * @throws AssertionError if the condition is false
+ */
+/* public static final void falsity(final boolean condition,
+ final String message) {
+ if (condition) {
+ error(message);
+
+ throw new AssertionError(message);
+ }
+ }
+*/
+
+ /**
+ * Asserts that an arbitrary condition is false and throws an
+ * error if the condition is true.
+ *
+ * @param condition The condition asserted
+ * @throws AssertionError if the condition is false
+ */
+/* public static final void falsity(final boolean condition) {
+ if (condition) {
+ error(DEFAULT_MESSAGE);
+
+ throw new AssertionError(DEFAULT_MESSAGE);
+ }
+ }
+*/
+ /**
+ * Verify that a parameter is not null and throw a runtime
+ * exception if so.
+ *
+ * @deprecated Use {@link #exists(Object)} instead
+ */
+/* public static final void assertNotNull(Object o) {
+ assertNotNull(o, "");
+ }
+*/
+ /**
+ * Verify that a parameter is not null and throw a runtime
+ * exception if so.
+ *
+ * @deprecated Use {@link #exists(Object,String)} instead
+ */
+/* public static final void assertNotNull(Object o, String label) {
+ if (isEnabled()) {
+ isTrue(o != null, "Value of " + label + " is null.");
+ }
+ }
+*/
+// /**
+// * Verify that a string is not empty and throw a runtime exception
+// * if so. A parameter is considered empty if it is null, or if it
+// * does not contain any characters that are non-whitespace.
+// *
+// * @deprecated with no replacement
+// */
+// public static final void assertNotEmpty(String s) {
+// assertNotEmpty(s, "");
+// }
+
+ /**
+ * Verify that a string is not empty and throw a runtime exception
+ * if so. A parameter is considered empty if it is null, or if it
+ * does not contain any characters that are non-whitespace.
+ *
+ * @deprecated with no replacement
+ */
+ public static final void assertNotEmpty(String s, String label) {
+ if (isEnabled()) {
+ isTrue(s != null && s.trim().length() > 0,
+ "Value of " + label + " is empty.");
+ }
+ }
+
+ /**
+ * Verify that two values are equal (according to their equals method,
+ * unless expected is null, then according to ==).
+ *
+ * @param expected Expected value.
+ * @param actual Actual value.
+ * @throws java.lang.IllegalStateException condition was false
+ * @deprecated Use {@link #isEqual(Object,Object)} instead
+ */
+ public static final void assertEquals(Object expected, Object actual) {
+ assertEquals(expected, actual, "expected", "actual");
+ }
+
+ /**
+ * Verify that two values are equal (according to their equals method,
+ * unless expected is null, then according to ==).
+ *
+ * @param expected Expected value.
+ * @param actual Actual value.
+ * @param expectedLabel Label for first (generally expected) value.
+ * @param actualLabel Label for second (generally actual) value.
+ * @throws java.lang.IllegalStateException condition was false
+ */
+ public static final void assertEquals(Object expected, Object actual,
+ String expectedLabel,
+ String actualLabel) {
+ if (isEnabled()) {
+ if (expected == null) {
+ isTrue(expected == actual,
+ "Values not equal, " +
+ expectedLabel + " '" + expected + "', " +
+ actualLabel + " '" + actual + "'");
+ } else {
+ isTrue(expected.equals(actual),
+ "Values not equal, " +
+ expectedLabel + " '" + expected + "', " +
+ actualLabel + " '" + actual + "'");
+ }
+ }
+ }
+
+ /**
+ * Verify that two values are equal.
+ *
+ * @param expected Expected value.
+ * @param actual Actual value.
+ * @throws java.lang.IllegalStateException condition was false
+ * @deprecated Use {@link #truth(boolean, String)} instead
+ */
+ public static final void assertEquals(int expected, int actual) {
+ assertEquals(expected, actual, "expected", "actual");
+ }
+
+ /**
+ * Verify that two values are equal.
+ *
+ * @param expected Expected value.
+ * @param actual Actual value.
+ * @param expectedLabel Label for first (generally expected) value.
+ * @param actualLabel Label for second (generally actual) value.
+ * @throws java.lang.IllegalStateException condition was false
+ * @deprecated Use {@link #truth(boolean, String)} instead
+ */
+ public static final void assertEquals(int expected, int actual,
+ String expectedLabel,
+ String actualLabel) {
+ if (isEnabled()) {
+ isTrue(expected == actual,
+ "Values not equal, " +
+ expectedLabel + " '" + expected + "', " +
+ actualLabel + " '" + actual + "'");
+ }
+ }
+
+ /**
+ * Verify that the model is locked and throw a runtime exception
+ * if it is not locked.
+ *
+ * @deprecated Use {@link #isLocked(Lockable)} instead
+ */
+/* public static void assertLocked(Lockable l) {
+ if (isEnabled()) {
+ isTrue(l.isLocked(),
+ "Illegal access to an unlocked " +
+ l.getClass().getName());
+ }
+ }
+*/
+ /**
+ * Verify that the model is locked and throw a runtime exception
+ * if it is locked.
+ *
+// * @deprecated Use {@link #isUnlocked(Lockable)} instead
+ */
+/* public static void assertUnlocked(Lockable l) {
+ if (isEnabled()) {
+ isTrue(!l.isLocked(),
+ "Illegal access to a isLocked " +
+ l.getClass().getName());
+ }
+ }
+*/
+ /**
+ * Verifies that lockable is not isLocked and
+ * throws an error if it is.
+ *
+ * @param lockable The object that must not be isLocked
+ * @see com.arsdigita.util.Lockable
+// * @deprecated use isUnlocked(Lockable) instead
+ */
+/* public static final void unlocked(final Lockable lockable) {
+ if (lockable != null && lockable.isLocked()) {
+ final String message = "Illegal access: " + lockable + " is isLocked.";
+
+ error(message);
+
+ throw new AssertionError(message);
+ }
+ }
+*/
+
+
+}
diff --git a/ccm-core/src/main/java/com/arsdigita/util/Classes.java b/ccm-core/src/main/java/com/arsdigita/util/Classes.java
new file mode 100644
index 000000000..bd1230241
--- /dev/null
+++ b/ccm-core/src/main/java/com/arsdigita/util/Classes.java
@@ -0,0 +1,148 @@
+/*
+ * Copyright (C) 2003-2004 Red Hat Inc. All Rights Reserved.
+ *
+ * This library is free software; you can redistribute it and/or
+ * modify it under the terms of the GNU Lesser General Public License
+ * as published by the Free Software Foundation; either version 2.1 of
+ * the License, or (at your option) any later version.
+ *
+ * This library is distributed in the hope that it will be useful,
+ * but WITHOUT ANY WARRANTY; without even the implied warranty of
+ * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU
+ * Lesser General Public License for more details.
+ *
+ * You should have received a copy of the GNU Lesser General Public
+ * License along with this library; if not, write to the Free Software
+ * Foundation, Inc., 59 Temple Place, Suite 330, Boston, MA 02111-1307 USA
+ *
+ */
+package com.arsdigita.util;
+
+import java.lang.reflect.InvocationTargetException;
+import java.lang.reflect.Constructor;
+import org.apache.log4j.Logger;
+
+import java.util.Arrays;
+
+/**
+ * A collection of static utility methods for dealing with Java
+ * classes.
+ *
+ * @author Justin Ross
+ * @version $Id$
+ */
+public final class Classes {
+
+ private static final Logger s_log = Logger.getLogger(Classes.class);
+
+ /**
+ * Loads a class from its fully qualified string name.
+ *
+ * @param clacc A fully qualified String naming
+ * the class to be loaded
+ */
+ public static final Class loadClass(final String clacc) {
+ Assert.exists(clacc, String.class);
+
+ try {
+ return Class.forName(clacc);
+ } catch (ClassNotFoundException ex) {
+ throw new UncheckedWrapperException(ex);
+ }
+ }
+
+ /**
+ * Constructs a new instance of a class using the given
+ * parameters.
+ *
+ * @param clacc The Class of which to make a new
+ * instance
+ * @param params A Class[] representing the arguments
+ * of the desired constructor
+ * @param values An Object[] of values to fill the
+ * parameters
+ */
+ public static final Object newInstance(final Class clacc,
+ final Class[] params,
+ final Object[] values) {
+ if (Assert.isEnabled()) {
+ Assert.exists(clacc, Class.class);
+ Assert.exists(params, Class.class);
+ Assert.exists(values, Object.class);
+ Assert.isTrue(params.length == values.length);
+ }
+
+ try {
+ final Constructor constructor = clacc.getConstructor(params);
+
+ return constructor.newInstance(values);
+ } catch (NoSuchMethodException ex) {
+ throw new UncheckedWrapperException
+ (message(clacc, params, values), ex);
+ } catch (IllegalAccessException ex) {
+ throw new UncheckedWrapperException
+ (message(clacc, params, values), ex);
+ } catch (InvocationTargetException ex) {
+ throw new UncheckedWrapperException
+ (message(clacc, params, values), ex);
+ } catch (InstantiationException ex) {
+ throw new UncheckedWrapperException
+ (message(clacc, params, values), ex);
+ }
+ }
+
+ private static String message(Class klass, Class[] params,
+ Object[] values) {
+ return "class = " + klass +
+ ", params = " + message(params) +
+ ", values = " + message(values);
+ }
+
+ private static String message(Object[] array) {
+ if (array == null) {
+ return "" + null;
+ } else {
+ return Arrays.asList(array).toString();
+ }
+ }
+
+ /**
+ * Constructs a new instance of the class referred to by
+ * clacc.
+ *
+ * @param clacc The fully qualified String
+ * clacc of the object you wish to instantiate
+ * @param params A Class[] representing the arguments
+ * of the desired constructor
+ * @param values An Object[] of values to fill the
+ * parameters
+ */
+ public static final Object newInstance(final String clacc,
+ final Class[] params,
+ final Object[] values) {
+ return newInstance(loadClass(clacc), params, values);
+ }
+
+ /**
+ * Creates a new instance of clacc using its no-args
+ * constructor. If the class has no such constructor, it throws a
+ * runtime exception.
+ *
+ * @param clacc The class of which to create a new instance
+ */
+ public static final Object newInstance(final Class clacc) {
+ return newInstance(clacc, new Class[0], new Object[0]);
+ }
+
+ /**
+ * Creates a new instance of the class represented by
+ * clacc using its no-args constructor. If the class
+ * has no such constructor, it throws a runtime exception.
+ *
+ * @param clacc The fully-qualified String name of
+ * the class
+ */
+ public static final Object newInstance(final String clacc) {
+ return newInstance(loadClass(clacc));
+ }
+}
diff --git a/ccm-core/src/main/java/com/arsdigita/util/ExceptionUnwrapper.java b/ccm-core/src/main/java/com/arsdigita/util/ExceptionUnwrapper.java
new file mode 100644
index 000000000..8171bed18
--- /dev/null
+++ b/ccm-core/src/main/java/com/arsdigita/util/ExceptionUnwrapper.java
@@ -0,0 +1,24 @@
+/*
+ * Copyright (C) 2003-2004 Red Hat Inc. All Rights Reserved.
+ *
+ * This library is free software; you can redistribute it and/or
+ * modify it under the terms of the GNU Lesser General Public License
+ * as published by the Free Software Foundation; either version 2.1 of
+ * the License, or (at your option) any later version.
+ *
+ * This library is distributed in the hope that it will be useful,
+ * but WITHOUT ANY WARRANTY; without even the implied warranty of
+ * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU
+ * Lesser General Public License for more details.
+ *
+ * You should have received a copy of the GNU Lesser General Public
+ * License along with this library; if not, write to the Free Software
+ * Foundation, Inc., 59 Temple Place, Suite 330, Boston, MA 02111-1307 USA
+ *
+ */
+package com.arsdigita.util;
+
+
+public interface ExceptionUnwrapper {
+ Throwable unwrap(Throwable t);
+}
diff --git a/ccm-core/src/main/java/com/arsdigita/util/Exceptions.java b/ccm-core/src/main/java/com/arsdigita/util/Exceptions.java
new file mode 100644
index 000000000..c0b3e23f1
--- /dev/null
+++ b/ccm-core/src/main/java/com/arsdigita/util/Exceptions.java
@@ -0,0 +1,113 @@
+/*
+ * Copyright (C) 2003-2004 Red Hat Inc. All Rights Reserved.
+ *
+ * This library is free software; you can redistribute it and/or
+ * modify it under the terms of the GNU Lesser General Public License
+ * as published by the Free Software Foundation; either version 2.1 of
+ * the License, or (at your option) any later version.
+ *
+ * This library is distributed in the hope that it will be useful,
+ * but WITHOUT ANY WARRANTY; without even the implied warranty of
+ * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU
+ * Lesser General Public License for more details.
+ *
+ * You should have received a copy of the GNU Lesser General Public
+ * License along with this library; if not, write to the Free Software
+ * Foundation, Inc., 59 Temple Place, Suite 330, Boston, MA 02111-1307 USA
+ *
+ */
+package com.arsdigita.util;
+
+import java.util.Map;
+import java.util.HashMap;
+import java.util.List;
+import java.util.ArrayList;
+
+import org.apache.log4j.Logger;
+
+public class Exceptions {
+
+ private static Logger s_log = Logger.getLogger(Exceptions.class);
+
+ private static Map s_unwrappers = new HashMap();
+
+ public static Throwable[] unwrap(Throwable t) {
+ Assert.exists(t, Throwable.class);
+
+ List exceptions = new ArrayList();
+
+ exceptions.add(t);
+
+ if (s_log.isDebugEnabled()) {
+ s_log.debug("Trying to unwrap " + t.getClass());
+ }
+
+ Throwable current = t;
+
+ for (;;) {
+ Throwable inner = null;
+ ExceptionUnwrapper unwrapper = findUnwrapper(current.getClass());
+
+ if (unwrapper != null) {
+ inner = unwrapper.unwrap(current);
+ }
+
+ if (inner == null) {
+ Assert.exists(current, Throwable.class);
+ if (s_log.isDebugEnabled()) {
+ s_log.debug("Returning exception " + current.getClass());
+ }
+ return (Throwable[])exceptions.toArray(
+ new Throwable[exceptions.size()]);
+ }
+
+ if (s_log.isDebugEnabled()) {
+ s_log.debug("Inner exception is " + inner.getClass());
+ }
+
+ exceptions.add(inner);
+
+ current = inner;
+ }
+
+ // Unreachable
+ //throw new RuntimeException("this cannot happen");
+ }
+
+
+ public static void registerUnwrapper(Class exception,
+ ExceptionUnwrapper unwrapper) {
+ s_unwrappers.put(exception, unwrapper);
+ }
+
+ public static void unregisterUnwrapper(Class exception) {
+ s_unwrappers.remove(exception);
+ }
+
+ public static ExceptionUnwrapper getUnwrapper(Class exception) {
+ return (ExceptionUnwrapper)s_unwrappers.get(exception);
+ }
+
+ public static ExceptionUnwrapper findUnwrapper(Class exception) {
+ if (s_log.isDebugEnabled()) {
+ s_log.debug("Finding unwrapper for " + exception.getName());
+ }
+
+ Class current = exception;
+ ExceptionUnwrapper unwrapper = null;
+ while (unwrapper == null &&
+ current != null) {
+ if (s_log.isDebugEnabled()) {
+ s_log.debug("Trying class " + current.getName());
+ }
+ unwrapper = (ExceptionUnwrapper)s_unwrappers.get(current);
+ current = current.getSuperclass();
+ }
+
+ if (s_log.isDebugEnabled()) {
+ s_log.debug("Got unwrapper " +
+ (unwrapper != null ? unwrapper.getClass() : null));
+ }
+ return unwrapper;
+ }
+}
diff --git a/ccm-core/src/main/java/com/arsdigita/util/JavaPropertyReader.java b/ccm-core/src/main/java/com/arsdigita/util/JavaPropertyReader.java
new file mode 100644
index 000000000..a29379612
--- /dev/null
+++ b/ccm-core/src/main/java/com/arsdigita/util/JavaPropertyReader.java
@@ -0,0 +1,110 @@
+/*
+ * Copyright (C) 2003-2004 Red Hat Inc. All Rights Reserved.
+ *
+ * This library is free software; you can redistribute it and/or
+ * modify it under the terms of the GNU Lesser General Public License
+ * as published by the Free Software Foundation; either version 2.1 of
+ * the License, or (at your option) any later version.
+ *
+ * This library is distributed in the hope that it will be useful,
+ * but WITHOUT ANY WARRANTY; without even the implied warranty of
+ * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU
+ * Lesser General Public License for more details.
+ *
+ * You should have received a copy of the GNU Lesser General Public
+ * License along with this library; if not, write to the Free Software
+ * Foundation, Inc., 59 Temple Place, Suite 330, Boston, MA 02111-1307 USA
+ *
+ */
+package com.arsdigita.util;
+
+import com.arsdigita.util.parameter.ErrorList;
+import com.arsdigita.util.parameter.Parameter;
+import com.arsdigita.util.parameter.ParameterReader;
+import java.io.IOException;
+import java.io.InputStream;
+import java.util.Properties;
+import org.apache.log4j.Logger;
+
+/**
+ * An implementation of ParameterReader that uses
+ * standard Java properties to retrieve values.
+ *
+ * Subject to change.
+ *
+ * @see com.arsdigita.util.parameter.ParameterReader
+ * @see JavaPropertyWriter
+ * @author Justin Ross <jross@redhat.com>
+ * @version $Id$
+ */
+public class JavaPropertyReader implements ParameterReader {
+
+ private static final Logger s_log = Logger.getLogger
+ (JavaPropertyReader.class);
+
+ private final Properties m_props;
+
+ /**
+ * Constructs a parameter reader that uses props.
+ *
+ * @param props The Properties object that stores
+ * property values; it cannot be null
+ */
+ public JavaPropertyReader(final Properties props) {
+ Assert.exists(props, Properties.class);
+
+ m_props = props;
+ }
+
+ /**
+ * Loads the internal Properties object using
+ * in.
+ *
+ * @param in The InputStream that has the source
+ * properties; it cannot be null
+ */
+ public final void load(final InputStream in) {
+ Assert.exists(in, InputStream.class);
+
+ try {
+ m_props.load(in);
+ } catch (IOException ioe) {
+ throw new UncheckedWrapperException(ioe);
+ }
+ }
+
+ /**
+ * Reads a String value back for a
+ * param.
+ *
+ * @param param The Parameter whose value is
+ * requested; it cannot be null
+ * @param errors An ErrorList to trap any errors
+ * encountered when reading; it cannot be null
+ * @return The String value for param;
+ * it can be null
+ */
+ @Override
+ public final String read(final Parameter param, final ErrorList errors) {
+ if (s_log.isDebugEnabled()) {
+ s_log.debug("Reading " + param + " from " + m_props);
+ }
+
+ if (Assert.isEnabled()) {
+ Assert.exists(param, Parameter.class);
+ Assert.exists(errors, ErrorList.class);
+ }
+
+ return m_props.getProperty(param.getName());
+ }
+
+ /**
+ * Returns a String representation of this object.
+ *
+ * @return super.toString() + "," + properties.size()
+ */
+ @Override
+ public String toString() {
+ return super.toString() + "," + m_props.size();
+ }
+}
diff --git a/ccm-core/src/main/java/com/arsdigita/util/JavaPropertyWriter.java b/ccm-core/src/main/java/com/arsdigita/util/JavaPropertyWriter.java
new file mode 100644
index 000000000..94f116048
--- /dev/null
+++ b/ccm-core/src/main/java/com/arsdigita/util/JavaPropertyWriter.java
@@ -0,0 +1,119 @@
+/*
+ * Copyright (C) 2003-2004 Red Hat Inc. All Rights Reserved.
+ *
+ * This library is free software; you can redistribute it and/or
+ * modify it under the terms of the GNU Lesser General Public License
+ * as published by the Free Software Foundation; either version 2.1 of
+ * the License, or (at your option) any later version.
+ *
+ * This library is distributed in the hope that it will be useful,
+ * but WITHOUT ANY WARRANTY; without even the implied warranty of
+ * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU
+ * Lesser General Public License for more details.
+ *
+ * You should have received a copy of the GNU Lesser General Public
+ * License along with this library; if not, write to the Free Software
+ * Foundation, Inc., 59 Temple Place, Suite 330, Boston, MA 02111-1307 USA
+ *
+ */
+package com.arsdigita.util;
+
+import com.arsdigita.util.parameter.Parameter;
+import com.arsdigita.util.parameter.ParameterWriter;
+import java.io.IOException;
+import java.io.OutputStream;
+import java.util.Properties;
+import org.apache.log4j.Logger;
+
+/**
+ * Subject to change.
+ *
+ * An implementation of ParameterWriter that uses
+ * standard Java properties to store values.
+ *
+ * @see com.arsdigita.util.parameter.ParameterWriter
+ * @see JavaPropertyReader
+ * @author Justin Ross <jross@redhat.com>
+ * @version $Id$
+ */
+public class JavaPropertyWriter implements ParameterWriter {
+
+ private static final Logger s_log = Logger.getLogger
+ (JavaPropertyWriter.class);
+
+ private static final String s_header =
+ " Generated by " + JavaPropertyWriter.class.getName();
+
+ private final Properties m_props;
+
+ /**
+ * Constructs a parameter writer that uses props.
+ *
+ * @param props The Properties object that stores
+ * property values; it cannot be null
+ */
+ public JavaPropertyWriter(final Properties props) {
+ Assert.exists(props, Properties.class);
+
+ m_props = props;
+ }
+
+ /**
+ * Tells the internal property object to store its values to
+ * out.
+ *
+ * @param out The OutputStream to send the saved
+ * parameters to; it cannot be null
+ */
+ public final void store(final OutputStream out) {
+ if (s_log.isDebugEnabled()) {
+ s_log.debug("Storing " + this);
+ }
+
+ Assert.exists(out, OutputStream.class);
+
+ try {
+ m_props.store(out, s_header);
+ } catch (IOException ioe) {
+ throw new UncheckedWrapperException(ioe);
+ }
+ }
+
+ /**
+ * Writes a String value back for a
+ * param.
+ *
+ * @param param The Parameter whose value is
+ * to be written; it cannot be null
+ * @param value The String value to write out; it can
+ * be null
+ */
+ public final void write(final Parameter param, final String value) {
+ if (s_log.isDebugEnabled()) {
+ s_log.debug("Writing " + param + " with value " + value);
+ }
+
+ if (Assert.isEnabled()) {
+ Assert.exists(param, Parameter.class);
+ }
+
+ // XXX: Properties objects blow up when you try to put null
+ // values in them. This null check fixes it for now, but it
+ // doesn't let us explicitly write out a null value if that's
+ // what we actually want to store. I.e. our property store
+ // doesn't know the difference between a parameter being
+ // unspecified and a parameter being explicitly set to null.
+ if (value != null) {
+ m_props.setProperty(param.getName(), value);
+ }
+ }
+
+ /**
+ * Returns a String representation of this object.
+ *
+ * @return super.toString() + "," + properties.size()
+ */
+ public String toString() {
+ return super.toString() + "," + m_props.size();
+ }
+}
diff --git a/ccm-core/src/main/java/com/arsdigita/util/Lockable.java b/ccm-core/src/main/java/com/arsdigita/util/Lockable.java
new file mode 100644
index 000000000..45bcec656
--- /dev/null
+++ b/ccm-core/src/main/java/com/arsdigita/util/Lockable.java
@@ -0,0 +1,85 @@
+/*
+ * Copyright (C) 2001-2004 Red Hat Inc. All Rights Reserved.
+ *
+ * This library is free software; you can redistribute it and/or
+ * modify it under the terms of the GNU Lesser General Public License
+ * as published by the Free Software Foundation; either version 2.1 of
+ * the License, or (at your option) any later version.
+ *
+ * This library is distributed in the hope that it will be useful,
+ * but WITHOUT ANY WARRANTY; without even the implied warranty of
+ * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU
+ * Lesser General Public License for more details.
+ *
+ * You should have received a copy of the GNU Lesser General Public
+ * License along with this library; if not, write to the Free Software
+ * Foundation, Inc., 59 Temple Place, Suite 330, Boston, MA 02111-1307 USA
+ *
+ */
+package com.arsdigita.util;
+
+/**
+ * A common interface for all lockable parts of ACS. The locking mechanism makes
+ * it possible to safely split all data structures that are used inside a web
+ * server into ones that are constant across all requests and those that may
+ * change during a request.
+ *
+ *
+ * The distinction betwen static and request-specific data helps in optimizing
+ * the amount of memory that needs to be allocated for each request. Data
+ * structures that are static can be allocated and initialized ahead of time,
+ * e.g., in the init method of a servlet. The initialized data
+ * structures are then locked to make them immutable. This mechanism
+ * ensures that static structures that are supposed to be shared by many
+ * requests, often even concurrently, do not change and are not "polluted" by
+ * request-specific data.
+ *
+ *
+ * There is no automatic mechanism that makes an object immutable by itself. The
+ * right checks and operations need to be implemented by each class implementing
+ * Lockable.
+ *
+ *
+ * Bebop parameters are a good example of how one logical structure is split
+ * into two classes: the class {@link
+ * com.arsdigita.bebop.parameters.ParameterModel} is Lockable and
+ * only contains the description of the parameter in an HTTP request that is
+ * static and does not change on a per-request basis, such as the name of the
+ * parameter and the (Java) type that the parameter value should be converted
+ * to. The class {@link
+ * com.arsdigita.bebop.parameters.ParameterData} contains all the
+ * request-specific information for a parameter, such as the value of the
+ * parameter.
+ *
+ *
+ * Any class that implements Lockable is expected to be fully
+ * modifiable until its {@link #lock} method is called. From that point on, it
+ * is read-only and should throw exceptions whenever an attempt is made to
+ * modify it.
+ *
+ * @author David Lutterkort
+ * @version $Id$
+ */
+public interface Lockable {
+
+ /**
+ * Lock an object. Locked objects are to be considered immutable. Any
+ * attempt to modify them, e.g., through a setXXX method should
+ * lead to an exception.
+ *
+ *
+ * Most lockable Bebop classes throw an {@link
+ * java.lang.IllegalStateException} if an attempt is made to modify a locked
+ * instance.
+ */
+ void lock();
+
+ /**
+ * Return whether an object is locked and thus immutable, or can still be
+ * modified.
+ *
+ * @return
+ */
+ boolean isLocked();
+
+}
diff --git a/ccm-core/src/main/java/com/arsdigita/util/LockableImpl.java b/ccm-core/src/main/java/com/arsdigita/util/LockableImpl.java
new file mode 100644
index 000000000..0e9d5faf2
--- /dev/null
+++ b/ccm-core/src/main/java/com/arsdigita/util/LockableImpl.java
@@ -0,0 +1,63 @@
+/*
+ * Copyright (C) 2001-2004 Red Hat Inc. All Rights Reserved.
+ *
+ * This library is free software; you can redistribute it and/or
+ * modify it under the terms of the GNU Lesser General Public License
+ * as published by the Free Software Foundation; either version 2.1 of
+ * the License, or (at your option) any later version.
+ *
+ * This library is distributed in the hope that it will be useful,
+ * but WITHOUT ANY WARRANTY; without even the implied warranty of
+ * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU
+ * Lesser General Public License for more details.
+ *
+ * You should have received a copy of the GNU Lesser General Public
+ * License along with this library; if not, write to the Free Software
+ * Foundation, Inc., 59 Temple Place, Suite 330, Boston, MA 02111-1307 USA
+ *
+ */
+package com.arsdigita.util;
+
+/**
+ * A generic implementation of the Lockable interface.
+ *
+ * @see Lockable
+ *
+ * @author Michael Bryzek
+ * @version $Id$
+ *
+ */
+public class LockableImpl implements Lockable {
+
+ private boolean m_locked = false;
+
+ /**
+ * Lock an object. Locked objetcs are to be considered immutable. Any
+ * attempt to modify them, e.g., through a setXXX method should
+ * lead to an exception.
+ *
+ * @see Lockable#lock()
+ *
+ */
+ // must not be final so cms.ui.Grid.GridModelBuilder can override it.
+ @Override
+ public void lock() {
+ m_locked = true;
+ }
+
+ /**
+ * Return whether an object is locked and thus immutable, or can still be
+ * modified.
+ *
+ * @return
+ *
+ * @see Lockable#isLocked()
+ *
+ */
+ // must not be final so cms.ui.PropertySheet.PSTMBAdapter can override it.
+ @Override
+ public boolean isLocked() {
+ return m_locked;
+ }
+
+}
diff --git a/ccm-core/src/main/java/com/arsdigita/util/StringUtils.java b/ccm-core/src/main/java/com/arsdigita/util/StringUtils.java
new file mode 100644
index 000000000..db4354d5e
--- /dev/null
+++ b/ccm-core/src/main/java/com/arsdigita/util/StringUtils.java
@@ -0,0 +1,1362 @@
+/*
+ * This library is free software; you can redistribute it and/or
+ * modify it under the terms of the GNU Lesser General Public License
+ * as published by the Free Software Foundation; either version 2.1 of
+ * the License, or (at your option) any later version.
+ *
+ * This library is distributed in the hope that it will be useful,
+ * but WITHOUT ANY WARRANTY; without even the implied warranty of
+ * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU
+ * Lesser General Public License for more details.
+ *
+ * You should have received a copy of the GNU Lesser General Public
+ * License along with this library; if not, write to the Free Software
+ * Foundation, Inc., 59 Temple Place, Suite 330, Boston, MA 02111-1307 USA
+ */
+
+package com.arsdigita.util;
+
+import java.io.PrintWriter;
+import java.io.StringWriter;
+import java.util.ArrayList;
+import java.util.HashMap;
+import java.util.Iterator;
+import java.util.LinkedList;
+import java.util.List;
+import java.util.Map;
+import java.util.Set;
+import java.util.StringTokenizer;
+
+import org.apache.oro.text.perl.Perl5Util;
+import org.apache.oro.text.regex.MalformedPatternException;
+import org.apache.oro.text.regex.MatchResult;
+import org.apache.oro.text.regex.Pattern;
+import org.apache.oro.text.regex.PatternMatcher;
+import org.apache.oro.text.regex.PatternMatcherInput;
+import org.apache.oro.text.regex.Perl5Compiler;
+import org.apache.oro.text.regex.Perl5Matcher;
+import org.apache.oro.text.regex.Substitution;
+import org.apache.oro.text.regex.Util;
+
+import org.apache.log4j.Logger;
+
+/**
+ * A (static) class of generally-useful string utilities.
+ *
+ * @author Bill Schneider
+ */
+public class StringUtils {
+
+ private static final Logger s_log = Logger.getLogger(StringUtils.class);
+
+ private static Perl5Util s_re = new Perl5Util();
+
+ public static final String NEW_LINE = System.getProperty("line.separator");
+
+
+ private StringUtils() {
+ // can't instantiate me!
+ }
+
+
+ /**
+ * Tests if a string is empty.
+ * @param s A string to test
+ * @return true if s is null or empty;
+ * otherwise false
+ */
+ public static boolean emptyString(String s) {
+ boolean expr = (s == null || s.trim().length() == 0);
+ return expr;
+ }
+
+ /**
+ * Tests if a string is empty.
+ * @param o A string to test
+ * @return true if o is null or empty;
+ * otherwise false
+ */
+ public static boolean emptyString(Object o) {
+ boolean expr =
+ (o == null || (o instanceof String && ((String)o).length() ==0));
+ return expr;
+ }
+
+ /**
+ * If the String is null, returns an empty string. Otherwise,
+ * returns the string unaltered
+ */
+ public static String nullToEmptyString(String s) {
+ return (s == null) ? "" : s;
+ }
+
+ /**
+ * Escapes some "special" characters in HTML text (ampersand, angle
+ * brackets, quote).
+ * @param s The plain-text string to quote
+ * @return The string with special characters escpaed.
+ */
+ public static String quoteHtml(String s) {
+ if (s != null) {
+ StringBuffer result = new StringBuffer(s.length() + 10);
+ for (int i = 0; i < s.length(); i++) {
+ char ch = s.charAt(i);
+ switch (ch) {
+ case '&':
+ result.append("&");
+ break;
+ case '"':
+ result.append(""");
+ break;
+ case '<':
+ result.append("<");
+ break;
+ case '>':
+ result.append(">");
+ break;
+ default:
+ result.append(ch);
+ }
+ }
+ return result.toString();
+ } else {
+ return "";
+ }
+ }
+
+ /**
+ * Takes a plaintext string, and returns an HTML string that, when
+ * rendered by a web browser, will appear as the original input string
+ *
+ * @param s The input plaintext string
+ * @return A HTML string with blank lines coverted to
<p>+ * and ampersands/angle brackets escaped. + */ + public static String textToHtml(String s) { + s = quoteHtml(s); + s = s_re.substitute("s/\r\n\r\n/
/g", s); + s = s_re.substitute("s/\n\n/
/g", s); + s = s_re.substitute("s/\r\r/
/g", s);
+ s = s_re.substitute("s/\r\n/
/g", s);
+ s = s_re.substitute("s/\n/
/g", s);
+ s = s_re.substitute("s/\r/
/g", s);
+ return s;
+ }
+
+ /**
+ * Removes tags and substitutes P tags with newlines. For much
+ * more extensive conversion of HTML fragments to plain text
+ * equivalents, see {@link HtmlToText}.
+ */
+ public static String htmlToText(String s) {
+ if (s != null) {
+ // first take out new-lines
+ s = s_re.substitute("s/\n//g", s);
+ s = s_re.substitute("s/\r//g", s);
+ s = s_re.substitute("s/<[Pp]>/\n\n/g", s);
+ s = s_re.substitute("s/
/\n/ig", s);
+ // take out other tags
+ s = s_re.substitute("s/<([^>]*)>/ /g", s);
+ return s;
+ } else {
+ return "";
+ }
+ }
+
+
+ /**
+ * Converts plain text with simple inline markup
+ * into HTML. The following constructs are recognised:
+ *
+ *
text
+ * * my item
+ * * my next item
+ * * my final item
+ my item
+ * + my next item
+ * + my final item
$2|gx", s);
+ s = s_re.substitute("s|\u0001|=|gx", s);
+
+ if (s_log.isDebugEnabled()) {
+ s_log.debug("After styles {" + s + "}");
+ }
+
+
+ // Links are next on the list @text(url)
+ s = s_re.substitute("s|@@|\u0001|gx", s);
+ s = s_re.substitute("s|@([^\\(@]+)\\(([^\\)]+)\\)|$1|gx", s);
+ s = s_re.substitute("s|\u0001|@|gx", s);
+ if (s_log.isDebugEnabled()) {
+ s_log.debug("After links pass two {" + s + "}");
+ }
+
+ // Finally we can unobscure the hyperlinks
+ s = smartTextReplace(new UnobscureSubstitution(links),
+ "\u0002([^\u0002]+)\u0002", s);
+ s = s_re.substitute("s|\u0001|@|gx", s);
+ if (s_log.isDebugEnabled()) {
+ s_log.debug("After links pass three {" + s + "}");
+ }
+
+ // And those entities
+ s = smartTextReplace(new EntitySubstitution(),
+ "\u0003([^\u0003]+)\u0003", s);
+ if (s_log.isDebugEnabled()) {
+ s_log.debug("After entities (complete) {" + s + "}");
+ }
+
+ return s;
+ }
+
+ /**
+ *
+ * @param subst
+ * @param pattern
+ * @param s
+ * @return
+ */
+ private static String smartTextReplace(Substitution subst,
+ String pattern,
+ String s) {
+ Perl5Matcher matcher = new Perl5Matcher();
+ Perl5Compiler compiler = new Perl5Compiler();
+ StringBuffer result = new StringBuffer();
+ PatternMatcherInput input = new PatternMatcherInput(s);
+
+ try {
+ Util.substitute(result,
+ matcher,
+ compiler.compile(pattern),
+ subst,
+ input,
+ Util.SUBSTITUTE_ALL);
+ } catch (MalformedPatternException e) {
+ throw new UncheckedWrapperException("cannot perform substitution", e);
+ }
+ return result.toString();
+ }
+
+ /**
+ *
+ */
+ private static class TitledLinkSubstitution implements Substitution {
+ private Map m_hash;
+
+ public TitledLinkSubstitution(Map hash) {
+ m_hash = hash;
+ }
+
+ public void appendSubstitution(StringBuffer appendBuffer,
+ MatchResult match,
+ int substitutionCount,
+ PatternMatcherInput originalInput,
+ PatternMatcher matcher,
+ Pattern pattern) {
+ String title = match.group(1);
+ String link = match.group(2);
+ s_log.debug("Link: " + link);
+
+ Integer i = new Integer(m_hash.size());
+ s_log.debug("Key: " + i);
+ m_hash.put(i, link);
+ String dst = "@" + title + "(\u0002" + i.toString() + "\u0002)";
+ appendBuffer.append(dst);
+ s_log.debug("Encoded Link: " + dst);
+ }
+ }
+
+ /**
+ *
+ */
+ private static class UntitledLinkSubstitution implements Substitution {
+ private Map m_hash;
+
+ public UntitledLinkSubstitution(Map hash) {
+ m_hash = hash;
+ }
+
+ public void appendSubstitution(StringBuffer appendBuffer,
+ MatchResult match,
+ int substitutionCount,
+ PatternMatcherInput originalInput,
+ PatternMatcher matcher,
+ Pattern pattern) {
+ String link = match.group(1);
+ s_log.debug("Link: " + link);
+
+ Integer i = new Integer(m_hash.size());
+ s_log.debug("Key: " + i);
+ m_hash.put(i, link);
+ String dst = "@\u0002" + i.toString() + "\u0002(\u0002" +
+ i.toString() + "\u0002)";
+ appendBuffer.append(dst);
+ s_log.debug("Encoded Link: " + dst);
+ }
+ }
+
+ /**
+ *
+ */
+ private static class UnobscureSubstitution implements Substitution {
+ private Map m_hash;
+
+ public UnobscureSubstitution(Map hash) {
+ m_hash = hash;
+ }
+
+ public void appendSubstitution(StringBuffer appendBuffer,
+ MatchResult match,
+ int substitutionCount,
+ PatternMatcherInput originalInput,
+ PatternMatcher matcher,
+ Pattern pattern) {
+ String s = match.group(1);
+ s_log.debug("Key: " + s);
+
+ Integer i = new Integer(s);
+ appendBuffer.append((String)m_hash.get(i));
+ s_log.debug("Link: " + m_hash.get(i));
+ }
+ }
+
+ /**
+ *
+ */
+ private static class EntitySubstitution implements Substitution {
+ public void appendSubstitution(StringBuffer appendBuffer,
+ MatchResult match,
+ int substitutionCount,
+ PatternMatcherInput originalInput,
+ PatternMatcher matcher,
+ Pattern pattern) {
+ String s = match.group(1);
+ s_log.debug("Key: " + s);
+
+ appendBuffer.append((String)s_entities.get(s));
+ s_log.debug("Entity: " + s_entities.get(s));
+ }
+ }
+
+
+ /**
+ * Convert a string of items separated by a separator
+ * character to an (string)array of the items. sep is the separator
+ * character. Example: Input - s == "cat,house,dog" sep==','
+ * Output - {"cat", "house", "dog"}
+ * @param s string contains items separated by a separator character.
+ * @param sep separator character.
+ * @return Array of items.
+ */
+ public static String [] split(String s, char sep) {
+ ArrayList al = new ArrayList();
+ int start_pos, end_pos;
+ start_pos = 0;
+ while (start_pos < s.length()) {
+ end_pos = s.indexOf(sep, start_pos);
+ if (end_pos == -1) {
+ end_pos = s.length();
+ }
+ String found_item = s.substring(start_pos, end_pos);
+ al.add(found_item);
+ start_pos = end_pos + 1;
+ }
+ if (s.length() > 0 && s.charAt(s.length()-1) == sep) {
+ al.add(""); // In case last character is separator
+ }
+ String [] returned_array = new String[al.size()];
+ al.toArray(returned_array);
+ return returned_array;
+ }
+
+ /**
+ * Given a string, split it into substrings matching a regular + * expression that you supply. Parts of the original string which + * don't match the regular expression also appear as substrings. The + * upshot of this is that the final substrings can be concatenated + * to get the original string.
+ * + *As an example, let's say the original string is:
+ * + *+ * s = "/packages/foo/xsl/::vhost::/foo_::locale::.xsl"; + *+ * + *
We call the function like this:
+ * + *+ * output = splitUp (s, "/::\\w+::/"); + *+ * + *
The result (output) will be the following list:
+ * ("/packages/foo/xsl/", "::vhost::", "/foo_", "::locale::", ".xsl")
+ *
+ *
+ * Notice the important property that concatenating all these + * strings together will restore the original string.
+ * + *Here is another useful example. To split up HTML into elements + * and content, do:
+ * + *+ * output = splitUp (html, "/<.*?>/"); + *+ * + *
You will end up with something like this:
+ * + *
+ * ("The following text will be ", "", "bold", "", ".")
+ *
+ *
+ * @param s The original string to split.
+ * @param re The regular expression in the format required by
+ * {@link org.apache.oro.text.perl.Perl5Util#match(String, String)}.
+ * @return List of substrings.
+ *
+ * @author Richard W.M. Jones
+ *
+ * This is equivalent to the Perl "global match in array context",
+ * specifically: @a = /(RE)|(.+)/g;
Add a possible newline for proper wrapping.
+ * + *Checks the given String to see if it ends with whitspace. If so, it
+ * assumes this whitespace is intentional formatting and returns a reference
+ * to the original string. If not, a new String object is
+ * created containing the original plus a platform-dependent newline
+ * character obtained from {@link System#getProperty(String)
+ * System.getProperty("line.separator")}.
Notes: + * + *
find in str and
+ * replaces them with them with replace.
+ *
+ * @pre find != null
+ * @pre replace != null
+ */
+ public static String replace(final String str,
+ final String find,
+ final String replace) {
+
+ Assert.exists(find, String.class);
+ Assert.exists(replace, String.class);
+
+ if ( str == null ) return null;
+
+ int cur = str.indexOf(find);
+ if ( cur < 0 ) return str;
+
+ final int findLength = find.length();
+ // If replace is longer than find, assume the result is going to be
+ // slightly longer than the original string.
+ final int bufferLength =
+ replace.length() > findLength ? (int) (str.length() * 1.1) : str.length();
+ StringBuffer sb = new StringBuffer(bufferLength);
+ int last = 0;
+
+ if ( cur == 0 ) {
+ sb.append(replace);
+ cur = str.indexOf(find, cur+findLength);
+ last = findLength;
+ }
+
+ while ( cur > 0 ) {
+ sb.append(str.substring(last, cur));
+ sb.append(replace);
+ last = cur + findLength;
+ cur = str.indexOf(find, cur+findLength);
+ }
+ if ( last < str.length()-1) {
+ sb.append(str.substring(last));
+ }
+
+ return sb.toString();
+ }
+
+
+ /**
+ * An interface allowing the value for a placeholder to be
+ * dynamically generated.
+ */
+ public interface PlaceholderValueGenerator {
+ /**
+ * Returns the value corresponding to the supplied key
+ * placeholder.
+ *
+ * @param key the key being substituted
+ */
+ public String generate(String key);
+ }
+
+
+
+ /**
+ *
+ */
+ private static class HashSubstitution implements Substitution {
+ private Map m_hash;
+
+ public HashSubstitution(Map hash) {
+ m_hash = hash;
+ }
+
+ public void appendSubstitution(StringBuffer appendBuffer,
+ MatchResult match,
+ int substitutionCount,
+ PatternMatcherInput originalInput,
+ PatternMatcher matcher,
+ Pattern pattern) {
+ String placeholder = match.toString();
+ String key = placeholder.substring(2, placeholder.length()-2);
+
+ Object value = (m_hash.containsKey(key) ?
+ m_hash.get(key) :
+ placeholder);
+
+ if( s_log.isDebugEnabled() ) {
+ Object hashValue = m_hash.get( key );
+
+ s_log.debug( "Placeholder: " + placeholder );
+ s_log.debug( "Key: " + key );
+ if( null != value ) {
+ s_log.debug( "Value (" + value.getClass().getName() +
+ "): " + value.toString() );
+ }
+ if( null != hashValue ) {
+ s_log.debug( "Hash Value (" +
+ hashValue.getClass().getName() + "): " +
+ hashValue.toString() );
+ }
+ }
+
+ value = (m_hash.containsKey(key) ? m_hash.get(key) : "");
+
+ String val;
+ if( value instanceof PlaceholderValueGenerator ) {
+ PlaceholderValueGenerator gen = (PlaceholderValueGenerator)value;
+ val = gen.generate(key);
+ } else if( value.getClass().isArray() ) {
+ Object[] values = (Object[]) value;
+
+ StringBuffer buf = new StringBuffer();
+ for( int i = 0; i < values.length; i++ ) {
+ buf.append( values[i].toString() );
+ if( (values.length - 1) != i ) {
+ buf.append( ", " );
+ }
+ }
+
+ val = buf.toString();
+ } else {
+ val = value.toString();
+ }
+
+ appendBuffer.append(val);
+ }
+ }
+
+ /**
+ * @throws NullPointerException if throwable is null
+ */
+ public static String getStackTrace(Throwable throwable) {
+ if (throwable==null) { throw new NullPointerException("throwable"); }
+
+ StringWriter sw = new StringWriter();
+ PrintWriter pw = new PrintWriter(sw);
+ throwable.printStackTrace(pw);
+ pw.close();
+ return sw.toString();
+ }
+
+ /**
+ * Returns a list of lines where each line represents one level
+ * in the stack trace captured by throwable.
+ *
+ * For a stack trace like this:
+ * + *+ * java.lang.Throwable + * at Main.level3(Main.java:19) + * at Main.level2(Main.java:15) + * at Main.level1(Main.java:11) + * at Main.main(Main.java:7) + *+ * + *
the returned list looks like this:
+ * + *+ * ["java.lang.Throwable", + * "Main.level3(Main.java:20)", + * "Main.level2(Main.java:15)", + * "Main.level1(Main.java:11)", + * "Main.main(Main.java:7)"] + *+ * + * @see #getStackTrace(Throwable) + * @throws NullPointerException if
throwable is null
+ */
+ public static List getStackList(Throwable throwable) {
+ StringTokenizer tkn = new StringTokenizer
+ (getStackTrace(throwable), System.getProperty("line.separator"));
+ List list = new LinkedList();
+ while ( tkn.hasMoreTokens() ) {
+ String token = tkn.nextToken().trim();
+ if ( "".equals(token) ) { continue; }
+ if ( token.startsWith("at ") ) {
+ list.add(token.substring(3));
+ } else {
+ list.add(token);
+ }
+ }
+
+ return list;
+ }
+
+ /**
+ * Convert a name into a URL form, the java equivalent of
+ * "manipulate-input.js"
+ *
+ * For example, "Business promotions!" will be converted to
+ * "business-promotions".
+ *
+ * @param name
+ * the to be converted into a URL.
+ * @return the converted name, possibly unchanged and null if the input is null.
+ */
+ public static String urlize(String name) {
+ if (name == null) {
+ return null;
+ }
+ StringBuffer urlizedName = new StringBuffer(name.length());
+
+ for (int i = 0; i < name.length(); i++) {
+ char ch = name.charAt(i);
+
+ if (Character.isLetter(ch)) {
+ urlizedName.append(Character.toLowerCase(ch));
+ }
+ else if (Character.isDigit(ch) || ch == '_' || ch == '-') {
+ urlizedName.append(ch);
+ }
+ else if (ch == ' ' || ch == '&' || ch == '/') {
+ urlizedName.append('-');
+ }
+ }
+ return urlizedName.toString();
+ }
+}
+
+
diff --git a/ccm-core/src/main/java/com/arsdigita/util/UncheckedWrapperException.java b/ccm-core/src/main/java/com/arsdigita/util/UncheckedWrapperException.java
new file mode 100644
index 000000000..4c5253aba
--- /dev/null
+++ b/ccm-core/src/main/java/com/arsdigita/util/UncheckedWrapperException.java
@@ -0,0 +1,207 @@
+/*
+ * Copyright (C) 2001-2004 Red Hat Inc. All Rights Reserved.
+ *
+ * This library is free software; you can redistribute it and/or
+ * modify it under the terms of the GNU Lesser General Public License
+ * as published by the Free Software Foundation; either version 2.1 of
+ * the License, or (at your option) any later version.
+ *
+ * This library is distributed in the hope that it will be useful,
+ * but WITHOUT ANY WARRANTY; without even the implied warranty of
+ * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU
+ * Lesser General Public License for more details.
+ *
+ * You should have received a copy of the GNU Lesser General Public
+ * License along with this library; if not, write to the Free Software
+ * Foundation, Inc., 59 Temple Place, Suite 330, Boston, MA 02111-1307 USA
+ *
+ */
+package com.arsdigita.util;
+
+import org.apache.log4j.Logger;
+
+/**
+ * A wrapper exception that can be used to rethrow another
+ * exception.
+ *
+ * TODO: This should become a skeleton when/if we switch to Java 1.4.
+ * http://java.sun.com/j2se/1.4/docs/guide/lang/chained-exceptions.html
+ *
+ * The basic exception methods are overridden with methods that
+ * combine this wrapper and its root cause, so it can be
+ * treated just like any normal exception in actual use.
+ *
+ * Note that it is not necessary to provide a string along
+ * with a root cause; in particular, the following usage:
+ * new UncheckedWrapperException(e); is more correct than
+ * new UncheckedWrapperException(e.getMessage(), e);
+ *
+ * @author David Eison
+ * @version $Id$
+ */
+public class UncheckedWrapperException extends RuntimeException {
+
+ private static final Logger logger = Logger.getLogger(
+ UncheckedWrapperException.class);
+
+ static {
+ logger.debug("Static initalizer starting...");
+ Exceptions.registerUnwrapper(
+ UncheckedWrapperException.class,
+ new ExceptionUnwrapper() {
+
+ public Throwable unwrap(Throwable t) {
+ UncheckedWrapperException ex =
+ (UncheckedWrapperException) t;
+ return ex.getRootCause();
+ }
+ });
+ logger.debug("Static initalizer finished.");
+ }
+ Throwable m_rootCause;
+
+ /**
+ * Constructor which only takes a msg, which will cause this
+ * UncheckedWrapperException to behave like a normal RuntimeException.
+ * While it doesn't seem to make a lot of sense to have a wrapper
+ * exception that doesn't wrap anything, this is needed so that it
+ * can be used as a direct replacement for RuntimeException.
+ */
+ public UncheckedWrapperException(String msg) {
+ this(msg, null);
+ }
+
+ /**
+ * Constructor which takes a root cause
+ * that this exception will be wrapping.
+ */
+ public UncheckedWrapperException(Throwable rootCause) {
+ this(null, rootCause);
+ }
+
+ /**
+ * Constructor which takes a message string and a root cause
+ * that this exception will be wrapping. The message string
+ * should be something different than rootCause.getMessage()
+ * would normally provide.
+ */
+ public UncheckedWrapperException(String s, Throwable rootCause) {
+ super(s);
+ this.m_rootCause = rootCause;
+ }
+
+ /**
+ * Throws an UncheckedWrapperException, and ensurs that it is logged at ERROR priority.
+ *
+ * @param source Class having the error. For Log4J reporting
+ * @param msg Error message
+ * @param rootCause The root cause exception
+ *
+ * @throws UncheckedWrapperException
+ */
+ public static void throwLoggedException(Class source, String msg,
+ Throwable rootCause) throws
+ UncheckedWrapperException {
+ Logger log = Logger.getLogger(source);
+ log.error(msg, rootCause);
+
+ throw new UncheckedWrapperException(msg, rootCause);
+ }
+
+ /**
+ * Indicates if this exception has a root cause.
+ */
+ public boolean hasRootCause() {
+ return m_rootCause != null;
+ }
+
+ /**
+ * Gets the root cause of this exception.
+ */
+ public Throwable getRootCause() {
+ return m_rootCause;
+ }
+
+ // All further methods override normal throwable behavior to
+ // combine information w/ the root cause.
+ /**
+ * Get a string representing this exception and the root cause.
+ */
+ @Override
+ public String toString() {
+ return toString(this.getClass());
+ }
+
+ /**
+ * Get a string representing this exception and the root cause.
+ *
+ * Functions like normal toString, except that the name of the
+ * provided class will be used instead of the name of the
+ * unchecked wrapper exception. Useful when another exception
+ * class is using an unchecked wrapper exception to delegate
+ * to.
+ */
+ public String toString(Class delegatingClass) {
+ // default toString calls getMessage, so we don't want to rely on it
+ // here.
+ StringBuffer b = new StringBuffer(delegatingClass.getName());
+ String superMsg = super.getMessage();
+ if (superMsg != null) {
+ b.append(": ").append(superMsg);
+ }
+ if (m_rootCause != null) {
+ b.append(" (root cause: ").append(m_rootCause.toString());
+ b.append(")");
+ }
+ return b.toString();
+ }
+
+ /**
+ * This exception's message and the root cause's.
+ */
+ @Override
+ public String getMessage() {
+ if (m_rootCause != null) {
+ return super.getMessage() + " (root cause: " + m_rootCause.
+ getMessage() + ")";
+ } else {
+ return super.getMessage();
+ }
+ }
+
+ /**
+ * Stack trace for the root cause.
+ */
+ @Override
+ public void printStackTrace() {
+ super.printStackTrace();
+ if (m_rootCause != null) {
+ System.err.print("Root cause: ");
+ m_rootCause.printStackTrace();
+ }
+ }
+
+ /**
+ * Stack trace for the root cause.
+ */
+ @Override
+ public void printStackTrace(java.io.PrintStream s) {
+ super.printStackTrace(s);
+ if (m_rootCause != null) {
+ s.println("Root cause: ");
+ m_rootCause.printStackTrace(s);
+ }
+ }
+
+ /**
+ * Stack trace for the root cause.
+ */
+ @Override
+ public void printStackTrace(java.io.PrintWriter s) {
+ super.printStackTrace(s);
+ if (m_rootCause != null) {
+ s.println("Root cause: ");
+ m_rootCause.printStackTrace(s);
+ }
+ }
+}
diff --git a/ccm-core/src/main/java/com/arsdigita/util/parameter/AbstractParameter.java b/ccm-core/src/main/java/com/arsdigita/util/parameter/AbstractParameter.java
new file mode 100644
index 000000000..3fcaf5573
--- /dev/null
+++ b/ccm-core/src/main/java/com/arsdigita/util/parameter/AbstractParameter.java
@@ -0,0 +1,339 @@
+/*
+ * Copyright (C) 2003-2004 Red Hat Inc. All Rights Reserved.
+ *
+ * This library is free software; you can redistribute it and/or
+ * modify it under the terms of the GNU Lesser General Public License
+ * as published by the Free Software Foundation; either version 2.1 of
+ * the License, or (at your option) any later version.
+ *
+ * This library is distributed in the hope that it will be useful,
+ * but WITHOUT ANY WARRANTY; without even the implied warranty of
+ * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU
+ * Lesser General Public License for more details.
+ *
+ * You should have received a copy of the GNU Lesser General Public
+ * License along with this library; if not, write to the Free Software
+ * Foundation, Inc., 59 Temple Place, Suite 330, Boston, MA 02111-1307 USA
+ *
+ */
+package com.arsdigita.util.parameter;
+
+import com.arsdigita.util.Assert;
+import org.apache.commons.beanutils.ConversionException;
+
+/**
+ * A base implementation of the Parameter interface.
+ *
+ * It offers subclasses use of the Apache BeanUtils framework, should
+ * they opt to use it.
+ *
+ * Methods of the form doXXX are extension points for subclasses.
+ * The isRequired() and getDefaultValue()
+ * methods may also be overriden.
+ *
+ * Subject to change.
+ *
+ * @see Parameter
+ * @author Justin Ross <jross@redhat.com>
+ * @version $Id$
+ */
+public abstract class AbstractParameter implements Parameter {
+
+ private final String m_name;
+ private final Class m_type;
+ private final int m_multiplicity;
+ private final Object m_default;
+ private ParameterInfo m_info;
+
+ /**
+ * Constructs a new parameter with the default value
+ * defaalt and using the beanutils converter
+ * registered for type.
+ *
+ * @param name The name of the parameter; it cannot be null
+ * @param multiplicity The multiplicity type of the parameter
+ * @param defaalt The default value to use if the value is unset
+ * or is null
+ * @param type The Class whose beanutils converter
+ * will be used to unmarshal literal values
+ */
+ protected AbstractParameter(final String name,
+ final int multiplicity,
+ final Object defaalt,
+ final Class type) {
+ if (Assert.isEnabled()) {
+ Assert.exists(name, String.class);
+ }
+
+ m_name = name;
+ m_type = type;
+ m_multiplicity = multiplicity;
+ m_default = defaalt;
+ }
+
+ /**
+ * Constructs a new parameter with the default value
+ * defaalt.
+ *
+ * @param name The name of the parameter; it cannot be null
+ * @param multiplicity The multiplicity type of the parameter
+ * @param defaalt The default value to use if the value is unset
+ * or is null
+ */
+ protected AbstractParameter(final String name,
+ final int multiplicity,
+ final Object defaalt) {
+ // XXX Get rid of this constructor?
+ this(name, multiplicity, defaalt, null);
+ }
+
+ /**
+ * Constructs a new parameter using the beanutils converter for
+ * type type. By default, the parameter is required
+ * and has no default.
+ *
+ * @param name The name of the parameter; it cannot be null
+ * @param type The Class whose beanutils converter
+ * will be used to unmarshal literal values
+ */
+ protected AbstractParameter(final String name,
+ final Class type) {
+ this(name, Parameter.REQUIRED, null, type);
+ }
+
+ /**
+ * Parameter users may override this method to make the multiplicity of
+ * the parameter dependent on the multiplicity of related parameters.
+ *
+ * @see Parameter#isRequired()
+ */
+ public boolean isRequired() {
+ return m_multiplicity == Parameter.REQUIRED;
+ }
+
+ /**
+ * @see Parameter#getName()
+ */
+ public final String getName() {
+ return m_name;
+ }
+
+ /**
+ * Parameter users may override this method to achieve dynamic
+ * defaulting.
+ *
+ * @see Parameter#getDefaultValue()
+ */
+ public Object getDefaultValue() {
+ return m_default;
+ }
+
+ /**
+ * @see Parameter#getInfo()
+ */
+ public final ParameterInfo getInfo() {
+ return m_info;
+ }
+
+ /**
+ * @see Parameter#setInfo(ParameterInfo)
+ */
+ public final void setInfo(final ParameterInfo info) {
+ m_info = info;
+ }
+
+ //
+ // Lifecycle events
+ //
+
+ /**
+ * Gets the parameter value as a Java object.
+ *
+ * The value will have a specific runtime type and so may be
+ * appropriately cast.
+ *
+ * Reading typically follows the following procedure:
+ *
+ * readererrors. Callers of this method will
+ * typically construct an ErrorList in which to
+ * collect errors.
+ * Actually calls {@link #doRead(ParameterReader,ErrorList)} (as an
+ * extension point for subclasses).
+ *
+ * @param reader The ParameterReader from which to
+ * recover a string literal value; it cannot be null
+ * @param errors The ErrorList in which to collect
+ * any errors encountered; it cannot be null
+ * @return The Java object value of the parameter
+ *
+ */
+ public final Object read(final ParameterReader reader,
+ final ErrorList errors) {
+ if (Assert.isEnabled()) {
+ Assert.exists(reader, ParameterReader.class);
+ Assert.exists(errors, ErrorList.class);
+ }
+
+ return doRead(reader, errors);
+ }
+
+ /**
+ * Extension point to read the value of the parameter from reader.
+ *
+ * It unmarshals the value, and returns it. If any errors are encountered,
+ * they are added to errors.
+ *
+ * If the literal string value from reader is not null,
+ * this method delegates to {@link #unmarshal(String,ErrorList)}.
+ *
+ * This implementation is suited to a parameter with a singular
+ * scalar value. Subclasses that are compound parameters should
+ * override this method to delegate to child parameters.
+ *
+ * @param reader The ParameterReader that will supply
+ * the literal stored value for this parameter; it cannot be null
+ * @param errors The ErrorList that will trap any
+ * errors encountered; it cannot be null
+ */
+ protected Object doRead(final ParameterReader reader,
+ final ErrorList errors) {
+ final String string = reader.read(this, errors);
+
+ if (string == null) {
+ return null;
+ } else {
+ return unmarshal(string, errors);
+ }
+ }
+
+ /**
+ * Converts a literal String value,
+ * value, to a Java object, which is returned.
+ *
+ * @param value The String value to convert from; it
+ * cannot be null
+ * @param errors An ErrorList that holds any errors
+ * encountered during unmarshaling; it cannot be null
+ */
+ protected Object unmarshal(final String value, final ErrorList errors) {
+ if (Assert.isEnabled()) {
+ Assert.exists(value, String.class);
+ Assert.exists(errors, String.class);
+ }
+
+ try {
+ return Converters.convert(m_type, value);
+ } catch (ConversionException ce) {
+ errors.add(new ParameterError(this, ce));
+ return null;
+ }
+ }
+
+ /**
+ * Calls {@link #doValidate(Object,ErrorList)} if
+ * value is not null. Otherwise, if the value is
+ * required and null, an error is added to
+ * errors and doValidate is not called.
+ *
+ * @see Parameter#validate(Object,ErrorList)
+ */
+ public final void validate(final Object value, final ErrorList errors) {
+ Assert.exists(errors, ErrorList.class);
+
+ if (value == null) {
+ // If the value is null, validation stops here.
+
+ if (isRequired()) {
+ final ParameterError error = new ParameterError
+ (this, "The value must not be null");
+ errors.add(error);
+ }
+ } else {
+ // Always do further validation for non-null values.
+
+ doValidate(value, errors);
+ }
+ }
+
+ /**
+ * Validates value, placing any validation errors in
+ * errors. This particular implementation does
+ * nothing. Subclasses are expected to add specific validation
+ * behaviors.
+ *
+ * @param value The value to validate; it cannot be null
+ * @param errors The ErrorList that traps validation
+ * errors; it cannot be null
+ */
+ protected void doValidate(final Object value, final ErrorList errors) {
+ if (Assert.isEnabled()) {
+ Assert.exists(value, Object.class);
+ Assert.exists(errors, ErrorList.class);
+ }
+
+ // Nothing
+ }
+
+ /**
+ * Calls {@link #doWrite(ParameterWriter,Object)}.
+ *
+ * @see Parameter#write(ParameterWriter,Object)
+ */
+ public final void write(final ParameterWriter writer, final Object value) {
+ Assert.exists(writer);
+
+ // XXX what to do about nulls here?
+
+ doWrite(writer, value);
+ }
+
+ /**
+ * Marshals and writes value to writer.
+ *
+ * This implementation is suited to a parameter with a singular
+ * scalar value. Subclasses that are compound parameters should
+ * override this method to delegate to child parameters.
+ *
+ * @param writer The ParameterWriter we write to; it
+ * cannot be null
+ * @param value The value to write; it may be null
+ */
+ protected void doWrite(final ParameterWriter writer, final Object value) {
+ writer.write(this, marshal(value));
+ }
+
+ /**
+ * Converts value to a representative
+ * String, which is returned.
+ *
+ * @param value The value to marshal; it may be null
+ * @return The String literal representation of
+ * value; it may be null
+ */
+ protected String marshal(final Object value) {
+ if (value == null) {
+ return null;
+ } else {
+ return value.toString();
+ }
+ }
+
+ /**
+ * Returns a String representation of this object.
+ *
+ * @return super.toString() + "," + getName() + "," +
+ * isRequired()
+ */
+ public String toString() {
+ return super.toString() + "," + getName() + "," + isRequired();
+ }
+}
diff --git a/ccm-core/src/main/java/com/arsdigita/util/parameter/AbstractParameterContext.java b/ccm-core/src/main/java/com/arsdigita/util/parameter/AbstractParameterContext.java
new file mode 100644
index 000000000..7714d68b3
--- /dev/null
+++ b/ccm-core/src/main/java/com/arsdigita/util/parameter/AbstractParameterContext.java
@@ -0,0 +1,307 @@
+/*
+ * Copyright (C) 2003-2004 Red Hat Inc. All Rights Reserved.
+ *
+ * This library is free software; you can redistribute it and/or
+ * modify it under the terms of the GNU Lesser General Public License
+ * as published by the Free Software Foundation; either version 2.1 of
+ * the License, or (at your option) any later version.
+ *
+ * This library is distributed in the hope that it will be useful,
+ * but WITHOUT ANY WARRANTY; without even the implied warranty of
+ * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU
+ * Lesser General Public License for more details.
+ *
+ * You should have received a copy of the GNU Lesser General Public
+ * License along with this library; if not, write to the Free Software
+ * Foundation, Inc., 59 Temple Place, Suite 330, Boston, MA 02111-1307 USA
+ *
+ */
+package com.arsdigita.util.parameter;
+
+import com.arsdigita.util.Assert;
+import com.arsdigita.util.UncheckedWrapperException;
+import java.io.IOException;
+import java.io.InputStream;
+import java.util.ArrayList;
+import java.util.HashMap;
+import java.util.Iterator;
+import java.util.LinkedList;
+import java.util.List;
+import java.util.Map;
+import java.util.Properties;
+import org.apache.log4j.Logger;
+
+/**
+ * A base implementation of the ParameterContext
+ * interface.
+ *
+ * Subject to change.
+ *
+ * @see com.arsdigita.util.parameter.ParameterContext
+ * @author Justin Ross <jross@redhat.com>
+ * @version $Id$
+ */
+public abstract class AbstractParameterContext implements ParameterContext {
+
+ private static final Logger s_log = Logger.getLogger
+ (AbstractParameterContext.class);
+
+ private final MapParameter m_param;
+ private final HashMap m_map;
+ private final Properties m_info;
+
+ /**
+ * Constructs a parameter context.
+ */
+ public AbstractParameterContext() {
+ m_param = new MapParameter("root");
+ m_map = new HashMap();
+ m_info = new Properties();
+ }
+
+ /**
+ * Registers param to the context.
+ *
+ * @param param The Parameter being registered; it
+ * cannot be null
+ */
+ public final void register(final Parameter param) {
+ if (s_log.isDebugEnabled()) {
+ s_log.debug("Registering " + param + " on " + this);
+ }
+
+ if (Assert.isEnabled()) {
+ Assert.exists(param, Parameter.class);
+ Assert.isTrue(!m_param.contains(param),
+ param + " is already registered");
+ }
+
+ m_param.add(param);
+ }
+
+ /**
+ * @see ParameterContext#getParameters()
+ */
+ public final Parameter[] getParameters() {
+ final ArrayList list = new ArrayList();
+ final Iterator params = m_param.iterator();
+
+ while (params.hasNext()) {
+ list.add(params.next());
+ }
+
+ return (Parameter[]) list.toArray(new Parameter[list.size()]);
+ }
+
+ /**
+ * Gets the unmarshaled value of param.
+ *
+ * If the loaded value is null, param.getDefaultValue()
+ * is returned.
+ *
+ * @param param The named Parameter whose value to
+ * retrieve; it cannot be null
+ * @return The unmarshaled Java object value of param
+ */
+ public Object get(final Parameter param) {
+ return get(param, param.getDefaultValue());
+ }
+
+ /**
+ * Gets the unmarshaled value of param, returning
+ * dephalt if param's value is null.
+ *
+ * @param param The Parameter whose value to
+ * retrieve; it cannot be null
+ * @param dephalt The fallback default value; it may be null
+ * @return The unmarshaled Java object value of param
+ * or dephalt if the former is null
+ */
+ public Object get(final Parameter param, final Object dephault) {
+ if (Assert.isEnabled()) {
+ Assert.exists(param, Parameter.class);
+ Assert.isTrue(m_param.contains(param),
+ param + " has not been registered");
+ }
+
+ // XXX check for is loaded?
+
+ final Object value = m_map.get(param);
+
+ if (value == null) {
+ return dephault;
+ } else {
+ return value;
+ }
+ }
+
+ /**
+ * @see ParameterContext#get(Parameter,Object)
+ */
+ public void set(final Parameter param, final Object value) {
+ if (s_log.isDebugEnabled()) {
+ s_log.debug("Setting " + param + " to " + value);
+ }
+
+ Assert.exists(param, Parameter.class);
+
+ m_map.put(param, value);
+ }
+
+ /**
+ * Reads and unmarshals all values associated with the registered
+ * parameters from reader. Any errors are returned.
+ *
+ * @param reader The ParameterReader from which to
+ * fetch the values; it cannot be null
+ * @return An ErrorList containing any errors
+ * encountered while loading; it cannot be null
+ */
+ public final ErrorList load(final ParameterReader reader) {
+ final ErrorList errors = new ErrorList();
+
+ load(reader, errors);
+
+ return errors;
+ }
+
+ /**
+ * Reads and unmarshals all values associated with the registered
+ * parameters from reader. If any errors are
+ * encountered, they are added to errors.
+ *
+ * @param reader The ParameterReader from which to
+ * fetch the values; it cannot be null
+ * @param errors The ErrorList that captures any
+ * errors while loading; it cannot be null
+ */
+ public final void load(final ParameterReader reader,
+ final ErrorList errors) {
+ if (Assert.isEnabled()) {
+ Assert.exists(reader, ParameterReader.class);
+ Assert.exists(errors, ErrorList.class);
+ }
+
+ m_map.putAll((Map) m_param.read(reader, errors));
+ }
+
+ /**
+ * Validates all values associated with the registered parameters.
+ * Any errors encountered are returned.
+ *
+ * @return An ErrorList containing validation errors;
+ * it cannot be null
+ */
+ public final ErrorList validate() {
+ final ErrorList errors = new ErrorList();
+
+ m_param.validate(m_map, errors);
+
+ return errors;
+ }
+
+ /**
+ * @see ParameterContext#validate(ErrorList)
+ */
+ public final void validate(final ErrorList errors) {
+ Assert.exists(errors, ErrorList.class);
+
+ m_param.validate(m_map, errors);
+ }
+
+ /**
+ * @see ParameterContext#save(ParameterWriter)
+ */
+ public final void save(ParameterWriter writer) {
+ m_param.write(writer, m_map);
+ }
+
+ /**
+ * Loads source data for ParameterInfo objects from
+ * the file YourClass_parameter.properties next to
+ * YourClass.class.
+ *
+ * YourClass_parameter.properties:
+ *
+ * + * + * @see ParameterInfo + */ + protected final void loadInfo() { + final InputStream in = findInfo(getClass()); + + try { + m_info.load(in); + } catch (IOException ioe) { + throw new UncheckedWrapperException(ioe); + } + + final Iterator params = m_param.iterator(); + + while (params.hasNext()) { + final Parameter param = (Parameter) params.next(); + + param.setInfo(new Info(param)); + } + } + + // + // Private classes and methods + // + + private class Info implements ParameterInfo { + private final String m_name; + + Info(final Parameter param) { + m_name = param.getName(); + } + + public final String getTitle() { + return m_info.getProperty(m_name + ".title"); + } + + public final String getPurpose() { + return m_info.getProperty(m_name + ".purpose"); + } + + public final String getExample() { + return m_info.getProperty(m_name + ".example"); + } + + public final String getFormat() { + return m_info.getProperty(m_name + ".format"); + } + } + + private static InputStream findInfo(final Class klass) { + final List files = new LinkedList(); + InputStream in = findInfo(klass, files); + if ( in == null ) { + throw new IllegalStateException + ("Could not find any of the following files: " + files); + } + return in; + } + + private static InputStream findInfo(final Class klass, final List files) { + if (klass == null) { return null; } + final String name = + klass.getName().replace('.', '/') + "_parameter.properties"; + files.add(name); + if ( klass.getClassLoader() == null ) { + return null; + } + final InputStream in = klass.getClassLoader().getResourceAsStream(name); + + if (in == null) { + return findInfo(klass.getSuperclass(), files); + } else { + return in; + } + } +} diff --git a/ccm-core/src/main/java/com/arsdigita/util/parameter/BooleanParameter.java b/ccm-core/src/main/java/com/arsdigita/util/parameter/BooleanParameter.java new file mode 100644 index 000000000..2365982cf --- /dev/null +++ b/ccm-core/src/main/java/com/arsdigita/util/parameter/BooleanParameter.java @@ -0,0 +1,48 @@ +/* + * Copyright (C) 2003-2004 Red Hat Inc. All Rights Reserved. + * + * This library is free software; you can redistribute it and/or + * modify it under the terms of the GNU Lesser General Public License + * as published by the Free Software Foundation; either version 2.1 of + * the License, or (at your option) any later version. + * + * This library is distributed in the hope that it will be useful, + * but WITHOUT ANY WARRANTY; without even the implied warranty of + * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU + * Lesser General Public License for more details. + * + * You should have received a copy of the GNU Lesser General Public + * License along with this library; if not, write to the Free Software + * Foundation, Inc., 59 Temple Place, Suite 330, Boston, MA 02111-1307 USA + * + */ +package com.arsdigita.util.parameter; + +import org.apache.commons.beanutils.converters.BooleanConverter; + +/** + * Subject to change. + * + * A parameter representing a Java+ * yourforum.notification_enabled.title=Flag to enable forum notifications + * yourforum.notification_enabled.purpose=Enables or disables forum notifications + * yourforum.notification_enabled.example=true + * yourforum.notifiaction_enabled.format=true|false + *
Boolean.
+ *
+ * @see java.lang.Boolean
+ * @see Parameter
+ * @author Justin Ross <jross@redhat.com>
+ * @version $Id$
+ */
+public class BooleanParameter extends AbstractParameter {
+
+ static {
+ Converters.set(Boolean.class, new BooleanConverter());
+ }
+
+ public BooleanParameter(final String name) {
+ super(name, Boolean.class);
+ }
+
+ public BooleanParameter(final String name,
+ final int multiplicity,
+ final Object defaalt) {
+ super(name, multiplicity, defaalt, Boolean.class);
+ }
+}
diff --git a/ccm-core/src/main/java/com/arsdigita/util/parameter/CSVParameterReader.java b/ccm-core/src/main/java/com/arsdigita/util/parameter/CSVParameterReader.java
new file mode 100644
index 000000000..7070edf58
--- /dev/null
+++ b/ccm-core/src/main/java/com/arsdigita/util/parameter/CSVParameterReader.java
@@ -0,0 +1,266 @@
+/*
+ * Copyright (C) 2003-2004 Red Hat Inc. All Rights Reserved.
+ *
+ * This library is free software; you can redistribute it and/or
+ * modify it under the terms of the GNU Lesser General Public License
+ * as published by the Free Software Foundation; either version 2.1 of
+ * the License, or (at your option) any later version.
+ *
+ * This library is distributed in the hope that it will be useful,
+ * but WITHOUT ANY WARRANTY; without even the implied warranty of
+ * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU
+ * Lesser General Public License for more details.
+ *
+ * You should have received a copy of the GNU Lesser General Public
+ * License along with this library; if not, write to the Free Software
+ * Foundation, Inc., 59 Temple Place, Suite 330, Boston, MA 02111-1307 USA
+ *
+ */
+package com.arsdigita.util.parameter;
+
+import com.arsdigita.util.UncheckedWrapperException;
+// import com.arsdigita.util.parameter.ErrorList;
+// import com.arsdigita.util.parameter.Parameter;
+// import com.arsdigita.util.parameter.ParameterLoader;
+// import com.arsdigita.util.parameter.ParameterValue;
+import java.io.IOException;
+import java.io.LineNumberReader;
+import java.io.Reader;
+import java.util.ArrayList;
+import java.util.HashMap;
+import java.util.List;
+
+/**
+ * Processes an input stream (a set of lines, each containing a comma separated
+ * list of parameter values) and ....
+ *
+ * @author Justin Ross <jross@redhat.com>
+ * @version $Id$
+ */
+public final class CSVParameterReader implements ParameterReader {
+
+ private final LineNumberReader m_reader;
+ private final Parameter[] m_params;
+ private final HashMap m_line;
+
+ /**
+ * Constructor
+ *
+ * @param reader: input stream to read values
+ * @param params: array of parameter objects to store procecced values
+ */
+ public CSVParameterReader(final Reader reader, final Parameter[] params) {
+ m_reader = new LineNumberReader(reader); // input stream
+ m_params = params; // array of parameters
+ m_line = new HashMap(params.length); //
+ }
+
+ /**
+ * read
+ *
+ *
+ *
+ * @param param
+ * @param errors
+ * @return
+ */
+ public final String read(final Parameter param, final ErrorList errors) {
+ return (String) m_line.get(param);
+ }
+/*
+ * May 2009: Obviously a reminiscence from previous versions of code. This class
+ * is currently used by coreloader only and it does not use the load method
+ * and it works with load commented out.
+ *
+ * Code should be removed after extensive testing.
+ *
+ public final ParameterValue load(final Parameter param) {
+ final ParameterValue value = new ParameterValue();
+
+ // XXX this won't work correctly with compound parameters
+ value.setObject(param.read(this, value.getErrors()));
+
+ value.getErrors().check();
+
+ return value;
+ }
+*/
+ /**
+ * Just a public visible entry point into internalNext, used to process
+ * an exception if thrown.
+ *
+ * @return: boolean true if any values could be processed.
+ */
+ public final boolean next() {
+ try {
+ return internalNext();
+ } catch (IOException ioe) {
+ throw new UncheckedWrapperException(ioe);
+ }
+ }
+
+ /**
+ * Internally used worker method which processes the next() method.
+ *
+ * Reads in a line from input stream and asks parseLine to process it. The
+ * resulting array of strings (each containing a value)
+ * @return
+ * @throws java.io.IOException
+ */
+ private boolean internalNext() throws IOException {
+ final String line = m_reader.readLine();
+
+ if (line == null) {
+ return false;
+ } else {
+ final String[] elems = parseLine(line);
+
+ // m_params: array of parameters to store the comma separated values
+ // used to determine the max. number of values which can be processed.
+ for (int i = 0; i < m_params.length; i++) {
+ if (i < elems.length) {
+ // If for the given index into the array of parametes a
+ // corresponding element in the array of strings exist,
+ // store it in a hash map (a hash map per line)
+ m_line.put(m_params[i], elems[i]);
+ } else {
+ m_line.put(m_params[i], null);
+ }
+ }
+
+ return true;
+ }
+ }
+
+ private static final char ESCAPE = '\\';
+ private static final char QUOTE = '"';
+ private static final char SEPARATOR = ',';
+
+ /**
+ * Internal used helper method of method parseLine.
+ *
+ * @param c
+ * @return
+ */
+ private char escape(char c) {
+ switch (c) {
+ case 'n':
+ return '\n';
+ case 't':
+ return '\t';
+ case 'r':
+ return '\r';
+ default:
+ return c;
+ }
+ }
+
+ /**
+ * Takes a string and analyses it as a list of comma separated values.
+ *
+ * Internally used to store each value found in a new string and add it
+ * to an array of strings.
+ *
+ * @param line: string containing a comma separated list of values
+ * @return : array of strings, each containing a value of the list
+ */
+ private String[] parseLine(final String line) {
+ int length = line.length();
+
+ // Check here if the last character is an escape character so
+ // that we don't need to check in the main loop.
+ if (line.charAt(length - 1) == ESCAPE) {
+ throw new IllegalArgumentException
+ (m_reader.getLineNumber() +
+ ": last character is an escape character\n" + line);
+ }
+
+ // The set of parsed fields.
+ List result = new ArrayList();
+
+ // The characters between seperators.
+ StringBuffer buf = new StringBuffer(length);
+ // Marks the begining of the field relative to buf,
+ // -1 indicates the beginning of buf.
+ int begin = -1;
+ // Marks the end of the field relative to buf.
+ int end = 0;
+
+ // Indicates whether or not we're in a quoted string.
+ boolean quote = false;
+
+ for (int i = 0; i < length; i++) {
+ char c = line.charAt(i);
+ if (quote) {
+ switch (c) {
+ case QUOTE:
+ quote = false;
+ break;
+ case ESCAPE:
+ buf.append(escape(line.charAt(++i)));
+ break;
+ default:
+ buf.append(c);
+ break;
+ }
+
+ end = buf.length();
+ } else {
+ switch (c) {
+ case SEPARATOR:
+ result.add(field(buf, begin, end));
+ buf = new StringBuffer(length);
+ begin = -1;
+ end = 0;
+ break;
+ case ESCAPE:
+ if (begin < 0) { begin = buf.length(); }
+ buf.append(escape(line.charAt(++i)));
+ end = buf.length();
+ break;
+ case QUOTE:
+ if (begin < 0) { begin = buf.length(); }
+ quote = true;
+ end = buf.length();
+ break;
+ default:
+ if (begin < 0 &&
+ !Character.isWhitespace(c)) {
+ begin = buf.length();
+ }
+ buf.append(c);
+ if (!Character.isWhitespace(c)) { end = buf.length(); }
+ break;
+ }
+ }
+ }
+
+ if (quote) {
+ throw new IllegalArgumentException
+ (m_reader.getLineNumber() + ": unterminated string\n" + line);
+ } else {
+ result.add(field(buf, begin, end));
+ }
+
+ String[] fields = new String[result.size()];
+ result.toArray(fields);
+ return fields;
+ }
+
+ /**
+ * internal helper method for method parseLine
+ *
+ * @param field
+ * @param begin
+ * @param end
+ * @return
+ */
+ private String field(StringBuffer field, int begin, int end) {
+ if (begin < 0) {
+ return field.substring(0, end);
+ } else {
+ return field.substring(begin, end);
+ }
+ }
+
+}
diff --git a/ccm-core/src/main/java/com/arsdigita/util/parameter/ClassParameter.java b/ccm-core/src/main/java/com/arsdigita/util/parameter/ClassParameter.java
new file mode 100644
index 000000000..1e067a98c
--- /dev/null
+++ b/ccm-core/src/main/java/com/arsdigita/util/parameter/ClassParameter.java
@@ -0,0 +1,74 @@
+/*
+ * Copyright (C) 2003-2004 Red Hat Inc. All Rights Reserved.
+ *
+ * This library is free software; you can redistribute it and/or
+ * modify it under the terms of the GNU Lesser General Public License
+ * as published by the Free Software Foundation; either version 2.1 of
+ * the License, or (at your option) any later version.
+ *
+ * This library is distributed in the hope that it will be useful,
+ * but WITHOUT ANY WARRANTY; without even the implied warranty of
+ * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU
+ * Lesser General Public License for more details.
+ *
+ * You should have received a copy of the GNU Lesser General Public
+ * License along with this library; if not, write to the Free Software
+ * Foundation, Inc., 59 Temple Place, Suite 330, Boston, MA 02111-1307 USA
+ *
+ */
+package com.arsdigita.util.parameter;
+
+import org.apache.commons.beanutils.converters.ClassConverter;
+import org.apache.log4j.Logger;
+
+/**
+ * A parameter representing a Java Class.
+ *
+ * Subject to change.
+ *
+ * @see java.lang.Class
+ * @see Parameter
+ * @author Justin Ross <jross@redhat.com>
+ * @version $Id$
+ */
+public class ClassParameter extends AbstractParameter {
+
+ private static final Logger logger = Logger.getLogger(ClassParameter.class);
+
+ static {
+ logger.debug("Static initalizer starting...");
+ Converters.set(Class.class, new ClassConverter());
+ logger.debug("Static initalizer finished.");
+ }
+
+ public ClassParameter(final String name) {
+ super(name, Class.class);
+ }
+
+ public ClassParameter(final String name,
+ final int multiplicity,
+ final Object defaalt) {
+ super(name, multiplicity, defaalt, Class.class);
+ }
+
+ // value != null
+ protected Object unmarshal(String value, ErrorList errors) {
+ Class theClass = null;
+ try {
+ theClass = Class.forName(value);
+ } catch (ClassNotFoundException e) {
+ errors.add(new ParameterError(this, "No such class: " + value));
+ }
+
+ return theClass;
+ }
+
+ protected String marshal(Object value) {
+ Class theClass = ((Class) value);
+ if (theClass == null) {
+ return null;
+ } else {
+ return theClass.getName();
+ }
+ }
+}
diff --git a/ccm-core/src/main/java/com/arsdigita/util/parameter/CompoundParameterReader.java b/ccm-core/src/main/java/com/arsdigita/util/parameter/CompoundParameterReader.java
new file mode 100644
index 000000000..f5789dea6
--- /dev/null
+++ b/ccm-core/src/main/java/com/arsdigita/util/parameter/CompoundParameterReader.java
@@ -0,0 +1,80 @@
+/*
+ * Copyright (C) 2003-2004 Red Hat Inc. All Rights Reserved.
+ *
+ * This library is free software; you can redistribute it and/or
+ * modify it under the terms of the GNU Lesser General Public License
+ * as published by the Free Software Foundation; either version 2.1 of
+ * the License, or (at your option) any later version.
+ *
+ * This library is distributed in the hope that it will be useful,
+ * but WITHOUT ANY WARRANTY; without even the implied warranty of
+ * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU
+ * Lesser General Public License for more details.
+ *
+ * You should have received a copy of the GNU Lesser General Public
+ * License along with this library; if not, write to the Free Software
+ * Foundation, Inc., 59 Temple Place, Suite 330, Boston, MA 02111-1307 USA
+ *
+ */
+package com.arsdigita.util.parameter;
+
+import com.arsdigita.util.Assert;
+import java.util.ArrayList;
+import java.util.Iterator;
+import java.util.List;
+import org.apache.log4j.Logger;
+
+/**
+ * Aggregates a set of ParameterReaders so they may be
+ * treated as one.
+ *
+ * Subject to change.
+ *
+ * @see ParameterReader
+ * @author Rafael H. Schloming <rhs@mit.edu>
+ * @author Justin Ross <jross@redhat.com>
+ * @version $Id$
+ */
+public class CompoundParameterReader implements ParameterReader {
+
+ private static final Logger s_log = Logger.getLogger
+ (CompoundParameterReader.class);
+
+ private final List m_readers;
+
+ /**
+ * Constructs a new compound parameter reader.
+ */
+ public CompoundParameterReader() {
+ m_readers = new ArrayList();
+ }
+
+ /**
+ * Adds reader to the set of component readers.
+ *
+ * @param reader The ParameterReader being added; it
+ * cannot be null
+ */
+ public void add(final ParameterReader reader) {
+ Assert.exists(reader, ParameterReader.class);
+
+ m_readers.add(reader);
+ }
+
+ /**
+ * @see ParameterReader#read(Parameter,ErrorList)
+ */
+ public String read(final Parameter param, final ErrorList errors) {
+ for (final Iterator it = m_readers.iterator(); it.hasNext(); ) {
+ final ParameterReader reader = (ParameterReader) it.next();
+
+ final String result = reader.read(param, errors);
+
+ if (result != null) {
+ return result;
+ }
+ }
+
+ return null;
+ }
+}
diff --git a/ccm-core/src/main/java/com/arsdigita/util/parameter/Converters.java b/ccm-core/src/main/java/com/arsdigita/util/parameter/Converters.java
new file mode 100644
index 000000000..0f8c4ba45
--- /dev/null
+++ b/ccm-core/src/main/java/com/arsdigita/util/parameter/Converters.java
@@ -0,0 +1,95 @@
+/*
+ * Copyright (C) 2003-2004 Red Hat Inc. All Rights Reserved.
+ *
+ * This library is free software; you can redistribute it and/or
+ * modify it under the terms of the GNU Lesser General Public License
+ * as published by the Free Software Foundation; either version 2.1 of
+ * the License, or (at your option) any later version.
+ *
+ * This library is distributed in the hope that it will be useful,
+ * but WITHOUT ANY WARRANTY; without even the implied warranty of
+ * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU
+ * Lesser General Public License for more details.
+ *
+ * You should have received a copy of the GNU Lesser General Public
+ * License along with this library; if not, write to the Free Software
+ * Foundation, Inc., 59 Temple Place, Suite 330, Boston, MA 02111-1307 USA
+ *
+ */
+package com.arsdigita.util.parameter;
+
+import com.arsdigita.util.Assert;
+
+import java.util.Collections;
+import java.util.Map;
+import java.util.HashMap;
+
+import org.apache.commons.beanutils.Converter;
+
+/**
+ * Subject to change.
+ *
+ * Collects together BeanUtils converters for use by the base
+ * Parameters.
+ *
+ * @author Justin Ross <jross@redhat.com>
+ * @version $Id$
+ */
+public class Converters {
+
+ private static Map s_converters = Collections.synchronizedMap
+ (new HashMap());
+
+ /**
+ * Gets the Converter registered for
+ * clacc. This method will fail if no converter is
+ * found.
+ *
+ * @param clacc The Class of the parameter value; it
+ * cannot be null
+ * @return A Converter instance; it cannot be null
+ */
+ public static final Converter get(final Class clacc) {
+ Assert.exists(clacc, Class.class);
+
+ final Converter converter = (Converter) s_converters.get(clacc);
+
+ Assert.exists(converter, Converter.class);
+
+ return converter;
+ }
+
+ /**
+ * Registers converter for clacc.
+ *
+ * @param clacc The Class of the parameter value; it
+ * cannot be null
+ * @param converter The Converter to register to
+ * clacc; it cannot be null
+ */
+ public static final void set(final Class clacc, final Converter converter) {
+ if (Assert.isEnabled()) {
+ Assert.exists(clacc, Class.class);
+ Assert.exists(converter, Converter.class);
+ }
+
+ s_converters.put(clacc, converter);
+ }
+
+ /**
+ * Converts value using the converter registered for
+ * clacc.
+ *
+ * @param clacc The Class of the parameter value; it
+ * cannot be null
+ * @param value The String-encoded value of the
+ * parameter; it may be null
+ * @return The Java object conversion for value; it
+ * may be null
+ */
+ public static final Object convert(final Class clacc, final String value) {
+ Assert.exists(clacc, Class.class);
+
+ return get(clacc).convert(clacc, value);
+ }
+}
diff --git a/ccm-core/src/main/java/com/arsdigita/util/parameter/EmailParameter.java b/ccm-core/src/main/java/com/arsdigita/util/parameter/EmailParameter.java
new file mode 100644
index 000000000..eaa3720d1
--- /dev/null
+++ b/ccm-core/src/main/java/com/arsdigita/util/parameter/EmailParameter.java
@@ -0,0 +1,66 @@
+/*
+ * Copyright (C) 2003-2004 Red Hat Inc. All Rights Reserved.
+ *
+ * This library is free software; you can redistribute it and/or
+ * modify it under the terms of the GNU Lesser General Public License
+ * as published by the Free Software Foundation; either version 2.1 of
+ * the License, or (at your option) any later version.
+ *
+ * This library is distributed in the hope that it will be useful,
+ * but WITHOUT ANY WARRANTY; without even the implied warranty of
+ * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU
+ * Lesser General Public License for more details.
+ *
+ * You should have received a copy of the GNU Lesser General Public
+ * License along with this library; if not, write to the Free Software
+ * Foundation, Inc., 59 Temple Place, Suite 330, Boston, MA 02111-1307 USA
+ *
+ */
+package com.arsdigita.util.parameter;
+
+import javax.mail.internet.AddressException;
+import javax.mail.internet.InternetAddress;
+import org.apache.oro.text.perl.Perl5Util;
+
+/**
+ * Subject to change.
+ *
+ * A parameter representing an InternetAddress.
+ *
+ * @see javax.mail.internet.InternetAddress
+ * @see Parameter
+ * @author Justin Ross <jross@redhat.com>
+ * @version $Id$
+ */
+public class EmailParameter extends StringParameter {
+
+ private static final Perl5Util s_perl = new Perl5Util();
+ private static final String s_regex =
+ "/^[^@<>\"\t ]+@[^@<>\".\t]+([.][^@<>\".\n ]+)+$/";
+
+ public EmailParameter(final String name) {
+ super(name);
+ }
+
+ protected Object unmarshal(final String value, final ErrorList errors) {
+ try {
+ return new InternetAddress(value);
+ } catch (AddressException ae) {
+ errors.add(new ParameterError(this, ae));
+ return null;
+ }
+ }
+
+ protected void doValidate(final Object value, final ErrorList errors) {
+ super.doValidate(value, errors);
+
+ final InternetAddress email = (InternetAddress) value;
+
+ if (!s_perl.match(s_regex, email.toString())) {
+ final ParameterError error = new ParameterError
+ (this, "The value is not a valid email address");
+
+ errors.add(error);
+ }
+ }
+}
diff --git a/ccm-core/src/main/java/com/arsdigita/util/parameter/EnumerationParameter.java b/ccm-core/src/main/java/com/arsdigita/util/parameter/EnumerationParameter.java
new file mode 100644
index 000000000..1483a8e0b
--- /dev/null
+++ b/ccm-core/src/main/java/com/arsdigita/util/parameter/EnumerationParameter.java
@@ -0,0 +1,85 @@
+/*
+ * Copyright (C) 2003-2004 Red Hat Inc. All Rights Reserved.
+ *
+ * This library is free software; you can redistribute it and/or
+ * modify it under the terms of the GNU Lesser General Public License
+ * as published by the Free Software Foundation; either version 2.1 of
+ * the License, or (at your option) any later version.
+ *
+ * This library is distributed in the hope that it will be useful,
+ * but WITHOUT ANY WARRANTY; without even the implied warranty of
+ * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU
+ * Lesser General Public License for more details.
+ *
+ * You should have received a copy of the GNU Lesser General Public
+ * License along with this library; if not, write to the Free Software
+ * Foundation, Inc., 59 Temple Place, Suite 330, Boston, MA 02111-1307 USA
+ *
+ */
+package com.arsdigita.util.parameter;
+
+import java.util.HashMap;
+import org.apache.log4j.Logger;
+
+/**
+ * Subject to change.
+ *
+ * A parameter that maps keys to values and, given a key, marshals or
+ * unmarshals to the corresponding value.
+ *
+ * @see Parameter
+ * @author Justin Ross <jross@redhat.com>
+ * @version $Id$
+ */
+public class EnumerationParameter extends AbstractParameter {
+
+ private static final Logger s_log = Logger.getLogger
+ (EnumerationParameter.class);
+
+ private final HashMap m_entries;
+ private final HashMap m_reverse;
+
+ public EnumerationParameter(final String name,
+ final int multiplicity,
+ final Object defaalt) {
+ super(name, multiplicity, defaalt);
+
+ m_entries = new HashMap();
+ m_reverse = new HashMap();
+ }
+
+ public EnumerationParameter(final String name) {
+ this(name, Parameter.REQUIRED, null);
+ }
+
+ public final void put(final String name, final Object value) {
+ if (m_entries.containsKey(name)) {
+ throw new IllegalArgumentException
+ ("name already has a value: " + name);
+ }
+ if (m_reverse.containsKey(value)) {
+ throw new IllegalArgumentException
+ ("value already has a name: " + value);
+ }
+ m_entries.put(name, value);
+ m_reverse.put(value, name);
+ }
+
+ protected Object unmarshal(final String value, final ErrorList errors) {
+ if (m_entries.containsKey(value)) {
+ return m_entries.get(value);
+ } else {
+ final ParameterError error = new ParameterError
+ (this, "The value must be one of " + m_entries.keySet());
+
+ errors.add(error);
+
+ return null;
+ }
+ }
+
+ protected String marshal(Object value) {
+ return (String) m_reverse.get(value);
+ }
+
+}
diff --git a/ccm-core/src/main/java/com/arsdigita/util/parameter/ErrorList.java b/ccm-core/src/main/java/com/arsdigita/util/parameter/ErrorList.java
new file mode 100644
index 000000000..923366674
--- /dev/null
+++ b/ccm-core/src/main/java/com/arsdigita/util/parameter/ErrorList.java
@@ -0,0 +1,156 @@
+/*
+ * Copyright (C) 2003-2004 Red Hat Inc. All Rights Reserved.
+ *
+ * This library is free software; you can redistribute it and/or
+ * modify it under the terms of the GNU Lesser General Public License
+ * as published by the Free Software Foundation; either version 2.1 of
+ * the License, or (at your option) any later version.
+ *
+ * This library is distributed in the hope that it will be useful,
+ * but WITHOUT ANY WARRANTY; without even the implied warranty of
+ * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU
+ * Lesser General Public License for more details.
+ *
+ * You should have received a copy of the GNU Lesser General Public
+ * License along with this library; if not, write to the Free Software
+ * Foundation, Inc., 59 Temple Place, Suite 330, Boston, MA 02111-1307 USA
+ *
+ */
+package com.arsdigita.util.parameter;
+
+import com.arsdigita.util.Assert;
+import com.arsdigita.util.UncheckedWrapperException;
+import java.io.IOException;
+import java.io.PrintWriter;
+import java.io.StringWriter;
+import java.io.Writer;
+import java.util.ArrayList;
+import java.util.Iterator;
+import org.apache.log4j.Logger;
+
+/**
+ * Subject to change.
+ *
+ * A collection to store ParameterErrors that are
+ * encountered during parameter reading or validation. This
+ * collection is used in the lifecycle methods of
+ * Parameter. It is ordinarily returned to the
+ * parameter-using code so that it can handle errors.
+ *
+ * @see ParameterError
+ * @see Parameter
+ * @author Justin Ross <jross@redhat.com>
+ * @version $Id$
+ */
+public final class ErrorList {
+
+ private static final Logger s_log = Logger.getLogger(ErrorList.class);
+
+ private final ArrayList m_params;
+
+ // XXX temporarily package access
+ final ArrayList m_errors;
+
+ /**
+ * Constructs a new error list.
+ */
+ public ErrorList() {
+ m_params = new ArrayList();
+ m_errors = new ArrayList();
+ }
+
+ /**
+ * Adds error to the error list.
+ *
+ * @param error A ParameterError representing a read
+ * or validation error; it cannot be null
+ */
+ public final void add(final ParameterError error) {
+ Assert.exists(error, ParameterError.class);
+
+ final Parameter param = error.getParameter();
+
+ synchronized (m_params) {
+ if (!m_params.contains(param)) {
+ m_params.add(param);
+ }
+ }
+
+ m_errors.add(error);
+ }
+
+ /**
+ * Gets an iterator over the currently stored errors.
+ *
+ * @see ParameterError
+ * @return An Iterator of
+ * ParameterErrors; it cannot be null
+ */
+ public final Iterator iterator() {
+ return m_errors.iterator();
+ }
+
+ /**
+ * Tells whether the error collection is empty or not.
+ *
+ * @return true if the collection is empty, otherwise
+ * false
+ */
+ public final boolean isEmpty() {
+ return m_errors.isEmpty();
+ }
+
+ /**
+ * Throws a ParameterException containing the error
+ * list. This method is for use when the client code wants the
+ * program to fail via an exception if there are errors.
+ *
+ * @throws ParameterException if the error list is not empty
+ */
+ public final void check() throws ParameterException {
+ if (!isEmpty()) {
+ final StringWriter writer = new StringWriter();
+ report(writer);
+ s_log.error(writer.toString());
+
+ throw new ParameterException
+ ("Errors encountered while reading parameters", this);
+ }
+ }
+
+ /**
+ * Prints parameter errors to out with formatting
+ * appropriate to the console.
+ *
+ * @param out The Writer to print the errors to
+ */
+ public final void report(final Writer out) {
+ try {
+ Assert.exists(out, PrintWriter.class);
+
+ final Iterator params = m_params.iterator();
+
+ while (params.hasNext()) {
+ final Parameter param = (Parameter) params.next();
+
+ out.write("Parameter " + param.getName() + " has the " +
+ "following errors:\n");
+
+ final Iterator errors = m_errors.iterator();
+
+ while (errors.hasNext()) {
+ final ParameterError error =
+ (ParameterError) errors.next();
+
+ if (error.getParameter().equals(param)) {
+ out.write("\t" + error.getMessage() + "\n");
+ }
+ }
+ }
+
+ out.flush();
+ } catch (IOException ioe) {
+ throw new UncheckedWrapperException(ioe);
+ }
+ }
+}
diff --git a/ccm-core/src/main/java/com/arsdigita/util/parameter/FileParameter.java b/ccm-core/src/main/java/com/arsdigita/util/parameter/FileParameter.java
new file mode 100644
index 000000000..979e2addc
--- /dev/null
+++ b/ccm-core/src/main/java/com/arsdigita/util/parameter/FileParameter.java
@@ -0,0 +1,67 @@
+/*
+ * Copyright (C) 2003-2004 Red Hat Inc. All Rights Reserved.
+ *
+ * This library is free software; you can redistribute it and/or
+ * modify it under the terms of the GNU Lesser General Public License
+ * as published by the Free Software Foundation; either version 2.1 of
+ * the License, or (at your option) any later version.
+ *
+ * This library is distributed in the hope that it will be useful,
+ * but WITHOUT ANY WARRANTY; without even the implied warranty of
+ * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU
+ * Lesser General Public License for more details.
+ *
+ * You should have received a copy of the GNU Lesser General Public
+ * License along with this library; if not, write to the Free Software
+ * Foundation, Inc., 59 Temple Place, Suite 330, Boston, MA 02111-1307 USA
+ *
+ */
+package com.arsdigita.util.parameter;
+
+import java.io.File;
+
+import org.apache.log4j.Logger;
+
+/**
+ * A Parameter representing a File
+ *
+ * @see Parameter
+ * @see java.io.File
+ * @author bche
+ */
+public class FileParameter extends AbstractParameter {
+ private static final Logger s_log = Logger.getLogger(FileParameter.class);
+
+ public FileParameter(final String name) {
+ super(name, File.class);
+ }
+
+ public Object unmarshal(final String value, final ErrorList errors) {
+ final String sPath = value;
+ File file = new File(sPath);
+ if (file.exists()) {
+ return file;
+ } else {
+ return null;
+ }
+ }
+
+ public String marshal(final Object value) {
+ final File file = (File) value;
+ if (file == null) {
+ return null;
+ } else {
+ return file.getAbsolutePath();
+ }
+ }
+
+ public void doValidate(final Object value, final ErrorList errors) {
+ final File file = (File) value;
+ if (!file.exists()) {
+ errors.add(
+ new ParameterError(
+ this,
+ "File " + file.getAbsolutePath() + " does not exist"));
+ }
+ }
+}
diff --git a/ccm-core/src/main/java/com/arsdigita/util/parameter/IntegerParameter.java b/ccm-core/src/main/java/com/arsdigita/util/parameter/IntegerParameter.java
new file mode 100644
index 000000000..46a11ff07
--- /dev/null
+++ b/ccm-core/src/main/java/com/arsdigita/util/parameter/IntegerParameter.java
@@ -0,0 +1,53 @@
+/*
+ * Copyright (C) 2003-2004 Red Hat Inc. All Rights Reserved.
+ *
+ * This library is free software; you can redistribute it and/or
+ * modify it under the terms of the GNU Lesser General Public License
+ * as published by the Free Software Foundation; either version 2.1 of
+ * the License, or (at your option) any later version.
+ *
+ * This library is distributed in the hope that it will be useful,
+ * but WITHOUT ANY WARRANTY; without even the implied warranty of
+ * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU
+ * Lesser General Public License for more details.
+ *
+ * You should have received a copy of the GNU Lesser General Public
+ * License along with this library; if not, write to the Free Software
+ * Foundation, Inc., 59 Temple Place, Suite 330, Boston, MA 02111-1307 USA
+ *
+ */
+package com.arsdigita.util.parameter;
+
+import org.apache.commons.beanutils.converters.IntegerConverter;
+import org.apache.log4j.Logger;
+
+/**
+ * A parameter representing a Java Integer.
+ *
+ * Subject to change.
+ *
+ * @see java.lang.Integer
+ * @see Parameter
+ * @author Justin Ross <jross@redhat.com>
+ * @version $Id$
+ */
+public class IntegerParameter extends AbstractParameter {
+
+ private final static Logger logger = Logger.getLogger(IntegerParameter.class);
+
+ static {
+ logger.debug("Static initalizer starting...");
+ Converters.set(Integer.class, new IntegerConverter());
+ logger.debug("Static initalizer finished.");
+ }
+
+ public IntegerParameter(final String name) {
+ super(name, Integer.class);
+ }
+
+ public IntegerParameter(final String name,
+ final int multiplicity,
+ final Object defaalt) {
+ super(name, multiplicity, defaalt, Integer.class);
+ }
+}
diff --git a/ccm-core/src/main/java/com/arsdigita/util/parameter/MapParameter.java b/ccm-core/src/main/java/com/arsdigita/util/parameter/MapParameter.java
new file mode 100644
index 000000000..6972cd59b
--- /dev/null
+++ b/ccm-core/src/main/java/com/arsdigita/util/parameter/MapParameter.java
@@ -0,0 +1,115 @@
+/*
+ * Copyright (C) 2003-2004 Red Hat Inc. All Rights Reserved.
+ *
+ * This library is free software; you can redistribute it and/or
+ * modify it under the terms of the GNU Lesser General Public License
+ * as published by the Free Software Foundation; either version 2.1 of
+ * the License, or (at your option) any later version.
+ *
+ * This library is distributed in the hope that it will be useful,
+ * but WITHOUT ANY WARRANTY; without even the implied warranty of
+ * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU
+ * Lesser General Public License for more details.
+ *
+ * You should have received a copy of the GNU Lesser General Public
+ * License along with this library; if not, write to the Free Software
+ * Foundation, Inc., 59 Temple Place, Suite 330, Boston, MA 02111-1307 USA
+ *
+ */
+package com.arsdigita.util.parameter;
+
+import com.arsdigita.util.Assert;
+import java.util.ArrayList;
+import java.util.HashMap;
+import java.util.Iterator;
+
+/**
+ * A parameter that manages a collection of Parameter to
+ * Object value mappings.
+ *
+ * Subject to change.
+ *
+ * @see java.util.Map
+ * @see Parameter
+ * @author Justin Ross <jross@redhat.com>
+ * @version $Id$
+ */
+public class MapParameter extends AbstractParameter {
+
+ private final ArrayList m_params;
+
+ public MapParameter(final String name,
+ final int multiplicity,
+ final Object defaalt) {
+ super(name, multiplicity, defaalt, String.class);
+
+ m_params = new ArrayList();
+ }
+
+ public MapParameter(final String name) {
+ super(name, String.class);
+
+ m_params = new ArrayList();
+ }
+
+ public final void add(final Parameter param) {
+ Assert.exists(param, Parameter.class);
+
+ m_params.add(param);
+ }
+
+ public final boolean contains(final Parameter param) {
+ Assert.exists(param, Parameter.class);
+
+ return m_params.contains(param);
+ }
+
+ public final Iterator iterator() {
+ return m_params.iterator();
+ }
+
+ protected Object doRead(final ParameterReader reader,
+ final ErrorList errors) {
+ final HashMap map = new HashMap();
+ final Iterator params = m_params.iterator();
+
+ while (params.hasNext()) {
+ final Parameter param = (Parameter) params.next();
+ final Object value = param.read(reader, errors);
+
+ if (value != null) {
+ map.put(param, value);
+ }
+ }
+
+ return map;
+ }
+
+ protected void doValidate(final Object value, final ErrorList errors) {
+ final HashMap map = (HashMap) value;
+ final Iterator params = m_params.iterator();
+
+ while (params.hasNext()) {
+ final Parameter param = (Parameter) params.next();
+
+ if (map.containsKey(param)) {
+ param.validate(map.get(param), errors);
+ } else {
+ param.validate(param.getDefaultValue(), errors);
+ }
+ }
+ }
+
+ protected void doWrite(final ParameterWriter writer, final Object value) {
+ final HashMap map = (HashMap) value;
+ final Iterator params = m_params.iterator();
+
+ while (params.hasNext()) {
+ final Parameter param = (Parameter) params.next();
+
+ if (map.containsKey(param)) {
+ param.write(writer, map.get(param));
+ }
+ }
+ }
+}
diff --git a/ccm-core/src/main/java/com/arsdigita/util/parameter/Parameter.java b/ccm-core/src/main/java/com/arsdigita/util/parameter/Parameter.java
new file mode 100644
index 000000000..e94f5a4d6
--- /dev/null
+++ b/ccm-core/src/main/java/com/arsdigita/util/parameter/Parameter.java
@@ -0,0 +1,183 @@
+/*
+ * Copyright (C) 2003-2004 Red Hat Inc. All Rights Reserved.
+ *
+ * This library is free software; you can redistribute it and/or
+ * modify it under the terms of the GNU Lesser General Public License
+ * as published by the Free Software Foundation; either version 2.1 of
+ * the License, or (at your option) any later version.
+ *
+ * This library is distributed in the hope that it will be useful,
+ * but WITHOUT ANY WARRANTY; without even the implied warranty of
+ * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU
+ * Lesser General Public License for more details.
+ *
+ * You should have received a copy of the GNU Lesser General Public
+ * License along with this library; if not, write to the Free Software
+ * Foundation, Inc., 59 Temple Place, Suite 330, Boston, MA 02111-1307 USA
+ *
+ */
+package com.arsdigita.util.parameter;
+
+/**
+ * Describes a named property that can read, write, and validate its
+ * own value.
+ *
+ * Subject to change.
+ *
+ * See the documentation on {@link #read}, {@link #write}, and {@link #validate}
+ * for details.
+
+ * They have the following features as well:
+ *
+ *
+ * Properties props = System.getProperties();
+ * ParameterReader reader = JavaPropertyReader(props);
+ * ParameterWriter writer = JavaPropertyWriter(props);
+ * ErrorList errors = new ErrorList();
+ *
+ * Object value = param.read(reader, errors);
+ * errors.check(); // If errors is not empty, fails
+ *
+ * param.validate(value, errors);
+ * errors.check(); // If errors is not empty, fails
+ *
+ * // We now have a valid unmarshaled value, so code of actual use can
+ * // go here.
+ *
+ * param.write(writer, value);
+ *
+ *
+ * @see com.arsdigita.util.parameter.AbstractParameter
+ * @see com.arsdigita.util.parameter.ParameterContext
+ * @author Rafael H. Schloming <rhs@mit.edu>
+ * @author Justin Ross <jross@redhat.com>
+ * @version $Id$
+ */
+public interface Parameter {
+
+ /**
+ * Flag to indicate the parameter value is nullable.
+ */
+ public static final int OPTIONAL = 0;
+
+ /**
+ * Flag to indicate the parameter value cannot be null.
+ */
+ public static final int REQUIRED = 1;
+
+ /**
+ * Tells wether the parameter is nullable or not.
+ *
+ * @return true if the parameter cannot be null; false if it can
+ * be null
+ */
+ boolean isRequired();
+
+ /**
+ * Gets the name of the parameter.
+ *
+ * @return The String parameter name; it cannot be
+ * null
+ */
+ String getName();
+
+ /**
+ * Gets the default value of the parameter. Implementations may
+ * choose to substitute this value for null.
+ *
+ * @return The fallback value; it may be null
+ */
+ Object getDefaultValue();
+
+ /**
+ * Gets metadata associated with the parameter if it is available.
+ *
+ * @return The ParameterInfo object; it may be null
+ */
+ ParameterInfo getInfo();
+
+ /**
+ * Sets the optional parameter metadata to info.
+ *
+ * @param info The ParameterInfo to associate; it may
+ * be null
+ */
+ void setInfo(ParameterInfo info);
+
+ /**
+ * Gets the parameter value as a Java object. The value will have
+ * a specific runtime type and so may be appropriately cast.
+ *
+ * Reading typically follows the following procedure:
+ *
+ * readererrors. Callers of this method will
+ * typically construct an ErrorList in which to
+ * collect errors.
+ *
+ * @param reader The ParameterReader from which to
+ * recover a string literal value; it cannot be null
+ * @param errors The ErrorList in which to collect
+ * any errors encountered; it cannot be null
+ * @return The Java object value of the parameter
+ */
+ Object read(ParameterReader reader, ErrorList errors);
+
+ /**
+ * Validates the parameter value, value. Any
+ * validation errors encountered are added to errors.
+ *
+ * @param value The value to validate; this is typically the value
+ * returned by {@link #read}; it may be null
+ * @param errors The ErrorList in which to collect
+ * any errors encountered; it cannot be null
+ */
+ void validate(Object value, ErrorList errors);
+
+ /**
+ * Writes the parameter value as a string literal. The parameter
+ * marshals the object value to a string and sends it
+ * to writer.
+ *
+ * @param writer The ParameterWriter that will take
+ * the marshaled value and store it; it cannot be null
+ * @param value The Java object value of the parameter
+ */
+ void write(ParameterWriter writer, Object value);
+}
diff --git a/ccm-core/src/main/java/com/arsdigita/util/parameter/ParameterContext.java b/ccm-core/src/main/java/com/arsdigita/util/parameter/ParameterContext.java
new file mode 100644
index 000000000..9fdf4b020
--- /dev/null
+++ b/ccm-core/src/main/java/com/arsdigita/util/parameter/ParameterContext.java
@@ -0,0 +1,105 @@
+/*
+ * Copyright (C) 2003-2004 Red Hat Inc. All Rights Reserved.
+ *
+ * This library is free software; you can redistribute it and/or
+ * modify it under the terms of the GNU Lesser General Public License
+ * as published by the Free Software Foundation; either version 2.1 of
+ * the License, or (at your option) any later version.
+ *
+ * This library is distributed in the hope that it will be useful,
+ * but WITHOUT ANY WARRANTY; without even the implied warranty of
+ * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU
+ * Lesser General Public License for more details.
+ *
+ * You should have received a copy of the GNU Lesser General Public
+ * License along with this library; if not, write to the Free Software
+ * Foundation, Inc., 59 Temple Place, Suite 330, Boston, MA 02111-1307 USA
+ *
+ */
+package com.arsdigita.util.parameter;
+
+/**
+ * A container of parameters.
+ *
+ * A parameter context binds together a set of parameters and keeps their values.
+ *
+ *
+ * Subject to change.
+ *
+ * @see com.arsdigita.util.parameter.Parameter
+ * @author Justin Ross <jross@redhat.com>
+ * @version $Id$
+ */
+public interface ParameterContext {
+
+ /**
+ * Returns all the parameters registered on the parameter context.
+ *
+ * @return A Parameter[] of all the parameters; it
+ * cannot be null
+ */
+ Parameter[] getParameters();
+
+ /**
+ * Gets the unmarshaled value of param. If the
+ * loaded value is null, param.getDefaultValue() is
+ * returned.
+ *
+ * @param param The named Parameter whose value to
+ * retrieve; it cannot be null
+ * @return The unmarshaled Java object value of param
+ */
+ Object get(Parameter param);
+
+ /**
+ * Gets the unmarshaled value of param, returning
+ * dephalt if param's value is null.
+ *
+ * @param param The Parameter whose value to
+ * retrieve; it cannot be null
+ * @param dephalt The fallback default value; it may be null
+ * @return The unmarshaled Java object value of param
+ * or dephalt if the former is null
+ */
+ Object get(Parameter param, Object dephalt);
+
+ /**
+ * Sets the value of param to value.
+ *
+ * @param param The Parameter whose value to set; it
+ * cannot be null
+ * @param value The new value of param; it may be
+ * null
+ */
+ void set(Parameter param, Object value);
+
+ /**
+ * Reads and unmarshals all values associated with the registered
+ * parameters from reader. If any errors are
+ * encountered, they are added to errors.
+ *
+ * @param reader The ParameterReader from which to
+ * fetch the values; it cannot be null
+ * @param errors The ErrorList that captures any
+ * errors while loading; it cannot be null
+ */
+ void load(ParameterReader reader, ErrorList errors);
+
+ /**
+ * Marshals and writes all values associated with the registered
+ * parameters to writer.
+ *
+ * @param writer The ParameterWriter to which values
+ * are written; it cannot be null
+ */
+ void save(ParameterWriter writer);
+
+ /**
+ * Validates all values associated with the registered parameters.
+ * Any errors encountered are added to errors.
+ *
+ * @param errors The ErrorList that captures
+ * validation errors; it cannot be null
+ */
+ void validate(ErrorList errors);
+}
diff --git a/ccm-core/src/main/java/com/arsdigita/util/parameter/ParameterError.java b/ccm-core/src/main/java/com/arsdigita/util/parameter/ParameterError.java
new file mode 100644
index 000000000..bc01d6225
--- /dev/null
+++ b/ccm-core/src/main/java/com/arsdigita/util/parameter/ParameterError.java
@@ -0,0 +1,116 @@
+/*
+ * Copyright (C) 2003-2004 Red Hat Inc. All Rights Reserved.
+ *
+ * This library is free software; you can redistribute it and/or
+ * modify it under the terms of the GNU Lesser General Public License
+ * as published by the Free Software Foundation; either version 2.1 of
+ * the License, or (at your option) any later version.
+ *
+ * This library is distributed in the hope that it will be useful,
+ * but WITHOUT ANY WARRANTY; without even the implied warranty of
+ * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU
+ * Lesser General Public License for more details.
+ *
+ * You should have received a copy of the GNU Lesser General Public
+ * License along with this library; if not, write to the Free Software
+ * Foundation, Inc., 59 Temple Place, Suite 330, Boston, MA 02111-1307 USA
+ *
+ */
+package com.arsdigita.util.parameter;
+
+import com.arsdigita.util.Assert;
+
+/**
+ * Subject to change.
+ *
+ * Information about an error for a parameter. Parameter implementors
+ * will add ParameterErrors to the passed in
+ * ErrorList when their parameters encounter error
+ * conditions.
+ *
+ * @see ErrorList
+ * @see Parameter
+ * @author Justin Ross <jross@redhat.com>
+ * @version $Id$
+ */
+public final class ParameterError {
+
+ private final Parameter m_param;
+ private final String m_message;
+ private Throwable m_throwable;
+
+ /**
+ * Constructs a parameter error for param.
+ *
+ * @param param The Parameter whose value is in
+ * error; it cannot be null
+ * @param message A String description of the error
+ */
+ public ParameterError(final Parameter param,
+ final String message) {
+ if (Assert.isEnabled()) {
+ Assert.exists(param, Parameter.class);
+ Assert.exists(message, String.class);
+ }
+
+ m_param = param;
+ m_message = message;
+ }
+
+ /**
+ * Constructs a parameter error for param, drawing
+ * its error message from throwable.
+ *
+ * @param param The Parameter whose value is in
+ * error; it cannot be null
+ * @param throwable The Throwable for the error; it
+ * cannot be null
+ */
+ public ParameterError(final Parameter param,
+ final Throwable throwable) {
+ this(param, throwable.getMessage());
+
+ m_throwable = throwable;
+ }
+
+ /**
+ * Gets the parameter associated with this error.
+ *
+ * @return The Parameter in error; it cannot be null
+ */
+ public final Parameter getParameter() {
+ return m_param;
+ }
+
+ /**
+ * Gets the message associated with this error.
+ *
+ * @return The String message for the error; it
+ * cannot be null
+ */
+ public final String getMessage() {
+ // XXX this actually can be null, so need to prevent that
+ return m_message;
+ }
+
+ /**
+ * Gets the throwable, if present, that corresponds to the error.
+ *
+ * @return The Throwable of this error; it may be
+ * null
+ */
+ public final Throwable getThrowable() {
+ return m_throwable;
+ }
+
+ /**
+ * Returns a string representation of the error suitable for
+ * debugging.
+ *
+ * @return super.toString() + "," + param.getName()
+ */
+ @Override
+ public String toString() {
+ return super.toString() + "," + m_param.getName();
+ }
+}
diff --git a/ccm-core/src/main/java/com/arsdigita/util/parameter/ParameterException.java b/ccm-core/src/main/java/com/arsdigita/util/parameter/ParameterException.java
new file mode 100644
index 000000000..651939a2e
--- /dev/null
+++ b/ccm-core/src/main/java/com/arsdigita/util/parameter/ParameterException.java
@@ -0,0 +1,74 @@
+/*
+ * Copyright (C) 2003-2004 Red Hat Inc. All Rights Reserved.
+ *
+ * This library is free software; you can redistribute it and/or
+ * modify it under the terms of the GNU Lesser General Public License
+ * as published by the Free Software Foundation; either version 2.1 of
+ * the License, or (at your option) any later version.
+ *
+ * This library is distributed in the hope that it will be useful,
+ * but WITHOUT ANY WARRANTY; without even the implied warranty of
+ * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU
+ * Lesser General Public License for more details.
+ *
+ * You should have received a copy of the GNU Lesser General Public
+ * License along with this library; if not, write to the Free Software
+ * Foundation, Inc., 59 Temple Place, Suite 330, Boston, MA 02111-1307 USA
+ *
+ */
+package com.arsdigita.util.parameter;
+
+import com.arsdigita.util.Assert;
+import java.util.List;
+import org.apache.log4j.Logger;
+
+/**
+ * Subject to change.
+ *
+ * An exception to indicate invalid parameter states. This exception should only
+ * be used when the client code of a parameter opts in to using exceptions
+ * rather than handling parameter errors itself. See
+ * {@link com.arsdigita.util.parameter.ErrorList#check()}.
+ *
+ * @see com.arsdigita.util.parameter.ErrorList
+ * @author Justin Ross <jross@redhat.com>
+ * @version $Id$
+ */
+public final class ParameterException extends RuntimeException {
+
+ private static final Logger s_log = Logger.getLogger(
+ ParameterException.class);
+ private static final long serialVersionUID = 1726920836531266365L;
+
+ private final ErrorList m_errors;
+
+ /**
+ * Constructs a new parameter exception with the content
+ * message.
+ *
+ * @param message A String describing what's wrong; it cannot
+ * be null
+ * @param errors The ErrorList containing the errors that
+ * prompted this exception; it cannot be null
+ */
+ public ParameterException(final String message, final ErrorList errors) {
+ super(message);
+
+ if (Assert.isEnabled()) {
+ Assert.exists(message, String.class);
+ Assert.exists(errors, List.class);
+ }
+
+ m_errors = errors;
+ }
+
+ /**
+ * Gets the set of errors associated with the exception.
+ *
+ * @return The ErrorList of errors; it cannot be null
+ */
+ public final ErrorList getErrors() {
+ return m_errors;
+ }
+
+}
diff --git a/ccm-core/src/main/java/com/arsdigita/util/parameter/ParameterInfo.java b/ccm-core/src/main/java/com/arsdigita/util/parameter/ParameterInfo.java
new file mode 100644
index 000000000..3c6fd0087
--- /dev/null
+++ b/ccm-core/src/main/java/com/arsdigita/util/parameter/ParameterInfo.java
@@ -0,0 +1,64 @@
+/*
+ * Copyright (C) 2003-2004 Red Hat Inc. All Rights Reserved.
+ *
+ * This library is free software; you can redistribute it and/or
+ * modify it under the terms of the GNU Lesser General Public License
+ * as published by the Free Software Foundation; either version 2.1 of
+ * the License, or (at your option) any later version.
+ *
+ * This library is distributed in the hope that it will be useful,
+ * but WITHOUT ANY WARRANTY; without even the implied warranty of
+ * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU
+ * Lesser General Public License for more details.
+ *
+ * You should have received a copy of the GNU Lesser General Public
+ * License along with this library; if not, write to the Free Software
+ * Foundation, Inc., 59 Temple Place, Suite 330, Boston, MA 02111-1307 USA
+ *
+ */
+package com.arsdigita.util.parameter;
+
+/**
+ * Subject to change.
+ *
+ * Metadata for a parameter that is of use for building documentation
+ * or user interfaces for parameters. The fields are not required and
+ * thus the methods of this class may return null.
+ *
+ * @see Parameter#setInfo(ParameterInfo)
+ * @see Parameter#getInfo()
+ * @author Justin Ross <jross@redhat.com>
+ * @version $Id$
+ */
+public interface ParameterInfo {
+
+ /**
+ * Gets the pretty name of the parameter.
+ *
+ * @return The String title of the parameter; it may
+ * be null
+ */
+ String getTitle();
+
+ /**
+ * Gets the parameter's reason for being.
+ *
+ * @return The String purpose of the parameter; it
+ * may be null
+ */
+ String getPurpose();
+
+ /**
+ * Gets an example value for the parameter.
+ *
+ * @return A String example value; it may be null
+ */
+ String getExample();
+
+ /**
+ * Gets a format description.
+ *
+ * @return A format String; it may be null
+ */
+ String getFormat();
+}
diff --git a/ccm-core/src/main/java/com/arsdigita/util/parameter/ParameterReader.java b/ccm-core/src/main/java/com/arsdigita/util/parameter/ParameterReader.java
new file mode 100644
index 000000000..4bf435512
--- /dev/null
+++ b/ccm-core/src/main/java/com/arsdigita/util/parameter/ParameterReader.java
@@ -0,0 +1,49 @@
+/*
+ * Copyright (C) 2003-2004 Red Hat Inc. All Rights Reserved.
+ *
+ * This library is free software; you can redistribute it and/or
+ * modify it under the terms of the GNU Lesser General Public License
+ * as published by the Free Software Foundation; either version 2.1 of
+ * the License, or (at your option) any later version.
+ *
+ * This library is distributed in the hope that it will be useful,
+ * but WITHOUT ANY WARRANTY; without even the implied warranty of
+ * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU
+ * Lesser General Public License for more details.
+ *
+ * You should have received a copy of the GNU Lesser General Public
+ * License along with this library; if not, write to the Free Software
+ * Foundation, Inc., 59 Temple Place, Suite 330, Boston, MA 02111-1307 USA
+ *
+ */
+package com.arsdigita.util.parameter;
+
+/**
+ * Reads an encoded string value for a parameter from storage. Any
+ * errors encountered while reading are added to an error list.
+ * This class is counterpart to ParameterWriter.
+ *
+ * Subject to change.
+ *
+ * @see Parameter#write(ParameterWriter, Object)
+ * @see ErrorList
+ * @see ParameterWriter
+ * @author Justin Ross <jross@redhat.com>
+ * @version $Id$
+ */
+public interface ParameterReader {
+
+ /**
+ * Reads an encoded String value for
+ * param from storage. If there are errors, they are
+ * added to errors.
+ *
+ * @param param The Parameter being read; it cannot
+ * be null
+ * @param errors The ErrorList that will collect any
+ * errors; it cannot be null
+ * @return The marshaled String value for
+ * param; it may be null
+ */
+ String read(Parameter param, ErrorList errors);
+}
diff --git a/ccm-core/src/main/java/com/arsdigita/util/parameter/ParameterWriter.java b/ccm-core/src/main/java/com/arsdigita/util/parameter/ParameterWriter.java
new file mode 100644
index 000000000..6794bfb49
--- /dev/null
+++ b/ccm-core/src/main/java/com/arsdigita/util/parameter/ParameterWriter.java
@@ -0,0 +1,44 @@
+/*
+ * Copyright (C) 2003-2004 Red Hat Inc. All Rights Reserved.
+ *
+ * This library is free software; you can redistribute it and/or
+ * modify it under the terms of the GNU Lesser General Public License
+ * as published by the Free Software Foundation; either version 2.1 of
+ * the License, or (at your option) any later version.
+ *
+ * This library is distributed in the hope that it will be useful,
+ * but WITHOUT ANY WARRANTY; without even the implied warranty of
+ * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU
+ * Lesser General Public License for more details.
+ *
+ * You should have received a copy of the GNU Lesser General Public
+ * License along with this library; if not, write to the Free Software
+ * Foundation, Inc., 59 Temple Place, Suite 330, Boston, MA 02111-1307 USA
+ *
+ */
+package com.arsdigita.util.parameter;
+
+/**
+ * Subject to change.
+ *
+ * Writes encoded parameter values to storage. Implementors define
+ * the exact nature of the storage.
+ *
+ * @see Parameter#write(ParameterWriter,Object)
+ * @see ParameterReader
+ * @author Justin Ross <jross@redhat.com>
+ * @version $Id$
+ */
+public interface ParameterWriter {
+
+ /**
+ * Writes the marshaled value for parameter
+ * param to storage.
+ *
+ * @param param The Parameter that is being written;
+ * it cannot be null
+ * @param value The encoded String value to store for
+ * param; it may be null
+ */
+ void write(Parameter param, String value);
+}
diff --git a/ccm-core/src/main/java/com/arsdigita/util/parameter/ResourceParameter.java b/ccm-core/src/main/java/com/arsdigita/util/parameter/ResourceParameter.java
new file mode 100644
index 000000000..ebc166975
--- /dev/null
+++ b/ccm-core/src/main/java/com/arsdigita/util/parameter/ResourceParameter.java
@@ -0,0 +1,153 @@
+/*
+ * Copyright (C) 2003-2004 Red Hat Inc. All Rights Reserved.
+ *
+ * This library is free software; you can redistribute it and/or
+ * modify it under the terms of the GNU Lesser General Public License
+ * as published by the Free Software Foundation; either version 2.1 of
+ * the License, or (at your option) any later version.
+ *
+ * This library is distributed in the hope that it will be useful,
+ * but WITHOUT ANY WARRANTY; without even the implied warranty of
+ * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU
+ * Lesser General Public License for more details.
+ *
+ * You should have received a copy of the GNU Lesser General Public
+ * License along with this library; if not, write to the Free Software
+ * Foundation, Inc., 59 Temple Place, Suite 330, Boston, MA 02111-1307 USA
+ *
+ */
+package com.arsdigita.util.parameter;
+
+import java.io.File;
+import java.io.FileInputStream;
+import java.io.FileNotFoundException;
+import java.io.InputStream;
+import java.net.URL;
+import java.util.Iterator;
+
+import org.apache.log4j.Logger;
+
+import com.arsdigita.util.UncheckedWrapperException;
+
+/**
+ * A parameter representing a JEE Resource (input stream).
+ *
+ * This takes in a path and makes sure that the resource exists either
+ * as a File or an actual resource. If it does, it returns the
+ * InputStream for the given Resource. If it does not, and if it is
+ * required, it logs an error. Otherwise, it returns null.
+ *
+ * Development note / CHANGELOG
+ * Had been deprecated for a while in favour of an URLParameter and a
+ * application specific resource: protocol extension (c.ad.util.protocol.resource).
+ * As of version 6.5 reverted to ResourceParameter to avoid non-standard extensions.
+ *
+ * @author Justin Ross <jross@redhat.com>
+ * @author Brett <bprucha@users.sourceforge net>
+ * @author PBoy <pboy@users.sourceforge net>
+ * @version $Id$
+ */
+public class ResourceParameter extends AbstractParameter {
+
+ private static final Logger s_log = Logger.getLogger(ResourceParameter.class);
+
+ private Object m_default = null;
+
+ public ResourceParameter(final String name) {
+ super(name, InputStream.class);
+ }
+
+ public ResourceParameter(final String name,
+ final int multiplicity,
+ final Object defaultValue) {
+
+ super(name, multiplicity, defaultValue, InputStream.class);
+ m_default = defaultValue;
+ }
+
+ /**
+ * Get default value and return it as InputStream.
+ *
+ * Developers note:
+ * This makes the trick to use Parameter.java interface rsp AbstractParameter
+ * for other types of parameter as String. If you don't overwrite this
+ * method, you will always get a casting error, because the parameter
+ * returns a string instead of the intended object!
+ *
+ * @return default value as InputStream
+ */
+ public Object getDefaultValue() {
+
+ if(m_default instanceof String) {
+ ErrorList errors = new ErrorList();
+ InputStream stream = (InputStream)unmarshal((String)m_default, errors);
+
+ if(!errors.isEmpty()) {
+ String strErrors = "";
+ for(Iterator i = errors.iterator(); i.hasNext(); ) {
+ ParameterError pe = (ParameterError)i.next();
+ strErrors += pe.getMessage() + "\r\n";
+ }
+ throw new UncheckedWrapperException(strErrors);
+ }
+
+ return stream;
+ } else
+ return m_default;
+ }
+
+ /**
+ * Unmarshals the encoded string value of the parameter to get the intended
+ * object type. It tries first to find a file of the specified name in the
+ * file system. If not successful it uses the classloader to find the file
+ * in the class path / jar files.
+ *
+ * @param value
+ * @param errors
+ * @return parameter value as an InputStream
+ */
+ @Override
+ protected Object unmarshal(String value, final ErrorList errors) {
+
+ // NOTE:
+ // This implementation will never find the file in the file system.
+ // The name has to be specified relativ to document root. So we must
+ // precede value with the context path, e.g. using
+ // c.ad.runtime.CCMResourceManager as soon as it's implementation is
+ // fixed / stable (when all modifications of the runtime environment
+ // are done).
+ File file = new File(value);
+
+ if (!file.exists()) {
+ // it is not a standard file so lets try to see if it
+ // is a resource
+ if (value.startsWith("/")) {
+ value = value.substring(1);
+ }
+
+ ClassLoader cload = Thread.currentThread().getContextClassLoader();
+ URL url = cload.getResource(value);
+ InputStream stream = cload.getResourceAsStream(value);
+ if (stream == null && isRequired()) {
+ s_log.error(value + " is not a valid file and is required");
+
+ final ParameterError error = new ParameterError
+ (this, "Resource not found");
+ errors.add(error);
+ }
+ return stream;
+ } else {
+ try {
+ return new FileInputStream(file);
+ } catch (FileNotFoundException ioe) {
+ // we know the file exists so this should not
+ // be an issue
+ s_log.error(value + " is not a valid file and is required", ioe);
+
+ errors.add(new ParameterError(this, ioe));
+
+ return null;
+ }
+ }
+ }
+}
diff --git a/ccm-core/src/main/java/com/arsdigita/util/parameter/SingletonParameter.java b/ccm-core/src/main/java/com/arsdigita/util/parameter/SingletonParameter.java
new file mode 100644
index 000000000..eb6d00d2d
--- /dev/null
+++ b/ccm-core/src/main/java/com/arsdigita/util/parameter/SingletonParameter.java
@@ -0,0 +1,62 @@
+/*
+ * Copyright (C) 2003-2004 Red Hat Inc. All Rights Reserved.
+ *
+ * This library is free software; you can redistribute it and/or
+ * modify it under the terms of the GNU Lesser General Public License
+ * as published by the Free Software Foundation; either version 2.1 of
+ * the License, or (at your option) any later version.
+ *
+ * This library is distributed in the hope that it will be useful,
+ * but WITHOUT ANY WARRANTY; without even the implied warranty of
+ * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU
+ * Lesser General Public License for more details.
+ *
+ * You should have received a copy of the GNU Lesser General Public
+ * License along with this library; if not, write to the Free Software
+ * Foundation, Inc., 59 Temple Place, Suite 330, Boston, MA 02111-1307 USA
+ *
+ */
+package com.arsdigita.util.parameter;
+
+import com.arsdigita.util.Classes;
+import com.arsdigita.util.UncheckedWrapperException;
+
+/**
+ * A parameter representing an instance of a Java class.
+ *
+ * Subject to change.
+ *
+ * @see Parameter
+ * @author Justin Ross <jross@redhat.com>
+ * @version $Id$
+ */
+public class SingletonParameter extends ClassParameter {
+
+ public SingletonParameter(final String name) {
+ super(name);
+ }
+
+ public SingletonParameter(final String name,
+ final int multiplicity,
+ final Object defaalt) {
+ super(name, multiplicity, defaalt);
+ }
+
+ protected String marshal(Object value) {
+ return super.marshal(value.getClass());
+ }
+
+ protected Object unmarshal(final String value, final ErrorList errors) {
+ final Class clacc = (Class) super.unmarshal(value, errors);
+ if(clacc == null) {
+ return null;
+ }
+
+ try {
+ return Classes.newInstance(clacc);
+ } catch (UncheckedWrapperException uwe) {
+ errors.add(new ParameterError(this, uwe.getRootCause()));
+ return null;
+ }
+ }
+}
diff --git a/ccm-core/src/main/java/com/arsdigita/util/parameter/SpecificClassParameter.java b/ccm-core/src/main/java/com/arsdigita/util/parameter/SpecificClassParameter.java
new file mode 100644
index 000000000..b26a220e0
--- /dev/null
+++ b/ccm-core/src/main/java/com/arsdigita/util/parameter/SpecificClassParameter.java
@@ -0,0 +1,73 @@
+/*
+ * Copyright (C) 2003-2004 Red Hat Inc. All Rights Reserved.
+ *
+ * This library is free software; you can redistribute it and/or
+ * modify it under the terms of the GNU Lesser General Public License
+ * as published by the Free Software Foundation; either version 2.1 of
+ * the License, or (at your option) any later version.
+ *
+ * This library is distributed in the hope that it will be useful,
+ * but WITHOUT ANY WARRANTY; without even the implied warranty of
+ * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU
+ * Lesser General Public License for more details.
+ *
+ * You should have received a copy of the GNU Lesser General Public
+ * License along with this library; if not, write to the Free Software
+ * Foundation, Inc., 59 Temple Place, Suite 330, Boston, MA 02111-1307 USA
+ *
+ */
+package com.arsdigita.util.parameter;
+
+/**
+ * A parameter representing a Java Class which is checked to be
+ * an implementation of a required class / interface.
+ *
+ * Subject to change.
+ *
+ * @see java.lang.Class
+ * @see Parameter
+ * @author Justin Ross <jross@redhat.com>
+ * @version $Id$
+ */
+public class SpecificClassParameter extends ClassParameter {
+
+
+ private Class m_requiredClass;
+
+ /**
+ * Constructor
+ * @param name
+ * @param multiplicity
+ * @param defaultObj
+ * @param requiredClass
+ */
+ public SpecificClassParameter(final String name,
+ final int multiplicity,
+ final Object defaultObj,
+ final Class requiredClass) {
+ super(name, multiplicity, defaultObj);
+ m_requiredClass = requiredClass;
+ }
+
+ /**
+ * Unmarshals a string representation of the parameter.
+ *
+ * @param value string representation of class, must be value != null
+ * @param errors
+ * @return
+ */
+ @Override
+ protected Object unmarshal(String value, ErrorList errors) {
+ Class theClass = (Class) super.unmarshal(value,errors);
+ if (theClass != null) {
+ if (!m_requiredClass.isAssignableFrom(theClass)) {
+ errors.add(new ParameterError(this, "class " + value +
+ " must implement : " +
+ m_requiredClass.getName()));
+ }
+ }
+
+ return theClass;
+ }
+
+}
diff --git a/ccm-core/src/main/java/com/arsdigita/util/parameter/StringArrayParameter.java b/ccm-core/src/main/java/com/arsdigita/util/parameter/StringArrayParameter.java
new file mode 100644
index 000000000..ec397cc62
--- /dev/null
+++ b/ccm-core/src/main/java/com/arsdigita/util/parameter/StringArrayParameter.java
@@ -0,0 +1,109 @@
+/*
+ * Copyright (C) 2004 Red Hat Inc. All Rights Reserved.
+ *
+ * This library is free software; you can redistribute it and/or
+ * modify it under the terms of the GNU Lesser General Public License
+ * as published by the Free Software Foundation; either version 2.1 of
+ * the License, or (at your option) any later version.
+ *
+ * This library is distributed in the hope that it will be useful,
+ * but WITHOUT ANY WARRANTY; without even the implied warranty of
+ * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU
+ * Lesser General Public License for more details.
+ *
+ * You should have received a copy of the GNU Lesser General Public
+ * License along with this library; if not, write to the Free Software
+ * Foundation, Inc., 59 Temple Place, Suite 330, Boston, MA 02111-1307 USA
+ *
+ */
+package com.arsdigita.util.parameter;
+
+// import com.arsdigita.util.parameter.StringParameter;
+// import com.arsdigita.util.parameter.ErrorList;
+import com.arsdigita.util.StringUtils;
+
+/**
+ * StringArrayParameter
+ *
+ * Usage Example:
+ *
+ * private static parameter exampleName ;
+ * exampleName = new StringArrayParameter(
+ * "com.arsdigita.package.example_name",
+ * Parameter.REQUIRED,
+ * new String[] {"String Example 01","String Example 02"}
+ * );
+ *
+ *
+ * @version $Id$
+ */
+public class StringArrayParameter extends StringParameter {
+
+ /**
+ *
+ * @param name: String literal
+ * @param multiplicity Indicator wether required (1) or not (0) (nullable)
+ * @param defaalt default value
+ */
+ public StringArrayParameter(final String name,
+ final int multiplicity,
+ final Object defaalt) {
+ super(name, multiplicity, defaalt);
+
+ }
+
+ /**
+ * Converts a String[] object into a literal representation.
+ *
+ * @param value
+ * @return
+ */
+ @Override
+ protected String marshal(final Object value) {
+ if (value == null) {
+ return null;
+ } else {
+ return StringUtils.join((String[])value, ',');
+ }
+ }
+
+ /**
+ *
+ * @param literal
+ * @param errors
+ * @return
+ */
+ @Override
+ protected Object unmarshal(final String literal,
+ final ErrorList errors) {
+ final String[] literals = StringUtils.split(literal, ',');
+ final String[] strings = new String[literals.length];
+
+ for (int i = 0; i < literals.length; i++) {
+ final String elem = literals[i];
+
+ strings[i] = (String) super.unmarshal(elem, errors);
+
+ if (!errors.isEmpty()) {
+ break;
+ }
+ }
+ return strings;
+ }
+
+ @Override
+ protected void doValidate(final Object value,
+ final ErrorList errors) {
+ if (value != null) {
+ final String[] strings = (String[]) value;
+
+ for (int i = 0; i < strings.length; i++) {
+ super.doValidate(strings[i], errors);
+
+ if (!errors.isEmpty()) {
+ break;
+ }
+ }
+ }
+ }
+}
diff --git a/ccm-core/src/main/java/com/arsdigita/util/parameter/StringParameter.java b/ccm-core/src/main/java/com/arsdigita/util/parameter/StringParameter.java
new file mode 100644
index 000000000..49c7f3f12
--- /dev/null
+++ b/ccm-core/src/main/java/com/arsdigita/util/parameter/StringParameter.java
@@ -0,0 +1,53 @@
+/*
+ * Copyright (C) 2003-2004 Red Hat Inc. All Rights Reserved.
+ *
+ * This library is free software; you can redistribute it and/or
+ * modify it under the terms of the GNU Lesser General Public License
+ * as published by the Free Software Foundation; either version 2.1 of
+ * the License, or (at your option) any later version.
+ *
+ * This library is distributed in the hope that it will be useful,
+ * but WITHOUT ANY WARRANTY; without even the implied warranty of
+ * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU
+ * Lesser General Public License for more details.
+ *
+ * You should have received a copy of the GNU Lesser General Public
+ * License along with this library; if not, write to the Free Software
+ * Foundation, Inc., 59 Temple Place, Suite 330, Boston, MA 02111-1307 USA
+ *
+ */
+package com.arsdigita.util.parameter;
+
+import org.apache.commons.beanutils.converters.StringConverter;
+import org.apache.log4j.Logger;
+
+/**
+ * A parameter representing a Java String.
+ *
+ * Subject to change.
+ *
+ * @see java.lang.String
+ * @see Parameter
+ * @author Justin Ross <jross@redhat.com>
+ * @version $Id$
+ */
+public class StringParameter extends AbstractParameter {
+
+ private static final Logger logger = Logger.getLogger(StringParameter.class);
+
+ static {
+ logger.debug("Static initalizer starting...");
+ Converters.set(String.class, new StringConverter());
+ logger.debug("Static initalizer finished.");
+ }
+
+ public StringParameter(final String name,
+ final int multiplicity,
+ final Object defaalt) {
+ super(name, multiplicity, defaalt, String.class);
+ }
+
+ public StringParameter(final String name) {
+ super(name, String.class);
+ }
+}
diff --git a/ccm-core/src/main/java/com/arsdigita/util/parameter/URLParameter.java b/ccm-core/src/main/java/com/arsdigita/util/parameter/URLParameter.java
new file mode 100644
index 000000000..5b23eab93
--- /dev/null
+++ b/ccm-core/src/main/java/com/arsdigita/util/parameter/URLParameter.java
@@ -0,0 +1,54 @@
+/*
+ * Copyright (C) 2003-2004 Red Hat Inc. All Rights Reserved.
+ *
+ * This library is free software; you can redistribute it and/or
+ * modify it under the terms of the GNU Lesser General Public License
+ * as published by the Free Software Foundation; either version 2.1 of
+ * the License, or (at your option) any later version.
+ *
+ * This library is distributed in the hope that it will be useful,
+ * but WITHOUT ANY WARRANTY; without even the implied warranty of
+ * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU
+ * Lesser General Public License for more details.
+ *
+ * You should have received a copy of the GNU Lesser General Public
+ * License along with this library; if not, write to the Free Software
+ * Foundation, Inc., 59 Temple Place, Suite 330, Boston, MA 02111-1307 USA
+ *
+ */
+package com.arsdigita.util.parameter;
+
+import java.net.MalformedURLException;
+import java.net.URL;
+
+/**
+ * Subject to change.
+ *
+ * A parameter representing a Java URL.
+ *
+ * @see java.net.URL
+ * @see Parameter
+ * @author Justin Ross <jross@redhat.com>
+ * @version $Id$
+ */
+public class URLParameter extends StringParameter {
+
+ public URLParameter(final String name) {
+ super(name);
+ }
+
+ public URLParameter(final String name,
+ final int multiplicity,
+ final Object defaalt) {
+ super(name, multiplicity, defaalt);
+ }
+
+ protected Object unmarshal(final String value, final ErrorList errors) {
+ try {
+ return new URL(value);
+ } catch (MalformedURLException mue) {
+ errors.add(new ParameterError(this, mue));
+ return null;
+ }
+ }
+}
diff --git a/ccm-core/src/main/java/com/arsdigita/xml/Element.java b/ccm-core/src/main/java/com/arsdigita/xml/Element.java
new file mode 100644
index 000000000..540bbd8fa
--- /dev/null
+++ b/ccm-core/src/main/java/com/arsdigita/xml/Element.java
@@ -0,0 +1,609 @@
+/*
+ * Copyright (C) 2001-2004 Red Hat Inc. All Rights Reserved.
+ *
+ * This library is free software; you can redistribute it and/or
+ * modify it under the terms of the GNU Lesser General Public License
+ * as published by the Free Software Foundation; either version 2.1 of
+ * the License, or (at your option) any later version.
+ *
+ * This library is distributed in the hope that it will be useful,
+ * but WITHOUT ANY WARRANTY; without even the implied warranty of
+ * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU
+ * Lesser General Public License for more details.
+ *
+ * You should have received a copy of the GNU Lesser General Public
+ * License along with this library; if not, write to the Free Software
+ * Foundation, Inc., 59 Temple Place, Suite 330, Boston, MA 02111-1307 USA
+ *
+ */
+package com.arsdigita.xml;
+
+import javax.xml.parsers.DocumentBuilderFactory;
+import javax.xml.parsers.ParserConfigurationException;
+import org.apache.log4j.Logger;
+import com.arsdigita.util.Assert;
+import com.arsdigita.util.UncheckedWrapperException;
+import java.util.ArrayList;
+import java.util.Collections;
+import java.util.Date;
+import java.util.Iterator;
+import java.util.List;
+import org.w3c.dom.NamedNodeMap;
+import org.w3c.dom.Attr;
+
+/**
+ * A wrapper class that implements some functionality of
+ * org.jdom.Element using org.w3c.dom.Element.
+ *
+ * @author Patrick McNeill
+ * @since ACS 4.5a
+ * @version $Revision$ $Date$
+ * @version $Id$
+ */
+public class Element {
+
+ private static final Logger s_log = Logger.getLogger(Element.class.getName());
+ protected org.w3c.dom.Element m_element;
+ /* DOM element that is being wrapped */
+ /**
+ * owner document
+ */
+ private org.w3c.dom.Document m_doc;
+
+ private static ThreadLocal s_localDocument = new ThreadLocal() {
+ @Override
+ public Object initialValue() {
+ try {
+ DocumentBuilderFactory builder =
+ DocumentBuilderFactory.newInstance();
+ builder.setNamespaceAware(true);
+ return builder.newDocumentBuilder().newDocument();
+ } catch (ParserConfigurationException e) {
+ s_log.error(e);
+ throw new UncheckedWrapperException(
+ "INTERNAL: Could not create thread local DOM document.",
+ e);
+ }
+ }
+
+ };
+
+ private static org.w3c.dom.Document getDocument() {
+ return (org.w3c.dom.Document) s_localDocument.get();
+ }
+
+// public org.w3c.dom.Document getOwnerDocument() {
+// if (null == m_doc) {
+// m_doc = (org.w3c.dom.Document) s_localDocument.get();
+// }
+//
+// return m_doc;
+// }
+// public void importElement(final Element element) {
+// element.m_element = (org.w3c.dom.Element) this.m_element
+// .getOwnerDocument().importNode(element.m_element,
+// true);
+// }
+
+ public void syncDocs() {
+ if (m_doc == null) {
+ m_doc = (org.w3c.dom.Document) s_localDocument.get();
+ }
+
+ if (!m_element.getOwnerDocument().equals(m_doc)) {
+ m_element = (org.w3c.dom.Element) m_doc.importNode(m_element, true);
+ }
+ }
+
+ /**
+ * Protected constructor to set up factories, etc. Does not actually
+ * create a new element. Used if we are programatically setting the
+ * m_element field later.
+ */
+ protected Element() {
+ }
+
+ /**
+ * Creates a new element with the given name and no assigned namespace.
+ *
+ * @param name the name of the element
+ */
+ public Element(String name) {
+ this();
+ Assert.exists(name, String.class);
+
+ m_element = getDocument().createElement(name);
+ }
+
+ /**
+ * Creates a new element with the given name, and assigns it to the
+ * namespace defined at uri. The namespace prefix is
+ * automatically determined.
+ *
+ * @param name the name of the element
+ * @param uri the URI for the namespace definition
+ */
+ public Element(String name, String uri) {
+ Assert.exists(name, String.class);
+ Assert.exists(uri, String.class);
+
+ m_element = getDocument().createElementNS(uri, name);
+ }
+
+ /**
+ * Creates a new element and adds it as a child to this
+ * element. elt.newChildElement("newElt") is
+ * equivalent to
+ *
+ * Element newElt = new Element("newElt");
+ * elt.addChild(newElt);
+ *
+ *
+ * @param name the name of the element
+ * @return the created child element.
+ * @pre m_element != null
+ */
+ public Element newChildElement(String name) {
+ Assert.exists(name, String.class);
+
+ if (m_doc == null) {
+ m_doc = this.m_element.getOwnerDocument();
+ }
+
+ Element result = new Element();
+ result.m_element = m_doc.createElement(name);
+ this.m_element.appendChild(result.m_element);
+ return result;
+ }
+
+ /**
+ * Creates a new element. Adds it as a child to this element
+ * element and assigns it to the namespace defined at uri.
+ * elt.newChildElement("newElt", namespace) is
+ * equivalent to
+ *
+ * Element newElt = new Element("newElt", namespace);
+ * elt.addChild(newElt);
+ *
+ *
+ * @param name the name of the Element
+ * @param uri the URI for the namespace definition
+ * @return the created child element.
+ * @pre m_element != null
+ */
+ public Element newChildElement(String name, String uri) {
+ Assert.exists(name, String.class);
+ Assert.exists(uri, String.class);
+
+ if (m_doc == null) {
+ m_doc = this.m_element.getOwnerDocument();
+ }
+
+ Element result = new Element();
+ result.m_element = m_doc.createElementNS(uri, name);
+ this.m_element.appendChild(result.m_element);
+ return result;
+ }
+
+ /**
+ * Copies the passed in element and all of its children to a new
+ * Element.
+ *
+ * @param copyFrom
+ * @return
+ */
+ public Element newChildElement(Element copyFrom) {
+ Assert.exists(copyFrom, Element.class);
+
+ if (m_doc == null) {
+ m_doc = this.m_element.getOwnerDocument();
+ }
+
+ Element copyTo = new Element();
+ copyTo.m_element = m_doc.createElementNS(copyFrom.m_element.getNamespaceURI(), copyFrom.getName());
+ this.m_element.appendChild(copyTo.m_element);
+ newChildElementHelper(copyFrom, copyTo);
+ return copyTo;
+ }
+
+ /**
+ * Copies the passed in element and all of its children to a new
+ * Element using the passed-in name.
+ *
+ * @param name
+ * @param copyFrom
+ * @return
+ */
+ public Element newChildElement(String name, Element copyFrom) {
+ if (m_doc == null) {
+ m_doc = this.m_element.getOwnerDocument();
+ }
+
+ Element copyTo = new Element();
+ copyTo.m_element = m_doc.createElement(name);
+ this.m_element.appendChild(copyTo.m_element);
+ newChildElementHelper(copyFrom, copyTo);
+ return copyTo;
+ }
+
+ /**
+ * Copies the passed in element and all of its children to a new
+ * Element using the passed-in name.
+ *
+ * @param name
+ * @param uri
+ * @param copyFrom
+ * @return
+ */
+ public Element newChildElement(String name, String uri, Element copyFrom) {
+ if (m_doc == null) {
+ m_doc = this.m_element.getOwnerDocument();
+ }
+
+ Element copyTo = new Element();
+ copyTo.m_element = m_doc.createElementNS(uri, name);
+ this.m_element.appendChild(copyTo.m_element);
+ newChildElementHelper(copyFrom, copyTo);
+ return copyTo;
+ }
+
+ private void newChildElementHelper(Element copyFrom, Element copyTo) {
+ copyTo.setText(copyFrom.getText());
+
+
+ NamedNodeMap nnm = copyFrom.m_element.getAttributes();
+
+ if (nnm != null) {
+ for (int i = 0; i < nnm.getLength(); i++) {
+ Attr attr = (org.w3c.dom.Attr) nnm.item(i);
+ copyTo.addAttribute(attr.getName(), attr.getValue());
+ }
+ }
+
+ Iterator iter = copyFrom.getChildren().iterator();
+
+ while (iter.hasNext()) {
+ Element child = (Element) iter.next();
+ copyTo.newChildElement(child);
+ }
+
+ }
+
+ /**
+ * Adds an attribute to the element.
+ *
+ * @param name the name of the attribute
+ * @param value the value of the attribute
+ * @return this element.
+ */
+ public Element addAttribute(String name, String value) {
+ Assert.exists(name, String.class);
+
+ m_element.setAttribute(name, value);
+
+ return this;
+ }
+
+ public Element addAttribute(String name,
+ String value,
+ String ns) {
+ Assert.exists(name, String.class);
+ Assert.exists(ns, String.class);
+
+ m_element.setAttributeNS(ns, name, value);
+
+ return this;
+ }
+
+ /**
+ * Adds a child element to this element.
+ *
+ * @param newContent the new child element
+ * @return this element.
+ */
+ public Element addContent(Element newContent) {
+ Assert.exists(newContent, Element.class);
+
+ newContent.importInto(m_element.getOwnerDocument());
+ m_element.appendChild(newContent.getInternalElement());
+
+ return this;
+ }
+
+ /**
+ * Sets the text value of the current element (the part between the
+ * tags). If the passed in text is null then it is converted to
+ * the empty string.
+ *
+ * @param text the text to include
+ * @return this element.
+ */
+ public Element setText(String text) {
+ if (text == null) {
+ // This converts the null to the empty string because
+ // org.w3c.dom does not like null and HTML does not
+ // differentiate between "" and null. The other option
+ // is to throw the NPE which causes other problems
+ text = "";
+ }
+ org.w3c.dom.Text textElem =
+ m_element.getOwnerDocument().createTextNode(text);
+ m_element.appendChild(textElem);
+
+ return this;
+ }
+
+ /**
+ * Returns the concatenation of all the text in all child nodes
+ * of the current element.
+ *
+ * @return
+ */
+ public String getText() {
+ StringBuilder result = new StringBuilder();
+
+ org.w3c.dom.NodeList nl = m_element.getChildNodes();
+
+ for (int i = 0; i < nl.getLength(); i++) {
+ org.w3c.dom.Node n = nl.item(i);
+
+ if (n.getNodeType() == org.w3c.dom.Node.TEXT_NODE) {
+ result.append(((org.w3c.dom.Text) n).getData());
+ }
+ }
+
+ return result.toString();
+ }
+
+ public Element setCDATASection(String cdata) {
+ s_log.debug("Setting CDATA section to '" + cdata + "'.");
+
+ if (cdata == null) {
+ cdata = "";
+ }
+
+ org.w3c.dom.CDATASection cdataSection =
+ m_element.getOwnerDocument().createCDATASection(cdata);
+
+ m_element.appendChild(cdataSection);
+
+ return this;
+ }
+
+ public String getCDATASection() {
+ StringBuilder result = new StringBuilder();
+
+ org.w3c.dom.NodeList nl = m_element.getChildNodes();
+
+ for (int i = 0; i < nl.getLength(); i++) {
+ org.w3c.dom.Node n = nl.item(i);
+
+ if (n.getNodeType() == org.w3c.dom.Node.CDATA_SECTION_NODE) {
+ result.append(((org.w3c.dom.CDATASection) n).getData());
+ }
+ }
+
+ String str = result.toString();
+
+ s_log.debug("Fetched this from CDATA section: " + str);
+
+ return str;
+ }
+
+ /**
+ * Returns a List of all the child elements nested
+ * directly (one level deep) within this element, as Element
+ * objects. If this target element has no nested elements, an empty
+ * List is returned. The returned list is "live", so
+ * changes to it affect the element's actual contents.
+ * + * + * This performs no recursion, so elements nested two levels deep would + * have to be obtained with: + *
+ * Iterator itr = currentElement.getChildren().iterator();
+ * while (itr.hasNext()) {
+ * Element oneLevelDeep = (Element)nestedElements.next();
+ * List twoLevelsDeep = oneLevelDeep.getChildren();
+ * // Do something with these children
+ * }
+ *
+ * @return list of child Element objects for this element.
+ */
+ public java.util.List getChildren() {
+ java.util.List retval = new java.util.ArrayList();
+ org.w3c.dom.NodeList nl = m_element.getChildNodes();
+ for (int i = 0; i < nl.getLength(); i++) {
+ org.w3c.dom.Node n = nl.item(i);
+ if (n instanceof org.w3c.dom.Element) {
+ Element elt = new Element();
+ elt.m_element = (org.w3c.dom.Element) n;
+ retval.add(elt);
+ }
+ }
+ return retval;
+ }
+
+ public java.util.Map getAttributes() {
+ // Retrieve the attributes of the DOM Element
+ org.w3c.dom.NamedNodeMap attributeNodeMap =
+ m_element.getAttributes();
+
+ // Create the HashMap that we will return the attributes
+ // in
+ java.util.HashMap returnMap = new java.util.HashMap();
+
+ // Copy the attribute values in the NamedNodeMap to the
+ // HashMap
+ for (int i = 0; i < attributeNodeMap.getLength(); ++i) {
+ // Get the Node
+ org.w3c.dom.Node attributeNode = attributeNodeMap.item(i);
+ // Copy the name and value to the map
+ returnMap.put(attributeNode.getNodeName(),
+ attributeNode.getNodeValue());
+ }
+
+ // Return the HashMap
+ return returnMap;
+ }
+
+ /**
+ * Retrieves an attribute value by name.
+ * @param name The name of the attribute to retrieve
+ * @return The Attr value as a string,
+ * or the empty string if that attribute does not have a specified
+ * or default value.
+ */
+ public String getAttribute(String name) {
+ return m_element.getAttribute(name);
+ }
+
+ public boolean hasAttribute(String name) {
+ return m_element.hasAttribute(name);
+ }
+
+ public String getName() {
+ return m_element.getTagName();
+ }
+
+ /**
+ * Functions to allow this class to interact appropriately with the
+ * Document class (for example, allows nodes to be moved around,
+ * and so on).
+ *
+ * @return the internal DOM Element.
+ */
+ protected final org.w3c.dom.Element getInternalElement() {
+ return m_element;
+ }
+
+ /**
+ * Imports the internal node into another document.
+ * This could also be done with a combination of getInternalElement
+ * and a setInternalElement function.
+ *
+ * @param doc the org.w3c.dom.Document to import into
+ */
+ protected void importInto(org.w3c.dom.Document doc) {
+ /*
+ Exception e = new Exception();
+ java.io.StringWriter sw = new java.io.StringWriter();
+ e.printStackTrace(new java.io.PrintWriter(sw));
+ System.out.println(sw.toString().substring(0, 300));
+ */
+
+ m_element = (org.w3c.dom.Element) doc.importNode(m_element, true);
+ }
+
+ /**
+ * Workaround for bug in some versions of Xerces.
+ * For some reason, importNode doesn't also copy attribute
+ * values unless you call getValue() on them first. This may
+ * be fixed in a later version of Xerces. In the meantime,
+ * calling visitAllAttributes(node) before importNode should
+ * help.
+ *
+ * @param node the org.w3c.dom.Node about to be imported
+ * @deprecated with no replacement, 1 May 2003
+ */
+ public static void visitAllAttributes(org.w3c.dom.Node node) {
+ org.w3c.dom.NamedNodeMap nnm = node.getAttributes();
+ if (nnm != null) {
+ for (int i = 0; i < nnm.getLength(); i++) {
+ org.w3c.dom.Attr attr = (org.w3c.dom.Attr) nnm.item(i);
+ attr.getValue();
+ }
+ }
+ org.w3c.dom.NodeList nl = node.getChildNodes();
+ if (nl != null) {
+ for (int i = 0; i < nl.getLength(); i++) {
+ visitAllAttributes(nl.item(i));
+ }
+ }
+ }
+
+ /**
+ * retrieve an unordered list of strings relating to node tree including
+ * and below the current element. Strings include element names, attribute names,
+ * attribute values, text and CData sections
+ * @return
+ */
+ private List getXMLFragments() {
+
+ List unsortedList = new ArrayList();
+ unsortedList.add(getName());
+ unsortedList.add(getText());
+ // CData sections are not included in getChildren()
+ unsortedList.add(getCDATASection());
+ Iterator it = getAttributes().entrySet().iterator();
+ while (it.hasNext()) {
+ java.util.Map.Entry entry = (java.util.Map.Entry) it.next();
+ unsortedList.add(entry.getKey());
+ unsortedList.add(entry.getValue());
+ }
+ Iterator childElements = getChildren().iterator();
+ while (childElements.hasNext()) {
+ Element el = (Element) childElements.next();
+ unsortedList.addAll(el.getXMLFragments());
+ }
+ return unsortedList;
+
+ }
+
+ /**
+ * retrieve a string that is an ordered concatenation of all information describing
+ * this node and its subnodes, suitable as the basis of a hashCode or equals
+ * implementation.
+ * @return
+ */
+ protected String getXMLHashString() {
+ // attributes and child nodes are retrieved as HashMap and List
+ // respectively. These make no guarantees about the order of
+ // iteration, and so we sort here to make sure the same element
+ // will return the same XMLHash
+ List sortedList = getXMLFragments();
+ Collections.sort(sortedList);
+ StringBuilder xml = new StringBuilder();
+ Iterator xmlFragments = sortedList.iterator();
+ while (xmlFragments.hasNext()) {
+ xml.append(xmlFragments.next());
+ }
+ s_log.debug("getXMLHashString: " + xml.toString());
+ return xml.toString();
+ }
+
+ @Override
+ public int hashCode() {
+ Date start = new Date();
+ String hashString = getXMLHashString();
+ s_log.debug(
+ "hashCode: getXMLString took "
+ + (new Date().getTime() - start.getTime())
+ + " millisecs");
+
+ return hashString.hashCode();
+
+ }
+
+ @Override
+ public boolean equals(Object other) {
+ s_log.debug("equals invoked");
+ Date start = new Date();
+ if (other == null) {
+ return false;
+ }
+ if (!other.getClass().equals(Element.class)) {
+ return false;
+ }
+ Element otherElement = (Element) other;
+ String thisXML = getXMLHashString();
+ String otherXML = otherElement.getXMLHashString();
+ s_log.debug(
+ "Equals: getXMLString twice took "
+ + (new Date().getTime() - start.getTime())
+ + " millisecs");
+ return thisXML.equals(otherXML);
+
+ }
+
+}
diff --git a/ccm-core/src/main/java/com/arsdigita/xml/Formatter.java b/ccm-core/src/main/java/com/arsdigita/xml/Formatter.java
new file mode 100644
index 000000000..2876190fa
--- /dev/null
+++ b/ccm-core/src/main/java/com/arsdigita/xml/Formatter.java
@@ -0,0 +1,41 @@
+/*
+ * Copyright (C) 2003-2004 Red Hat Inc. All Rights Reserved.
+ *
+ * This library is free software; you can redistribute it and/or
+ * modify it under the terms of the GNU Lesser General Public License
+ * as published by the Free Software Foundation; either version 2.1 of
+ * the License, or (at your option) any later version.
+ *
+ * This library is distributed in the hope that it will be useful,
+ * but WITHOUT ANY WARRANTY; without even the implied warranty of
+ * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU
+ * Lesser General Public License for more details.
+ *
+ * You should have received a copy of the GNU Lesser General Public
+ * License along with this library; if not, write to the Free Software
+ * Foundation, Inc., 59 Temple Place, Suite 330, Boston, MA 02111-1307 USA
+ *
+ */
+package com.arsdigita.xml;
+
+/**
+ * An interface providing an API for converting an object
+ * to a string. Thus instead of doing
+ *
+ * Date today = new Date();
+ * element.addAttribute("today", date.toString());
+ *
+ * we can do:
+ *
+ * Date today = new Date();
+ * element.addAttribute("today", XML.format(date));
+ *
+ * Or if you require a non-default format:
+ *
+ * Date today = new Date();
+ * element.addAttribute("today", new DateTimeFormatter.format(today));
+ *
+ */
+public interface Formatter {
+ String format(Object value);
+}
diff --git a/ccm-core/src/main/java/com/arsdigita/xml/XML.java b/ccm-core/src/main/java/com/arsdigita/xml/XML.java
new file mode 100644
index 000000000..8d049781c
--- /dev/null
+++ b/ccm-core/src/main/java/com/arsdigita/xml/XML.java
@@ -0,0 +1,284 @@
+/*
+ * Copyright (C) 2003-2004 Red Hat Inc. All Rights Reserved.
+ *
+ * This library is free software; you can redistribute it and/or
+ * modify it under the terms of the GNU Lesser General Public License
+ * as published by the Free Software Foundation; either version 2.1 of
+ * the License, or (at your option) any later version.
+ *
+ * This library is distributed in the hope that it will be useful,
+ * but WITHOUT ANY WARRANTY; without even the implied warranty of
+ * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU
+ * Lesser General Public License for more details.
+ *
+ * You should have received a copy of the GNU Lesser General Public
+ * License along with this library; if not, write to the Free Software
+ * Foundation, Inc., 59 Temple Place, Suite 330, Boston, MA 02111-1307 USA
+ *
+ */
+package com.arsdigita.xml;
+
+import com.arsdigita.util.UncheckedWrapperException;
+
+import com.arsdigita.xml.formatters.DateTimeFormatter;
+
+import java.io.IOException;
+import java.io.InputStream;
+import java.io.PrintWriter;
+import java.io.StringWriter;
+import java.util.Date;
+import java.util.HashMap;
+import java.util.Iterator;
+import java.util.Map;
+
+import javax.xml.parsers.ParserConfigurationException;
+import javax.xml.parsers.SAXParser;
+import javax.xml.parsers.SAXParserFactory;
+
+import org.xml.sax.SAXException;
+import org.xml.sax.helpers.DefaultHandler;
+
+
+import org.apache.log4j.Logger;
+
+/**
+ * Provides a set of static helper methods for dealing with XML,
+ * including file parsing & object -> string serialization
+ */
+public class XML {
+
+ private static final Logger s_log = Logger.getLogger(XML.class);
+
+ // private static XMLConfig s_config;
+
+ private static final Map s_formatters = new HashMap();
+ static {
+ s_log.debug("Static initalizer starting...");
+ s_formatters.put(Date.class, new DateTimeFormatter());
+ s_log.debug("Static initalizer finished.");
+ }
+
+ /**
+ * Constructor. All methods are static, no initialization required.
+ */
+ private XML() {}
+
+ /**
+ * Retrieves the current configuration
+ public static XMLConfig getConfig() {
+ if (s_config == null) {
+ s_config = new XMLConfig();
+ s_config.load();
+ }
+ return s_config;
+ }
+ */
+
+ /**
+ * Registers a formatter for serializing objects of a
+ * class to a String suitable for XML output.
+ * @param klass
+ * @param formatter
+ */
+ public static void registerFormatter(Class klass,
+ Formatter formatter) {
+ s_formatters.put(klass, formatter);
+ }
+
+ /**
+ * Unregisters a formatter against a class.
+ * @param klass
+ */
+ public static void unregisterFormatter(Class klass) {
+ s_formatters.remove(klass);
+ }
+
+ /**
+ * Gets a directly registered formatter for a class.
+ * @param klass the class to find a formatter for
+ * @return the formatter, or null if non is registered
+ */
+ public static Formatter getFormatter(Class klass) {
+ return (Formatter)s_formatters.get(klass);
+ }
+
+ /**
+ * Looks for the best matching formatter.
+ *
+ * @param klass the class to find a formatter for
+ * @return the formatter, or null if non is registered
+ */
+ public static Formatter findFormatter(Class klass) {
+ Formatter formatter = null;
+ while (formatter == null && klass != null) {
+ formatter = getFormatter(klass);
+ klass = klass.getSuperclass();
+ }
+ return formatter;
+ }
+
+ /**
+ * Converts an object to a String using the closest
+ * matching registered Formatter implementation. Looks
+ * for a formatter registered against the object's
+ * class first, then its superclass, etc. If no formatter
+ * is found, uses the toString() method.
+ *
+ * @param value
+ * @return
+ */
+ public static String format(Object value) {
+ if (value == null) {
+ return null;
+ }
+
+ if (value instanceof String) {
+ return (String)value;
+ }
+
+ Formatter formatter = findFormatter(value.getClass());
+ if (formatter == null) {
+ if (s_log.isDebugEnabled()) {
+ s_log.debug("No formatter for " + value.getClass());
+ }
+ return value.toString();
+ }
+ if (s_log.isDebugEnabled()) {
+ s_log.debug("Processing " + value.getClass() +
+ " with " + formatter.getClass());
+ }
+ return formatter.format(value);
+ }
+
+ /**
+ * Processes an XML file with the default SAX Parser, with
+ * namespace processing, schema validation & DTD validation
+ * enabled.
+ *
+ * @param path the XML file relative to the webapp root
+ * @param handler the content handler
+ */
+ public static final void parseResource(String path,
+ DefaultHandler handler) {
+ if (s_log.isDebugEnabled()) {
+ s_log.debug("Processing resource " + path +
+ " with " + handler.getClass());
+ }
+
+ if (path.startsWith("/")) {
+ path = path.substring(1);
+ }
+
+ ClassLoader cload = Thread.currentThread().getContextClassLoader();
+ InputStream stream = cload.getResourceAsStream(path);
+
+ if (stream == null) {
+ throw new IllegalArgumentException("no such resource: " + path);
+ }
+
+ parse(stream, handler);
+ }
+
+ /**
+ * Processes an XML file with the default SAX Parser, with
+ * namespace processing, schema validation & DTD validation
+ * enabled.
+ *
+ * @param source the xml input stream
+ * @param handler the content handler
+ */
+ public static final void parse(InputStream source,
+ DefaultHandler handler) {
+ if (s_log.isDebugEnabled()) {
+ s_log.debug("Processing stream " + source +
+ " with " + handler.getClass());
+ }
+
+ try {
+ // ToDo (pboy): We should use
+ // SAXParserFactory.newInstance(String clName, ClassLoader clLoader)
+ // instead to achieve independence of a JVM wide acceptable
+ // configuration (affecting all CCM instances which may run in a
+ // container).
+ // Requires additional modifications in c.ad.util.xml.XML
+ SAXParserFactory spf = SAXParserFactory.newInstance();
+ spf.setFeature("http://xml.org/sax/features/namespaces", true);
+ SAXParser parser = spf.newSAXParser();
+ parser.parse(source, handler);
+ } catch (ParserConfigurationException e) {
+ throw new UncheckedWrapperException("error parsing stream", e);
+ } catch (SAXException e) {
+ if (e.getException() != null) {
+ throw new UncheckedWrapperException("error parsing stream",
+ e.getException());
+ } else {
+ throw new UncheckedWrapperException("error parsing stream", e);
+ }
+ } catch (IOException e) {
+ throw new UncheckedWrapperException("error parsing stream", e);
+ }
+ }
+
+ /**
+ * This visitor is called by {@link #traverse(Element, int, XML.Action)}.
+ **/
+ public interface Action {
+ void apply(Element elem, int level);
+ }
+
+ /**
+ * Prints the skeleton structure of the element to the supplied print
+ * writer.
+ * @param element
+ * @param writer
+ **/
+ public static void toSkeleton(final Element element,
+ final PrintWriter writer) {
+
+ XML.traverse(element, 0, new Action() {
+ @Override
+ public void apply(Element elem, int level) {
+ final String padding = " ";
+ for (int ii=0; iijdoe. The screen name is used as user name for logins (if
- * the system if configured so, otherwise the email address of the user is
+ * For example a the John Doe might have the scree name
+ * jdoe. The screen name is used as user name for logins (if
+ * the system if configured so, otherwise the email address of the user is
* used).
*/
- @Column(name = "screen_name", length = 255, nullable = false)
+ @Column(name = "screen_name", length = 255, nullable = false, unique = true)
@NotBlank
@XmlElement(name = "screen-name", namespace = CORE_XML_NS)
private String screenName;
/**
- * A user can be banned which means that he or she can't login into the
+ * A user can be banned which means that he or she can't login into the
* system anymore.
*/
@Column(name = "banned")
@@ -96,7 +104,8 @@ public class User extends Party implements Serializable {
private boolean banned;
/**
- * An alias for the user used in an another system for SSO, for example LDAP.
+ * An alias for the user used in an another system for SSO, for example
+ * LDAP.
*/
@Column(name = "sso_login", length = 512)
@XmlElement(name = "sso-login", namespace = CORE_XML_NS)
@@ -117,25 +126,25 @@ public class User extends Party implements Serializable {
private String salt;
/**
- * The hash algorithm used to hash the password. This allows us
- * the change to another, stronger hash algorithm without invalidating
- * existing accounts. The algorithm to use for new passwords can be
- * configured by the administrator.
- *
+ * The hash algorithm used to hash the password. This allows us the change
+ * to another, stronger hash algorithm without invalidating existing
+ * accounts. The algorithm to use for new passwords can be configured by the
+ * administrator.
+ *
*/
@Column(name = "hash_algorithm", length = 64)
@XmlTransient
private String hashAlgorithm;
/**
- * Indicates that the user should be forced to change his or her password
- * on the next login.
+ * Indicates that the user should be forced to change his or her password on
+ * the next login.
*/
@Column(name = "password_reset_required")
private boolean passwordResetRequired;
/**
- * Question the recover a forgotten password.
+ * Question the recover a forgotten password.
*/
@Column(name = "password_question", length = 2048)
@XmlElement(name = "password-question", namespace = CORE_XML_NS)
diff --git a/ccm-core/src/main/java/org/libreccm/core/UserManager.java b/ccm-core/src/main/java/org/libreccm/core/UserManager.java
new file mode 100644
index 000000000..3253952fd
--- /dev/null
+++ b/ccm-core/src/main/java/org/libreccm/core/UserManager.java
@@ -0,0 +1,113 @@
+/*
+ * Copyright (C) 2015 LibreCCM Foundation.
+ *
+ * This library is free software; you can redistribute it and/or
+ * modify it under the terms of the GNU Lesser General Public
+ * License as published by the Free Software Foundation; either
+ * version 2.1 of the License, or (at your option) any later version.
+ *
+ * This library is distributed in the hope that it will be useful,
+ * but WITHOUT ANY WARRANTY; without even the implied warranty of
+ * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU
+ * Lesser General Public License for more details.
+ *
+ * You should have received a copy of the GNU Lesser General Public
+ * License along with this library; if not, write to the Free Software
+ * Foundation, Inc., 51 Franklin Street, Fifth Floor, Boston,
+ * MA 02110-1301 USA
+ */
+package org.libreccm.core;
+
+
+import java.io.UnsupportedEncodingException;
+import java.security.MessageDigest;
+import java.security.NoSuchAlgorithmException;
+import java.util.Random;
+
+import javax.enterprise.context.RequestScoped;
+import javax.inject.Inject;
+
+/**
+ * This class provides complex operations on {@link User} objects like updating
+ * the password. To use this class add an injection point to your class.
+ *
+ * @author Jens Pelzetter
+ */
+@RequestScoped
+public class UserManager {
+
+ /**
+ * {@link UserRepository} for interacting with the database. The method
+ * takes care of hashing the password with random salt.
+ *
+ */
+ @Inject
+ private transient UserRepository userRepository;
+
+ /**
+ * Update the password of an user.
+ *
+ * @param user The user whose password is to be updated.
+ * @param password The new password.
+ */
+ public void updatePassword(final User user, final String password) {
+
+ try {
+ final Random random = new Random(System.currentTimeMillis());
+ final byte[] passwordBytes = password.getBytes("UTF-8");
+ final byte[] salt = new byte[getSaltLength()];
+ random.nextBytes(salt);
+
+ final byte[] saltedPassword = new byte[passwordBytes.length
+ + salt.length];
+ System.arraycopy(passwordBytes,
+ 0,
+ saltedPassword,
+ 0,
+ passwordBytes.length);
+ System.arraycopy(salt,
+ 0,
+ saltedPassword,
+ passwordBytes.length,
+ salt.length);
+ final MessageDigest digest = MessageDigest.getInstance(
+ getHashAlgorithm());
+ final byte[] hashedBytes = digest.digest(saltedPassword);
+
+ final String hashedPassword = new String(hashedBytes, "UTF-8");
+
+ user.setPassword(hashedPassword);
+ userRepository.save(user);
+
+ } catch (NoSuchAlgorithmException ex) {
+ throw new RuntimeException(String.format(
+ "Configured hash algorithm '%s 'is not available.",
+ getHashAlgorithm()), ex);
+ } catch (UnsupportedEncodingException ex) {
+ throw new RuntimeException("UTF-8 charset is not supported.");
+ }
+ }
+
+ /**
+ * Gets the hash algorithm to use.
+ *
+ * ToDo: Make configurable.
+ *
+ * @return At the moment SHA-512, will be made configurable.
+ */
+ private String getHashAlgorithm() {
+ return "SHA-512";
+ }
+
+ /**
+ * Returns the length for the salt (number of bytes).
+ *
+ * ToDo: Make configurable.
+ *
+ * @return At the moment 256. Will be made configurable.
+ */
+ private int getSaltLength() {
+ return 256;
+ }
+
+}
diff --git a/ccm-core/src/main/java/org/libreccm/core/UserRepository.java b/ccm-core/src/main/java/org/libreccm/core/UserRepository.java
new file mode 100644
index 000000000..8fefffb5e
--- /dev/null
+++ b/ccm-core/src/main/java/org/libreccm/core/UserRepository.java
@@ -0,0 +1,91 @@
+/*
+ * Copyright (C) 2015 LibreCCM Foundation.
+ *
+ * This library is free software; you can redistribute it and/or
+ * modify it under the terms of the GNU Lesser General Public
+ * License as published by the Free Software Foundation; either
+ * version 2.1 of the License, or (at your option) any later version.
+ *
+ * This library is distributed in the hope that it will be useful,
+ * but WITHOUT ANY WARRANTY; without even the implied warranty of
+ * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU
+ * Lesser General Public License for more details.
+ *
+ * You should have received a copy of the GNU Lesser General Public
+ * License along with this library; if not, write to the Free Software
+ * Foundation, Inc., 51 Franklin Street, Fifth Floor, Boston,
+ * MA 02110-1301 USA
+ */
+package org.libreccm.core;
+
+import java.util.List;
+
+import javax.enterprise.context.RequestScoped;
+import javax.persistence.TypedQuery;
+
+/**
+ *
+ * @author Jens Pelzetter
+ */
+@RequestScoped
+public class UserRepository extends AbstractEntityRepository