/*
* 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.ObjectParsing;
import de.timefinder.core.io.text.Parsing;
import de.timefinder.core.util.ReaderInputStream;
import de.timefinder.data.DBInterface;
import de.timefinder.data.DataPool;
import de.timefinder.data.Event;
import de.timefinder.data.Feature;
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.set.BitRasterImpl;
import de.timefinder.data.set.WeekRasterImpl;
import de.timefinder.data.util.Helper;
import java.io.InputStream;
import java.io.Reader;
import java.lang.reflect.Method;
import java.util.ArrayList;
import java.util.BitSet;
import java.util.Collection;
import java.util.Iterator;
import java.util.List;
import java.util.Map;
import java.util.Map.Entry;
import javolution.util.FastMap;
import org.dom4j.Document;
import org.dom4j.Element;
import org.dom4j.io.SAXReader;
/**
*
* @author Peter Karich, peat_hal 'at' users 'dot' sourceforge 'dot' net
*/
public class XmlImport extends AbstractXmlHelper {
private InputStream reader;
private ObjectParsing objectParsing = new ObjectParsing();
private Map<Class, Map<Long, DBInterface>> simpleDaos = FastMap.newInstance();
XmlImport(DataPool pool, DataPoolSettings settings, Reader reader) {
this(pool, settings, new ReaderInputStream(reader));
}
public XmlImport(DataPool pool, DataPoolSettings settings, InputStream reader) {
super(pool, settings);
if (reader == null)
throw new NullPointerException("InputStream cannot be null!");
this.reader = reader;
for (Dao<? extends DBInterface> dao : pool.getDaos()) {
// should we use existing values?? (Map<Long, DBInterface>) dao.getMap()
simpleDaos.put(dao.getType(), new FastMap());
}
}
public void doWork() {
try {
SAXReader saxReader = new SAXReader();
Document document = saxReader.read(reader);
List list = document.getRootElement().elements();
Iterator iter = list.iterator();
while (iter.hasNext()) {
Element element = (Element) iter.next();
if ("settings".equals(element.getName())) {
readElements(element, settings);
} else if ("objects".equals(element.getName())) {
Class clazz = Class.forName(element.attributeValue("class"));
readGeneric(clazz, element);
}
}
} catch (Exception ex) {
throw new RuntimeException(ex);
}
fillPoolFromSimpleDaos();
}
private void fillPoolFromSimpleDaos() {
for (Entry<Class, Map<Long, DBInterface>> entry : simpleDaos.entrySet()) {
Dao<DBInterface> dao = pool.getDao(entry.getKey());
if (dao == null)
throw new IllegalStateException("no dao in pool found for class:" + entry.getKey());
dao.attachAll(entry.getValue().values());
}
}
private void readGeneric(Class<DBInterface> clazz, Element rootElement) {
for (Object objEl : rootElement.elements()) {
Element objElement = (Element) objEl;
if (!"object".equals(objElement.getName()))
throw new UnsupportedOperationException("sub element should be object");
long id = getId(objElement);
Object obj = getObject(id, clazz);
readElements(objElement, obj);
}
}
private Element readElements(Element rootElement, Object object) {
Map<String, Method> methods = Helper.findSetterMethods(object.getClass());
for (Object objectEl : rootElement.elements()) {
Element element = (Element) objectEl;
Method m = methods.get(element.getName());
if (m == null)
throw new NullPointerException("Cannot find setter for element:" + element.getName());
try {
Class setterType = m.getParameterTypes()[0];
Object result = getResult(object, setterType, element);
m.invoke(object, result);
} catch (Exception ex) {
throw new RuntimeException("Exception while parsing ("
+ element.getName() + ") to "
+ object.getClass().getName() + " " + object, ex);
}
}
return rootElement;
}
private Long getId(Element objElement) {
try {
return Long.parseLong(objElement.attributeValue("id"));
} catch (Exception ex) {
throw new UnsupportedOperationException("id of element mustn't be null!"
+ objElement.getUniquePath(), ex);
}
}
private Long getRef(Element objElement) {
try {
return Long.parseLong(objElement.attributeValue("ref"));
} catch (Exception ex) {
throw new UnsupportedOperationException("ref of element mustn't be null!"
+ objElement.getUniquePath(), ex);
}
}
private <T extends DBInterface> T getObject(Long id, Class<T> clazz) {
Map<Long, DBInterface> simpleDao = simpleDaos.get(clazz);
DBInterface obj = simpleDao.get(id);
if (obj == null) {
try {
obj = clazz.newInstance();
// obj.set(id) will be done in attach of the 'real dao'
simpleDao.put(id, obj);
} catch (Exception ex) {
throw new IllegalStateException("Cannot create object of class:" + clazz, ex);
}
}
return (T) obj;
}
public <T> T getResult(Object obj, Class<T> propertyClass, Element element) {
Parsing parsing = objectParsing.getParsing(propertyClass);
if (parsing == null) {
if (Person.class.isAssignableFrom(obj.getClass())) {
if ("constraints".equals(element.getName())) {
return (T) readConstraints(element);
} else if ("eventsMap".equals(element.getName())) {
Map map = FastMap.newInstance();
for (Object evEl : element.elements()) {
Element evElement = (Element) evEl;
long ref = getRef(evElement);
Role role = Role.valueOf(evElement.attributeValue("role"));
map.put(getObject(ref, Event.class), role);
}
return (T) map;
}
} else if (Event.class.isAssignableFrom(obj.getClass())) {
if ("constraints".equals(element.getName())) {
return (T) readConstraints(element);
} else if ("features".equals(element.getName())) {
return (T) readCollection(element, Feature.class);
}
// location already parsed via location->events
// persons already parsed via persons->events
} else if (Feature.class.isAssignableFrom(obj.getClass())) {
if ("constraints".equals(element.getName())) {
return (T) readConstraints(element);
}
} else if (Location.class.isAssignableFrom(obj.getClass())) {
if ("constraints".equals(element.getName())) {
return (T) readConstraints(element);
} else if ("events".equals(element.getName())) {
return (T) readCollection(element, Event.class);
} else if ("features".equals(element.getName())) {
return (T) readCollection(element, Feature.class);
}
}
throw new UnsupportedOperationException("No parsing found for property class "
+ propertyClass + " and object " + obj);
} else
return (T) parsing.parse(element.getText());
}
public Collection readCollection(Element eventsElement, Class clazz) {
List res = new ArrayList();
for (Object obj : eventsElement.elements()) {
Element element = (Element) obj;
long ref = getRef(element);
res.add(getObject(ref, clazz));
}
return res;
}
public Collection readConstraints(Element constrElement) {
List<Constraint> res = new ArrayList();
for (Object obj : constrElement.elements()) {
Element element = (Element) obj;
Constraint constraint = null;
if (RasterConstraint.class.getSimpleName().equals(element.getName())) {
BitSet bs = getResult(obj, BitSet.class, element);
int length = Integer.parseInt(element.attributeValue("length"));
constraint = new RasterConstraint(new WeekRasterImpl(
new BitRasterImpl(bs, length)));
} else if (EventOrderConstraint.class.getSimpleName().equals(element.getName())) {
UnsupportedOperationException excFirstEl = new UnsupportedOperationException(
"First element in eventOrderConstraint must be the event!" + element.getPath());
for (Object eocObj : element.elements()) {
Element eocEl = (Element) eocObj;
if ("event".equals(eocEl.getName())) {
Event ev = getObject(getRef(eocEl), Event.class);
constraint = new EventOrderConstraint(ev);
} else if ("beforeEvent".equals(eocEl.getName())) {
if (constraint == null)
throw excFirstEl;
Event ev = getObject(getRef(eocEl), Event.class);
((EventOrderConstraint) constraint).addBefore(ev);
} else if ("followsEvent".equals(eocEl.getName())) {
if (constraint == null)
throw excFirstEl;
Event ev = getObject(getRef(eocEl), Event.class);
((EventOrderConstraint) constraint).addFollow(ev);
} else
throw new UnsupportedOperationException("Element not supported in eventOrderConstraint:"
+ eocEl.getPath());
}
} else if (PersonITCRasterConstraint.class.getSimpleName().equals(element.getName())) {
Person p = getObject(getRef(element.element("person")), Person.class);
constraint = new PersonITCRasterConstraint(p, settings);
} else if (MinGapsConstraint.class.getSimpleName().equals(element.getName())) {
constraint = new MinGapsConstraint(settings,
readCollection(element, Event.class));
((MinGapsConstraint) constraint).setCountEarly(
Boolean.parseBoolean(element.attributeValue("countEarly")));
} else if (DifferentDayConstraint.class.getSimpleName().equals(element.getName())) {
constraint = new DifferentDayConstraint(settings,
readCollection(element, Event.class));
} else
throw new UnsupportedOperationException("Constraint not supported:" + element.getName());
if (constraint == null)
throw new IllegalStateException("Couldn't find a constraint in path:" + element.getPath());
float weight = Float.parseFloat(element.attributeValue("weight"));
constraint.setWeight(weight);
res.add(constraint);
}
return res;
}
}