All Downloads are FREE. Search and download functionalities are using the official Maven repository.

rs.data.file.storage.XmlStorageStrategy Maven / Gradle / Ivy

The newest version!
/*
 * This file is part of RS Library (Data File Library).
 *
 *  RS Library is free software: you can redistribute it 
 *  and/or modify it under the terms of version 3 of the GNU 
 *  Lesser General Public  License as published by the Free Software 
 *  Foundation.
 *  
 *  RS 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 RS Library.  If not, see 
 *  .
 */
package rs.data.file.storage;

import java.io.File;
import java.io.FileOutputStream;
import java.io.IOException;
import java.io.OutputStreamWriter;
import java.io.Serializable;
import java.io.Writer;
import java.lang.reflect.InvocationTargetException;
import java.util.ArrayList;
import java.util.Collection;
import java.util.HashMap;
import java.util.Map;

import org.apache.commons.beanutils.PropertyUtils;
import org.apache.commons.configuration2.HierarchicalConfiguration;
import org.apache.commons.configuration2.XMLConfiguration;
import org.apache.commons.io.Charsets;

import rs.baselib.bean.IBean;
import rs.baselib.configuration.ConfigurationUtils;
import rs.baselib.lang.LangUtils;
import rs.data.api.IDaoFactory;
import rs.data.api.bo.IGeneralBO;
import rs.data.api.dao.IGeneralDAO;

/**
 * Storage strategy for XML files.
 * @param  type of ID for Business Objects to be managed
 * @param  type of Business Object the strategy manages
 * @author ralph
 *
 */
public class XmlStorageStrategy> extends AbstractStorageStrategy {

	/**
	 * Constructor.
	 * @param daoFactory - the DAO factory this strategy belongs to
	 */
	public XmlStorageStrategy(IDaoFactory daoFactory) {
		super(daoFactory);
	}

	/**
	 * {@inheritDoc}
	 */
	@Override
	public void load(T bo, K id, File specifier) throws IOException {
		// Find all relevant property names
		Collection propertyNames = new ArrayList(getPropertyNames(bo));
		propertyNames.add("id");
		try {
			XMLConfiguration cfg = ConfigurationUtils.getXmlConfiguration(specifier);
			// Removed in 2.0: cfg.setListDelimiter((char)0);
			for (String name : propertyNames) {
				try {
					HierarchicalConfiguration subConfig = cfg.configurationAt(name+"(0)");
					bo.set(name, loadValue(subConfig));
				} catch (IllegalArgumentException e) {
					bo.set(name,  null);
				}
			}
		} catch (Exception e) {
			throw new IOException("Cannot load XML file", e);
		}
	}

