/*******************************************************************************
* Copyright (c) 2014 Open Door Logistics (www.opendoorlogistics.com)
* All rights reserved. This program and the accompanying materials
* are made available under the terms of the GNU Lesser Public License v3
* which accompanies this distribution, and is available at http://www.gnu.org/licenses/lgpl.txt
******************************************************************************/
package com.opendoorlogistics.core.scripts.io;
import java.util.ArrayList;
import java.util.List;
import java.util.Map;
import java.util.TreeMap;
import javax.xml.bind.Binder;
import javax.xml.bind.JAXBContext;
import javax.xml.bind.annotation.XmlRootElement;
import javax.xml.parsers.DocumentBuilder;
import javax.xml.parsers.DocumentBuilderFactory;
import org.w3c.dom.Document;
import org.w3c.dom.Node;
import com.opendoorlogistics.core.scripts.elements.AdaptedTableConfig;
import com.opendoorlogistics.core.scripts.elements.AdapterConfig;
import com.opendoorlogistics.core.scripts.elements.ComponentConfig;
import com.opendoorlogistics.core.scripts.elements.OutputConfig;
import com.opendoorlogistics.core.scripts.elements.Script;
import com.opendoorlogistics.core.scripts.elements.ScriptElementType;
import com.opendoorlogistics.core.tables.memory.ODLTableDefinitionImpl;
import com.opendoorlogistics.core.utils.strings.Strings;
final public class XMLConversionHandlerImpl implements XMLConversionHandler {
private final Binder<Node> genericBinder;
private final ScriptElementType myType;
public XMLConversionHandlerImpl(ScriptElementType type) {
this.myType = type;
try {
JAXBContext context = JAXBContext.newInstance(type.getScriptElementClass());
genericBinder = context.createBinder();
} catch (Throwable e) {
throw new RuntimeException(e);
}
}
@Override
public Document toXML(Object item) {
if(myType.getScriptElementClass().isInstance(item)==false){
throw new RuntimeException();
}
try {
DocumentBuilderFactory docFactory = DocumentBuilderFactory.newInstance();
DocumentBuilder docBuilder = docFactory.newDocumentBuilder();
Document doc = docBuilder.newDocument();
genericBinder.marshal(item, doc);
return doc;
} catch (Throwable e) {
throw new RuntimeException(e);
}
}
@SuppressWarnings("unchecked")
@Override
public List<Object> fromXML(Document document) {
ArrayList<Object> ret = new ArrayList<>();
// try unmarshalling at all different supported script items
if(document.getFirstChild()!=null){
String typeName = document.getFirstChild().getNodeName();
for (Map.Entry<ScriptElementType, Binder<Node>> entry : binderByType.entrySet()) {
try {
ScriptElementType elemType = entry.getKey();
// check names match before attempting unmarshalling (will cut down on the number of exceptions)
XmlRootElement rootElement = elemType.getScriptElementClass().getAnnotation(XmlRootElement.class);
if(rootElement!=null && Strings.equalsStd(rootElement.name(), typeName)){
Object item = entry.getValue().unmarshal(document);
if (item != null) {
processUnmarshalled(elemType, item, ret);
break;
}
}
} catch (Throwable e) {
// non-fatal error as may not be correct object type in clipboard...
}
}
}
return ret;
}
@SuppressWarnings("unchecked")
private void processUnmarshalled(ScriptElementType unmarshalledType, Object item, ArrayList<Object> ret) {
if(item.getClass() == myType.getScriptElementClass()){
ret.add(item);
}else{
switch (unmarshalledType) {
case SCRIPT:{
Script script = (Script)item;
// recurse all
for(AdapterConfig child:script.getAdapters()){
processUnmarshalled(ScriptElementType.ADAPTER, child, ret);
}
for(ComponentConfig child:script.getInstructions()){
processUnmarshalled(ScriptElementType.INSTRUCTION, child, ret);
}
for(OutputConfig child:script.getOutputs()){
processUnmarshalled(ScriptElementType.OUTPUT, child, ret);
}
break;
}
case ADAPTER:{
AdapterConfig adapterConfig = (AdapterConfig)item;
// recurse
for(AdaptedTableConfig table:adapterConfig.getTables()){
processUnmarshalled(ScriptElementType.ADAPTED_TABLE, table, ret);
}
break;
}
case INSTRUCTION:
break;
case ADAPTED_TABLE:{
AdaptedTableConfig adaptedTable = (AdaptedTableConfig)item;
if(myType == ScriptElementType.ADAPTER){
// create wrapper for the table
AdapterConfig newAdapter = new AdapterConfig("New adapter");
newAdapter.getTables().add(adaptedTable);
ret.add(newAdapter);
}
else if(myType == ScriptElementType.TABLE_DEFINITION){
// convert to adapted table to a table
ODLTableDefinitionImpl tableDfn = new ODLTableDefinitionImpl(-1, adaptedTable.getName());
adaptedTable.createOutputDefinition(tableDfn);
}
break;
}
case TABLE_DEFINITION:
break;
default:
break;
}
}
}
private static TreeMap<ScriptElementType, Binder<Node>> binderByType = new TreeMap<>();
static {
for (ScriptElementType type : ScriptElementType.values()) {
// skip instruction for the moment as this requires special marshalling..
if (type == ScriptElementType.INSTRUCTION) {
continue;
}
try {
JAXBContext context = JAXBContext.newInstance(type.getScriptElementClass());
Binder<Node> binder = context.createBinder();
if (binder != null) {
binderByType.put(type, binder);
}
} catch (Throwable e) {
// TODO: handle exception
}
}
}
}