/*
* Copyright 2010 Peter Karich, peat_hal 'at' users 'dot' sourceforge 'dot' net.
*
* Licensed under the Apache License, Version 2.0 (the "License");
* you may not use this file except in compliance with the License.
* You may obtain a copy of the License at
*
* http://www.apache.org/licenses/LICENSE-2.0
*
* Unless required by applicable law or agreed to in writing, software
* distributed under the License is distributed on an "AS IS" BASIS,
* WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
* See the License for the specific language governing permissions and
* limitations under the License.
* under the License.
*/
package de.timefinder.core.io.xml;
import de.timefinder.algo.constraint.DifferentDayConstraint;
import de.timefinder.algo.constraint.EventOrderConstraint;
import de.timefinder.algo.constraint.MinGapsConstraint;
import de.timefinder.algo.constraint.PersonITCRasterConstraint;
import de.timefinder.algo.constraint.RasterConstraint;
import de.timefinder.core.io.text.ObjectWriting;
import de.timefinder.core.io.text.Writing;
import de.timefinder.core.util.WriterOutputStream;
import de.timefinder.data.ConstraintObject;
import de.timefinder.data.DBInterface;
import de.timefinder.data.DataPool;
import de.timefinder.data.Event;
import de.timefinder.data.Feature;
import de.timefinder.data.FeatureContainer;
import de.timefinder.data.Location;
import de.timefinder.data.Person;
import de.timefinder.data.Role;
import de.timefinder.data.access.Dao;
import de.timefinder.data.algo.Constraint;
import de.timefinder.data.algo.DataPoolSettings;
import de.timefinder.data.util.Helper;
import java.io.OutputStream;
import java.io.Writer;
import java.lang.reflect.Method;
import java.util.BitSet;
import java.util.Map;
import java.util.Map.Entry;
import org.dom4j.Document;
import org.dom4j.DocumentHelper;
import org.dom4j.Element;
import org.dom4j.io.OutputFormat;
import org.dom4j.io.XMLWriter;
/**
*
* @author Peter Karich, peat_hal 'at' users 'dot' sourceforge 'dot' net
*/
public class XmlExport extends AbstractXmlHelper {
private OutputStream writer;
private ObjectWriting objectWriting = new ObjectWriting();
XmlExport(DataPool pool, DataPoolSettings settings, Writer writer) {
this(pool, settings, new WriterOutputStream(writer));
}
public XmlExport(DataPool pool, DataPoolSettings settings, OutputStream writer) {
super(pool, settings);
if (writer == null)
throw new NullPointerException("OutputStream cannot be null!");
this.writer = writer;
}
public void doWork() {
try {
XMLWriter output = new XMLWriter(writer, new OutputFormat(" ", true));
output.write(createDocument());
output.close();
} catch (Exception ex) {
throw new RuntimeException(ex);
}
}
public Document createDocument() {
Document document = DocumentHelper.createDocument();
Element timefinderEl = document.addElement("timefinder"/*, "xmlnsValue"*/);
timefinderEl.addAttribute("version", "4.1");
writeElement(settings, timefinderEl);
for (Dao<? extends DBInterface> dao : pool.getDaos()) {
Element el = timefinderEl.addElement("objects");
el.addAttribute("class", dao.getType().getName());
for (DBInterface object : dao.getAll()) {
if (object == null)
throw new NullPointerException("You should persist null objects!");
if (Person.class.isAssignableFrom(dao.getType())) {
Element tmp = writeGeneric((Person) object, el, dao.getType());
writeElement((Person) object, tmp);
} else if (Event.class.isAssignableFrom(dao.getType())) {
Element tmp = writeGeneric((Event) object, el, dao.getType());
writeElement((Event) object, tmp);
} else if (Feature.class.isAssignableFrom(dao.getType())) {
writeGeneric((Feature) object, el, dao.getType());
} else if (Location.class.isAssignableFrom(dao.getType())) {
Element tmp = writeGeneric((Location) object, el, dao.getType());
writeElement((Location) object, tmp);
} else
throw new UnsupportedOperationException("Only person, feature, "
+ "event and location are supported, but was:" + object.getClass());
}
}
return document;
}
private void writeElement(DataPoolSettings settings, Element timefinderEl) {
Element settingsEl = timefinderEl.addElement("settings");
writeElements(settingsEl, settings, SETTINGS_PROPERTIES);
}
private void writeElement(Person person, Element element) {
writeElements(element, person, PERSON_PROPERTIES);
if (person.getEvents().size() == 0)
return;
Element newElement = element.addElement("eventsMap");
for (Entry<Event, Role> entry : person.getEventsMap().entrySet()) {
newElement.addElement("event").
addAttribute("ref", "" + entry.getKey().getId()).
addAttribute("role", entry.getValue().toString());
}
}
private void writeElement(Event event, Element element) {
writeElements(element, event, EVENT_PROPERTIES);
writeFeatures(element, event);
// write persons -> already done in person->events
// write location -> already done in location->events
}
private void writeElement(Location location, Element element) {
writeElements(element, location, LOCATION_PROPERTIES);
writeFeatures(element, location);
if (location.getEvents().size() == 0)
return;
Element newElement = element.addElement("events");
for (Event ev : location.getEvents()) {
newElement.addElement("event").addAttribute("ref", "" + ev.getId());
}
}
private Element writeGeneric(ConstraintObject obj, Element element, Class concreteClass) {
element = element.addElement("object");
if (concreteClass != obj.getClass()) {
element.addAttribute("class", obj.getClass().getName());
}
element.addAttribute("id", "" + obj.getId());
element.addElement("name").setText(obj.getName());
writeConstraints(element, obj);
return element;
}
public Element writeConstraints(Element element, ConstraintObject co) {
if (co.getConstraints().size() == 0)
return element;
Element newElement = element.addElement("constraints");
for (Constraint constr : co.getConstraints()) {
Element el = newElement.addElement(constr.getClass().getSimpleName());
el.addAttribute("weight", "" + constr.getWeight());
if (constr instanceof RasterConstraint) {
RasterConstraint rc = (RasterConstraint) constr;
Writing w = getWriting(BitSet.class);
el.setText(w.toString(rc.getRaster().getForbidden()));
el.addAttribute("length", "" + rc.getRaster().getForbidden().getLength());
} else if (constr instanceof EventOrderConstraint) {
EventOrderConstraint eoc = (EventOrderConstraint) constr;
el.addElement("event").addAttribute("ref", "" + eoc.getEvent().getId());
for (Event ev : eoc.getBefores()) {
el.addElement("beforeEvent").addAttribute("ref", "" + ev.getId());
}
for (Event ev : eoc.getFollows()) {
el.addElement("followsEvent").addAttribute("ref", "" + ev.getId());
}
} else if (constr instanceof PersonITCRasterConstraint) {
PersonITCRasterConstraint pc = (PersonITCRasterConstraint) constr;
el.addElement("person").addAttribute("ref", "" + pc.getPerson().getId());
} else if (constr instanceof MinGapsConstraint) {
MinGapsConstraint mgc = (MinGapsConstraint) constr;
el.addAttribute("countEarly", "" + mgc.isCountEarly());
for (Event ev : mgc.getEvents()) {
el.addElement("event").addAttribute("ref", "" + ev.getId());
}
} else if (constr instanceof DifferentDayConstraint) {
DifferentDayConstraint ddc = (DifferentDayConstraint) constr;
for (Event ev : ddc.getEvents()) {
el.addElement("event").addAttribute("ref", "" + ev.getId());
}
} else
throw new UnsupportedOperationException("Constraint export/import not yet implemented for class " + constr.getClass().getName());
}
return element;
}
public Element writeFeatures(Element element, FeatureContainer fc) {
if (fc.getFeatures().size() == 0)
return element;
Element newElement = element.addElement("features");
for (Feature feature : fc.getFeatures()) {
newElement.addElement("feature").addAttribute("ref", "" + feature.getId());
}
return element;
}
private Element writeElements(Element element, Object object, String... properties) {
Map<String, Method> methods = Helper.findGetterMethods(object.getClass());
for (String property : properties) {
Method m = methods.get(property);
try {
if (m == null)
throw new IllegalStateException("No getter found for " + property);
Object result = m.invoke(object);
if (result != null) {
Writing writing = getWriting(m.getReturnType());
if (writing == null)
throw new UnsupportedOperationException("No writing found for " + m.getReturnType());
element.addElement(property).setText(writing.toString(result));
}
} catch (Exception ex) {
throw new RuntimeException("exception while writing "
+ property + " from "
+ object.getClass().getName() + " " + object, ex);
}
}
return element;
}
public Writing getWriting(Class clazz) {
return objectWriting.getWriting(clazz);
}
}