/*******************************************************************************
* Copyright (c) 2007-2011, G. Weirich and Elexis
* All rights reserved. This program and the accompanying materials
* are made available under the terms of the Eclipse Public License v1.0
* which accompanies this distribution, and is available at
* http://www.eclipse.org/legal/epl-v10.html
*
* Contributors:
* G. Weirich - initial API and implementation
******************************************************************************/
package ch.elexis.data;
import java.io.IOException;
import java.io.StringReader;
import java.lang.reflect.Constructor;
import java.util.HashMap;
import java.util.List;
import java.util.Stack;
import java.util.Vector;
import javax.xml.parsers.DocumentBuilder;
import javax.xml.parsers.DocumentBuilderFactory;
import javax.xml.parsers.ParserConfigurationException;
import org.jdom.IllegalAddException;
import org.w3c.dom.Document;
import org.w3c.dom.Node;
import org.w3c.dom.NodeList;
import org.xml.sax.InputSource;
import org.xml.sax.SAXException;
import ch.rgw.tools.Log;
/**
* This class helps exporting/importing of database data.<br>
* Export is transforming database values into an xml sheet. <br>
* Import is reading an xml sheet and updating the database.<br>
* XML structure looks like: <br>
* <ARTIKEL javaclass="ch.elexis.data.Artikel"> <br>
* <UID field="ID">r895a3a395be62a6e19c1103</UID> <br>
* <EAN>7680573770024</EAN> <br>
* ... other field values <br>
* </ARTIKEL> <br>
*
*/
public class XML2Database {
// TODO -> to delete?
protected static Log log = Log.get(XML2Database.class.getName());
private final static String HEADER = "<?xml version=\"1.0\" encoding=\"UTF-8\"?>\n";
private final static String LIST_TAG = "DATALIST";
private final static String UID_TAG = "UID";
private final static String UID_VERSION_ATTRIBUTE = "version";
private final static String CLASS_ATTRIBUTE = "javaclass";
private final static String TEXT_TAG = "#text"; // Tags to ignore
@SuppressWarnings("unchecked")
private HashMap<Class, HashMap<String, List<String>>> cache =
new HashMap<Class, HashMap<String, List<String>>>();
private static class DataField {
final String name;
final String value;
public DataField(String name, String value){
super();
this.name = name;
this.value = value;
}
}
private static class XMLObject {
private PersistentObject object = null;
private StringBuffer buffer = new StringBuffer();
private final Stack<String> stack = new Stack<String>();
private boolean elementOpened = false;
private XMLObject(final PersistentObject object){
this.object = object;
}
private void openElement(final String name){
if (elementOpened) {
writeCloseSign(true);
}
addTab();
buffer.append("<" + name);
elementOpened = true;
stack.push(name);
}
private void writeCloseSign(boolean newLine){
buffer.append(">");
if (newLine) {
buffer.append("\n");
}
elementOpened = false;
}
private void addTab(){
for (int i = 0; i < stack.size(); i++) {
buffer.append("\t");
}
}
private void closeElement(){
if (elementOpened) {
writeCloseSign(true);
stack.pop();
} else {
if (!stack.isEmpty()) {
String name = stack.pop();
buffer.append("</" + name + ">\n");
}
}
}
private void addAttribute(final String attribute, final String value){
if (elementOpened) {
buffer.append(" " + attribute + "=\"" + value + "\"");
} else {
throw new IllegalAddException("");
}
}
private void addValue(final String value){
if (elementOpened) {
writeCloseSign(false);
}
buffer.append(value);
}
/**
* Exports an array
*
* @return
*/
private String exportData(){
String[] fieldList = object.getExportFields();
String[] resultList = new String[fieldList.length];
object.get(fieldList, resultList);
openElement(object.getTableName());
addAttribute(CLASS_ATTRIBUTE, object.getClass().getName());
// Primary key
openElement(UID_TAG);
addAttribute(UID_VERSION_ATTRIBUTE, object.getExportUIDVersion());
addValue(object.getExportUIDValue());
closeElement();
// Fields
for (int i = 0; i < fieldList.length; i++) {
String name = fieldList[i];
String value = resultList[i];
openElement(name);
addValue(value);
closeElement();
}
closeElement();
return buffer.toString();
}
}
/********************************************************************************
* PRIVATE METHODS
*/
@SuppressWarnings("unchecked")
private List<String> getValues(Class javaClass, String uidValue){
HashMap<String, List<String>> map = cache.get(javaClass);
if (map == null) {
map = new HashMap<String, List<String>>();
cache.put(javaClass, map);
Query<PersistentObject> query = new Query<PersistentObject>(javaClass);
List<PersistentObject> poList = query.execute();
for (PersistentObject po : poList) {
String value = po.getExportUIDValue();
List<String> idList = map.get(value);
if (idList == null) {
idList = new Vector<String>();
map.put(value, idList);
}
idList.add(po.getId());
}
}
return map.get(uidValue);
}
/**
* Import
*
* @param tableNode
* @param overwrite
* @throws ClassNotFoundException
*/
@SuppressWarnings("unchecked")
private void importData(final Node tableNode, final boolean overwrite){
try {
String className =
tableNode.getAttributes().getNamedItem(CLASS_ATTRIBUTE).getNodeValue();
Class javaClass = Class.forName(className);
String uidValue = null;
String uidVersion = "";
// Read fields
List<DataField> fieldList = new Vector<DataField>();
NodeList nodeList = tableNode.getChildNodes();
for (int index = 0; index < nodeList.getLength(); index++) {
Node fieldNode = nodeList.item(index);
if (!TEXT_TAG.equals(fieldNode.getNodeName())) {
if (UID_TAG.equals(fieldNode.getNodeName())) {
uidValue = fieldNode.getTextContent();
if (fieldNode.getAttributes().getNamedItem(UID_VERSION_ATTRIBUTE) == null) {
throw new IllegalArgumentException("No UID export version found!");
}
uidVersion =
fieldNode.getAttributes().getNamedItem(UID_VERSION_ATTRIBUTE)
.getNodeValue();
} else {
fieldList.add(new DataField(fieldNode.getNodeName(), fieldNode
.getTextContent()));
}
}
}
// Create PersistentObject
String[] fields = new String[fieldList.size()];
String[] results = new String[fieldList.size()];
for (int i = 0; i < fieldList.size(); i++) {
fields[i] = fieldList.get(i).name;
results[i] = fieldList.get(i).value;
}
// Check uidField
Constructor<PersistentObject> constructor = javaClass.getDeclaredConstructor();
PersistentObject po = constructor.newInstance();
if (!uidVersion.equals(po.getExportUIDVersion())) {
throw new IllegalArgumentException("UID export versions are different!");
}
// Read values
List<String> list = getValues(javaClass, uidValue);
if (list == null || list.size() == 0) {
// Create new
po.create(null);
po.set(fields, results);
} else if (overwrite) {
for (String idValue : list) {
Constructor<PersistentObject> tmpConstr =
javaClass.getDeclaredConstructor(String.class);
PersistentObject tmpPo = tmpConstr.newInstance(idValue);
System.out.println("Name: " + tmpPo.get("Name"));
tmpPo.set(fields, results);
}
}
} catch (Exception e) {
log.log(e.getMessage(), Log.ERRORS);
}
}
/**
* Parsing XML Document with SAX
*/
private Document readXmlDocument(InputSource is, String docDescription) throws SAXException,
ParserConfigurationException, java.io.IOException{
if (is == null) {
return null;
}
DocumentBuilder builder = DocumentBuilderFactory.newInstance().newDocumentBuilder();
return builder.parse(is);
}
/**
* Imports data into database. <br>
* XML could be a list of data objects or only one data object
*/
private void importXML(final String xml, final boolean overwrite){
try {
Document document = readXmlDocument(new InputSource(new StringReader(xml)), "Import");
NodeList rootNodeList = document.getChildNodes();
for (int rootIndex = 0; rootIndex < rootNodeList.getLength(); rootIndex++) {
Node rootNode = rootNodeList.item(rootIndex);
if (LIST_TAG.equals(rootNode.getNodeName())) {
NodeList nodeList = rootNode.getChildNodes();
for (int i = 0; i < nodeList.getLength(); i++) {
Node tableNode = nodeList.item(i);
if (!TEXT_TAG.equals(tableNode.getNodeName())
&& tableNode.getAttributes() != null) {
importData(tableNode, overwrite);
}
}
} else {
importData(rootNode, overwrite);
}
}
} catch (SAXException e) {
// TODO Auto-generated catch block
e.printStackTrace();
} catch (ParserConfigurationException e) {
// TODO Auto-generated catch block
e.printStackTrace();
} catch (IOException e) {
// TODO Auto-generated catch block
e.printStackTrace();
}
}
/********************************************************************************
* PUBLIC METHODS
*/
/**
* Imports data into database. <br>
* XML could be a list of data objects or only one data object
*/
public static void importData(final String data, final boolean overwrite){
new XML2Database().importXML(data, overwrite);
}
/**
* Exports a persistent object
*
* @return
*/
public static String exportData(final PersistentObject object){
StringBuffer buffer = new StringBuffer(HEADER);
buffer.append(new XMLObject(object).exportData());
return buffer.toString();
}
/**
* Exports an array
*
* @return
*/
public static String exportData(final List<? extends PersistentObject> dataList){
StringBuffer buffer = new StringBuffer(HEADER);
buffer.append("<" + LIST_TAG + ">\n");
for (PersistentObject object : dataList) {
buffer.append(new XMLObject(object).exportData());
}
buffer.append("</" + LIST_TAG + ">\n");
return buffer.toString();
}
}