	/**
	 * Internal helper to load a sub configuration.
	 * 

Descendants shall not override this but several specific helper methods.

* @param cfg the tag to be loaded * @return th eobject represented by this tag * @see #loadBusinessObject(Class, HierarchicalConfiguration) * @see #loadCollection(Class, HierarchicalConfiguration) * @see #loadMap(Class, HierarchicalConfiguration) * @see #loadBean(Class, HierarchicalConfiguration) * @see #loadSerialized(Class, HierarchicalConfiguration) * @throws IOException - the I/O exception when operating on a filesystem * @throws ClassNotFoundException - when class could not be found * @throws InstantiationException - when object cannot be instantiated * @throws IllegalAccessException - when constructor is not accessible * @throws InvocationTargetException - when constructor threw an exception * @throws NoSuchMethodException - when constructor does not exist */ protected Object loadValue(HierarchicalConfiguration cfg) throws IOException, ClassNotFoundException, InstantiationException, IllegalAccessException, InvocationTargetException, NoSuchMethodException { String className = cfg.getString("[@class]"); if ((className != null) && !className.isEmpty()) { Class clazz = LangUtils.forName(className); Object rc = null; if (IGeneralBO.class.isAssignableFrom(clazz)) { rc = loadBusinessObject(clazz, cfg); } else if (Collection.class.isAssignableFrom(clazz)) { rc = loadCollection(clazz, cfg); } else if (Map.class.isAssignableFrom(clazz)) { rc = loadMap(clazz, cfg); } else if (IBean.class.isAssignableFrom(clazz)) { rc = loadBean(clazz, cfg); } else { rc = loadSerialized(clazz, cfg); if (rc == null) { throw new RuntimeException("Cannot unserialize property: "+className+": "+cfg.getString("")); } } return rc; } else { return null; } } /** * Loads a {@link IGeneralBO business object} from the XML tag. * @param clazz class to be loaded * @param cfg configuration of class * @return the loaded object * @throws IOException - the I/O exception when operating on a filesystem * @throws ClassNotFoundException - when class could not be found * @throws InstantiationException - when object cannot be instantiated * @throws IllegalAccessException - when constructor is not accessible * @throws InvocationTargetException - when constructor threw an exception * @throws NoSuchMethodException - when constructor does not exist */ @SuppressWarnings("unchecked") protected IGeneralBO loadBusinessObject(Class clazz, HierarchicalConfiguration cfg) throws IOException, ClassNotFoundException, InstantiationException, IllegalAccessException, InvocationTargetException, NoSuchMethodException { Serializable id = (Serializable)loadValue(cfg.configurationAt("refid(0)")); IDaoFactory factory = getDaoFactory(); if (factory != null) { IGeneralDAO> dao = factory.getDaoFor((Class>)clazz); if (dao != null) return dao.findById(id); } return null; } /** * Loads a collection from the XML tag. * @param clazz class to be loaded * @param cfg configuration of class * @return the loaded collection * @throws IOException - the I/O exception when operating on a filesystem * @throws ClassNotFoundException - when class could not be found * @throws InstantiationException - when object cannot be instantiated * @throws IllegalAccessException - when constructor is not accessible * @throws InvocationTargetException - when constructor threw an exception * @throws NoSuchMethodException - when constructor does not exist */ @SuppressWarnings("unchecked") protected Collection loadCollection(Class clazz, HierarchicalConfiguration cfg) throws IOException, ClassNotFoundException, InstantiationException, IllegalAccessException, InvocationTargetException, NoSuchMethodException { Collection collection = (Collection)clazz.getConstructor().newInstance(); for (HierarchicalConfiguration subConfig : cfg.configurationsAt("item")) { collection.add(loadValue(subConfig)); } return collection; } /** * Loads a map from the XML tag. * @param clazz class to be loaded * @param cfg configuration of class * @return the loaded map * @throws IOException - the I/O exception when operating on a filesystem * @throws ClassNotFoundException - when class could not be found * @throws InstantiationException - when object cannot be instantiated * @throws IllegalAccessException - when constructor is not accessible * @throws InvocationTargetException - when constructor threw an exception * @throws NoSuchMethodException - when constructor does not exist */ protected Map loadMap(Class clazz, HierarchicalConfiguration cfg) throws IOException, ClassNotFoundException, InstantiationException, IllegalAccessException, InvocationTargetException, NoSuchMethodException { Map map = new HashMap(); for (HierarchicalConfiguration subConfig : cfg.configurationsAt("item")) { Object key = loadValue(subConfig.configurationAt("key(0)")); Object value = loadValue(subConfig.configurationAt("value(0)")); map.put(key, value); } return map; } /** * Loads a {@link IBean} from the XML tag. * @param clazz class to be loaded * @param cfg configuration of class * @return the loaded bean * @throws IOException - the I/O exception when operating on a filesystem * @throws ClassNotFoundException - when class could not be found * @throws InstantiationException - when object cannot be instantiated * @throws IllegalAccessException - when constructor is not accessible * @throws InvocationTargetException - when constructor threw an exception * @throws NoSuchMethodException - when constructor does not exist */ protected IBean loadBean(Class clazz, HierarchicalConfiguration cfg) throws IOException, ClassNotFoundException, InstantiationException, IllegalAccessException, InvocationTargetException, NoSuchMethodException { IBean bean = (IBean)clazz.getConstructor().newInstance(); for (String name : getBeanPropertyNames(clazz)) { bean.set(name, loadValue(cfg.configurationAt(name+"(0)"))); } return bean; } /** * Loads a {@link Serializable} from the XML tag. * @param clazz class to be loaded * @param cfg configuration of class * @return the loaded object * @throws IOException - the I/O exception when operating on a filesystem * @throws ClassNotFoundException - when class could not be found * @throws InstantiationException - when object cannot be instantiated * @throws IllegalAccessException - when constructor is not accessible * @throws InvocationTargetException - when constructor threw an exception * @throws NoSuchMethodException - when constructor does not exist */ protected Object loadSerialized(Class clazz, HierarchicalConfiguration cfg) throws IOException, ClassNotFoundException, InstantiationException, IllegalAccessException, InvocationTargetException, NoSuchMethodException { String valueString = cfg.getString(""); // ? return unserialize(clazz.getName(), valueString); } /** * {@inheritDoc} */ @Override public void save(T bo, File specifier) throws IOException { // Find all relevant property names Collection propertyNames = new ArrayList(getPropertyNames(bo)); propertyNames.add("id"); Writer out = null; try { out = new OutputStreamWriter(new FileOutputStream(specifier), Charsets.toCharset(getEncoding())); out.write("\n"); out.write("\n"); for (String name : propertyNames) { Object value = PropertyUtils.getProperty(bo, name); writeValue(out, 1, value, name); } out.write(""); } catch (Exception e) { throw new IOException("Cannot load XML file", e); } finally { if (out != null) { out.flush(); out.close(); } } } /** * Internal helper to write a value to the XML stream. * @param out the stream * @param indent the indentation to be used * @param value the value to be written * @param tagName the tag name to be used * @throws IOException - the I/O exception when operating on a filesystem * @throws ClassNotFoundException - when class could not be found * @throws InstantiationException - when object cannot be instantiated * @throws IllegalAccessException - when constructor is not accessible * @throws InvocationTargetException - when constructor threw an exception * @throws NoSuchMethodException - when constructor does not exist */ protected void writeValue(Writer out, int indent, Object value, String tagName) throws IOException, ClassNotFoundException, InstantiationException, IllegalAccessException, InvocationTargetException, NoSuchMethodException { StringBuilder foo = new StringBuilder(); for (int i=0; i"); if (value instanceof IGeneralBO) { out.write("\n"); writeValue(out, indent+1, ((IGeneralBO) value).getId(), "refid"); out.write(indentS); } else if (value instanceof Collection) { out.write("\n"); // We need to split this up for (Object o : (Collection)value) { writeValue(out, indent+1, o, "item"); } out.write(indentS); } else if (value instanceof Map) { out.write("\n"); // We need to split this up for (Map.Entry entry : ((Map)value).entrySet()) { out.write(indentS+" \n"); writeValue(out, indent+2, entry.getKey(), "key"); writeValue(out, indent+2, entry.getValue(), "value"); out.write(indentS+" \n"); } out.write(indentS); } else if (value instanceof IBean) { out.write("\n"); for (String name : getBeanPropertyNames(value)) { Object v = PropertyUtils.getProperty(value, name); writeValue(out, indent+1, v, name); } out.write(indentS); } else { String s = serialize(value); if (s != null) { if (isXmlCompatible(s)) { out.write(s); } else { out.write(""); } } else { out.write(""); } } out.write("\n"); } else { out.write(indentS+"<"+tagName+"/>\n"); } } /** * Returns whether the string is XML compatible. * @param s string to be evaluated * @return true when string can be used as content in XML */ protected boolean isXmlCompatible(String s) { return (s.indexOf("&") < 0) && (s.indexOf(">") < 0) && (s.indexOf("<") < 0); } /** * {@inheritDoc} */ @Override public void refresh(T bo, File specifier) throws IOException { load(bo, bo.getId(), specifier); } /** * {@inheritDoc} */ @SuppressWarnings("unchecked") @Override public Map getList(Collection specifiers) throws IOException { Map rc = new HashMap(); // Read the ID of each file for (File f : specifiers) { try { XMLConfiguration cfg = ConfigurationUtils.getXmlConfiguration(f); // Removed with 2.0: cfg.setListDelimiter((char)0); try { HierarchicalConfiguration subConfig = cfg.configurationAt("id(0)"); K id = (K)loadValue(subConfig); rc.put(id, f); } catch (IllegalArgumentException e) { // Ignore } } catch (Exception e) { throw new IOException("Cannot load XML file", e); } } return rc; } }