package rtt.core.manager.data;
import java.io.InputStream;
import java.io.OutputStream;
import javax.xml.bind.JAXBContext;
import javax.xml.bind.JAXBElement;
import javax.xml.bind.Marshaller;
import javax.xml.bind.Unmarshaller;
import javax.xml.transform.Result;
import javax.xml.transform.Source;
import javax.xml.transform.stream.StreamResult;
import javax.xml.transform.stream.StreamSource;
import rtt.core.exceptions.RTTException;
import rtt.core.exceptions.RTTException.Type;
import rtt.core.loader.ArchiveLoader;
import rtt.core.loader.fetching.IFileFetching;
import rtt.core.utils.RTTLogging;
/**
* This abstract data manager provides a basic set of operations for loading and
* storing informations in the rtt archive.
*
* @author Christian Oelsner <C.Oelsner@gmail.com>
*
* @param <T>
* The main type of data used within this data manager.
*/
public abstract class AbstractDataManager<T> {
/**
* The current data of the manager.
*/
protected T data;
/**
* The current {@link ArchiveLoader} of the manager.
*/
protected ArchiveLoader loader;
/**
* The current {@link IFileFetching} of the manager
*/
protected IFileFetching strategy;
/**
* Creates a data manager with an archive loader and a strategy for loading
* a file, which contains all data of this manager.
*
* @param loader
* the {@link ArchiveLoader}
* @param strategy
* the {@link IFileFetching}
* @see AbstractDataManager
* @see ArchiveLoader
* @see IFileFetching
*/
public AbstractDataManager(ArchiveLoader loader,
IFileFetching strategy) {
this.loader = loader;
setFetchingStrategy(strategy);
data = getEmptyData();
}
/**
* Creates a data manager without a {@link IFileFetching}. This
* strategy must be set before using this manager.
*
* @param loader
* the {@link ArchiveLoader}
* @see AbstractDataManager
* @see ArchiveLoader
* @see IFileFetching
* @see #setFetchingStrategy(IFileFetching)
*/
public AbstractDataManager(ArchiveLoader loader) {
this(loader, null);
}
/**
* Sets the strategy for loading a file, which contains all data of this
* manager.
*
* @param strategy
* the {@link IFileFetching}
* @see AbstractDataManager
* @see IFileFetching
*/
protected void setFetchingStrategy(IFileFetching strategy) {
this.strategy = strategy;
}
protected InputStream getInputStream() {
InputStream result = null;
if (strategy != null) {
result = loader.getInputStream(strategy.getFileName(), strategy.getFolders());
}
return result;
}
protected OutputStream getOutputStream() {
OutputStream result = null;
if (strategy != null) {
result = loader.getOutputStream(strategy.getFileName(), strategy.getFolders());
}
return result;
}
/**
* Returns the data of the file given by the current
* {@link IFileFetching}. The conversion of the data will be made
* through JAXB.
*
* @param clazz
* the class of the data
* @return a object containing the data
* @throws Exception
* thrown, if any errors occur during loading
* @see AbstractDataManager
* @see IFileFetching
* @see #unmarshall(Class, InputStream)
* @see Unmarshaller#unmarshal(Source, Class)
*/
@SuppressWarnings("unchecked")
protected T unmarshall(Class<T> clazz) throws Exception {
return (T) unmarshall(clazz, getInputStream());
}
/**
* Returns the data of the given {@link InputStream}. The conversion of the
* data will be made through JAXB.
*
* @param clazz
* the class of the data
* @param inputStream
* the {@link InputStream} of the data
* @return a object containing the data
* @throws Exception
* thrown, if any errors occur during loading
* @see AbstractDataManager
* @see InputStream
* @see Unmarshaller#unmarshal(Source, Class)
*/
protected Object unmarshall(Class<?> clazz, InputStream inputStream)
throws Exception {
// create new context and an unmarshaller
JAXBContext jc = JAXBContext.newInstance(clazz);
Unmarshaller unmarshaller = jc.createUnmarshaller();
// unmarshall data from stream source
Source source = new StreamSource(inputStream);
JAXBElement<?> element = unmarshaller.unmarshal(source, clazz);
// return value of jaxb element
return element.getValue();
}
/**
* Saves the given data to the file given by the current
* {@link IFileFetching}. The conversion of the data will be made
* through JAXB.
*
* @param clazz
* the class of the data
* @param data
* the data, which should be saved
* @see AbstractDataManager
* @see IFileFetching
* @see #marshall(Class, Object, OutputStream)
* @see Marshaller#marshal(Object, Result)
*/
protected void marshall(Class<T> clazz, T data) {
marshall(clazz, data, getOutputStream());
}
/**
* Saves the given data to the given {@link OutputStream}.
*
* @param clazz
* the class of the data
* @param data
* the data, which should be saved
* @param outputStream
* the {@link OutputStream}
* @see AbstractDataManager
* @see OutputStream
* @see Marshaller#marshal(Object, Result)
*/
protected void marshall(Class<?> clazz, Object data,
OutputStream outputStream) {
if (outputStream != null) {
try {
// create new context and marshaller
JAXBContext jc = JAXBContext.newInstance(clazz);
Marshaller marshaller = jc.createMarshaller();
marshaller.setProperty(Marshaller.JAXB_FORMATTED_OUTPUT, true);
// TODO add CDATA support for values
// XMLOutputFactory xof = XMLOutputFactory.newInstance();
// XMLStreamWriter streamWriter = xof.createXMLStreamWriter(outputStream, "UTF-8");
// create stream result and marshall data
Result result = new StreamResult(outputStream);
marshaller.marshal(data, result);
outputStream.close();
} catch (Exception e) {
RTTLogging.error("Could not marshall output data", e);
}
}
}
/**
* Gets data from manager. This function will return empty data, but not
* {@code null}, if called before {@link #load()}.
*
* @return the data of the manager
* @see AbstractDataManager
* @see #load()
*/
public T getData() {
return data;
}
/**
* Loads all data into the manager.
*
* @throws RTTException
* thrown, if any error occur during loading
* @see AbstractDataManager
*/
public final void load() throws RTTException {
try {
data = doLoad();
} catch (Exception e) {
throw new RTTException(Type.DATA_NOT_FOUND, "Could not load data for '"
+ this.getClass() + "'");
}
}
/**
* Saves all data from the manager.
*
* @throws RTTException
* thrown, if any error occur during saving
* @see AbstractDataManager
*/
public final void save() throws RTTException {
try {
doSave(data);
} catch (Exception e) {
throw new RTTException(Type.DATA_NOT_FOUND, "Could not load data for '"
+ this.getClass() + "'");
}
}
/**
* Returns an empty data object.
* @return an empty data object.
*/
protected abstract T getEmptyData();
/**
* Loads the data from archive.
*
* @return the loaded data
* @throws Exception
* thrown, if any error occur during loading
*/
protected abstract T doLoad() throws Exception;
/**
* Saves the data to the archive.
*
* @param data
* the data, which should be saved
* @throws Exception
* thrown, if any error occur during saving
*/
protected abstract void doSave(T data) throws Exception;
}