package gr.ntua.ivml.mint.mapping;
import java.io.BufferedReader;
import java.io.File;
import java.io.FileInputStream;
import java.io.FileNotFoundException;
import java.io.FileReader;
import java.io.FileWriter;
import java.io.IOException;
import java.io.InputStream;
import java.io.InputStreamReader;
import java.util.ArrayList;
import java.util.HashMap;
import java.util.Iterator;
import java.util.List;
import java.util.Map;
import org.apache.log4j.Logger;
import gr.ntua.ivml.mint.db.DB;
import gr.ntua.ivml.mint.persistent.XmlSchema;
import net.sf.json.JSONArray;
import net.sf.json.JSONObject;
import net.sf.json.JSONSerializer;
public class MappingConverter {
protected static final Logger log = Logger.getLogger( MappingConverter.class);
private XmlSchema schema = null;
private String template = null;
private JSONObject iNamespaces = null;
private JSONObject oNamespaces = null;
public static int main(String[] args) {
System.out.println("Converting...");
XmlSchema schema = null;
List<XmlSchema> list = DB.getXmlSchemaDAO().findAll();
for(XmlSchema s: list) {
if(s.getName().indexOf("LIDO") >= 0) {
schema = s;
break;
}
}
if(schema == null) return -1;
MappingConverter converter = new MappingConverter(schema);
File input = new File("test.xml");
File output = new File("output.xml");
try {
JSONObject result = converter.convert(new FileInputStream(input));
FileWriter writer = new FileWriter(output);
writer.write(result.toString());
writer.close();
} catch (FileNotFoundException e) {
e.printStackTrace();
} catch (IOException e) {
e.printStackTrace();
}
return 0;
}
/**
* Initialize a mapping converter that converts input mappings to mappings of provided schema.
* @param schema the provided schema.
*/
public MappingConverter(XmlSchema schema) {
this.setSchema(schema);
}
/**
* Get the XmlSchema for this mapping converter.
* @return the XmlSchema.
*/
public XmlSchema getSchema() {
return this.schema;
}
/**
* Set the schema for this mapping converter.
* @param schema
*/
public void setSchema(XmlSchema schema) {
this.schema = schema;
this.template = schema.getJsonTemplate();
}
/**
* Provide a mapping template directly.
* This operation will set schema to null. Use it only if template is not related to a schema.
* Use setSchema otherwise.
* @param template The template json string.
*/
public void setTemplate(String template) {
this.schema = null;
this.template = template;
}
/**
* Convert mapping contained in InputStream.
* @param stream InputStream of mapping json string.
* @return Converted mapping.
*/
public JSONObject convert(InputStream stream) {
try {
StringBuffer buffer = new StringBuffer();
String line;
BufferedReader in = new BufferedReader(new InputStreamReader(stream, "UTF8"));
while((line = in.readLine()) != null) {
buffer.append(line);
}
String mapping = buffer.toString();
return convert(mapping);
} catch (IOException e) {
e.printStackTrace();
return null;
}
}
/**
* Convert mapping in string.
* @param mapping json string of a mapping.
* @return Converted mapping.
*/
public JSONObject convert(String mapping) {
JSONObject input = (JSONObject) JSONSerializer.toJSON(mapping);
return convert(input);
}
/**
* Convert mapping in JSONOBject.
* @param mapping JSONObject serialization of mapping.
* @return Converted mapping.
*/
public JSONObject convert(JSONObject mapping) {
JSONObject result = (JSONObject) JSONSerializer.toJSON(this.schema.getJsonTemplate());
JSONMappingHandler input = new JSONMappingHandler(mapping);
JSONMappingHandler output = new JSONMappingHandler(result);
this.iNamespaces = input.getNamespaces();
this.oNamespaces = output.getNamespaces();
log.debug("Merging template");
JSONMappingHandler iTemplate = input.getTemplate();
JSONMappingHandler oTemplate = output.getTemplate();
this.merge(iTemplate, oTemplate);
Map<String, JSONMappingHandler> iGroups = input.getGroupHandlers();
Map<String, JSONMappingHandler> oGroups = output.getGroupHandlers();
Iterator<String> keys = iGroups.keySet().iterator();
while(keys.hasNext()) {
String key = keys.next();
if(oGroups.containsKey(key)) {
log.debug("Merging group " + key);
JSONMappingHandler iGroup = iGroups.get(key);
JSONMappingHandler oGroup = oGroups.get(key);
JSONObject iContents = iGroup.getObject("contents");
JSONObject oContents = oGroup.getObject("contents");
if(oContents.isArray()) {
if(iContents.isArray()) merge(iGroup.getArray("contents"), oGroup.getArray("contents"));
else merge(iContents, oGroup.getArray("contents").getJSONObject(0));
} else {
if(iContents.isArray()) merge(iGroup.getArray("contents").getJSONObject(0), oContents);
else merge(iContents, oContents);
}
} else {
log.debug("Group " + key + " not found in target schema");
}
}
return result;
}
private void merge(JSONObject iContents, JSONObject jsonObject) {
merge(new JSONMappingHandler(iContents), new JSONMappingHandler(jsonObject));
}
/**
* Merge contents of newElement into element.
* @param newElement a new element to be merged with element.
* @param element an existing mapping element. This element will be modified.
*/
public void merge(JSONMappingHandler newElement, JSONMappingHandler element) {
log.debug("merging " + newElement.getFullName() + " to " + element.getFullName());
if(this.sameName(newElement, element) && this.sameNamespace(newElement, element)) {
// merge mappings
log.debug("-- merge mappings");
if(!element.isFixed() && element.has(JSONMappingHandler.ELEMENT_MAPPINGS)) {
JSONArray iMappings = newElement.getMappings();
JSONArray oMappings = element.getMappings();
log.debug("input mappings : " + iMappings);
log.debug("output mappings : " + oMappings);
if(iMappings != null) oMappings.addAll(iMappings);
}
// merge conditions
log.debug("-- merge conditions");
if(element.has(JSONMappingHandler.ELEMENT_CONDITION)) {
JSONObject iCondition = newElement.getCondition();
if(iCondition != null) {
element.setCondition(iCondition);
}
}
// apply to children
log.debug("-- merge children");
this.merge(newElement.getChildren(), element.getChildren());
// apply to attributes
log.debug("-- merge attributes");
this.merge(newElement.getAttributes(), element.getAttributes());
}
}
private HashMap<String, List<JSONMappingHandler>> elementMap(JSONArray elements) {
HashMap<String, List<JSONMappingHandler>> map = new HashMap<String, List<JSONMappingHandler>>();
for(int i = 0; i < elements.size(); i++) {
JSONObject element = elements.getJSONObject(i);
JSONMappingHandler handler = new JSONMappingHandler(element);
String label = handler.getFullName();
List<JSONMappingHandler> list = null;
if(map.containsKey(label)) {
list = map.get(label);
} else {
list = new ArrayList<JSONMappingHandler>();
map.put(label, list);
}
list.add(handler);
}
return map;
}
public void merge(JSONArray newElements, JSONArray elements) {
if(elements == null || newElements == null) return;
HashMap<String, List<JSONMappingHandler>> iElements = elementMap(newElements);
HashMap<String, List<JSONMappingHandler>> oElements = elementMap(elements);
List<JSONObject> createdElements = new ArrayList<JSONObject>();
List<JSONMappingHandler> oUsed = new ArrayList<JSONMappingHandler>();
Iterator<String> iKeys = iElements.keySet().iterator();
while(iKeys.hasNext()) {
String key = iKeys.next();
if(oElements.containsKey(key)) {
List<JSONMappingHandler> iList = iElements.get(key);
List<JSONMappingHandler> oList = oElements.get(key);
JSONMappingHandler oFirst = oList.get(0);
if(oFirst.isRepeatable()) {
log.debug(" " + oFirst.getFullName() + " is repeatable");
for(int i = 0; i < iList.size(); i++) {
JSONMappingHandler iElement = iList.get(i);
if(iElement.hasMappingsRecursive()) {
JSONMappingHandler oElement = null;
for(int j = 0; j < oList.size(); j++) {
JSONMappingHandler oe = oList.get(j);
if(iElement.has(JSONMappingHandler.ELEMENT_LABEL)) {
if(!oe.has(JSONMappingHandler.ELEMENT_LABEL) || !iElement.getLabel().equals(oe.getLabel())) continue;
}
if(!oUsed.contains(oe)) {
oUsed.add(oe);
oElement = oe;
break;
}
}
if(oElement == null && !iElement.has(JSONMappingHandler.ELEMENT_LABEL)) {
JSONObject duplicate = (JSONObject) JSONSerializer.toJSON(oFirst.object.toString());
createdElements.add(duplicate);
oElement = new JSONMappingHandler(duplicate);
}
this.merge(iElement, oElement);
}
}
} else {
log.debug(" " + oFirst.getFullName() + " is unique");
this.merge(iList.get(0), oList.get(0));
}
} else {
//TODO: notify element was not found in output schema
}
}
elements.addAll(createdElements);
}
private boolean sameName(JSONMappingHandler one, JSONMappingHandler other) {
return (one.has("name") && other.has("name") &&
one.getString("name").equalsIgnoreCase(other.getString("name")));
}
private boolean sameNamespace(JSONMappingHandler input, JSONMappingHandler output) {
String iPrefix = input.getString("prefix");
String oPrefix = output.getString("prefix");
if(iPrefix == null && oPrefix == null) return true;
if(iPrefix.length() == 0 && oPrefix.length() == 0) return true;
if(!iNamespaces.has(iPrefix) && !oNamespaces.has(oPrefix) && iPrefix.equalsIgnoreCase(oPrefix)) return true;
else if(iNamespaces.has(iPrefix) && oNamespaces.has(oPrefix)) {
String iURL = iNamespaces.getString(iPrefix);
String oURL = oNamespaces.getString(oPrefix);
if(iURL.equalsIgnoreCase(oURL)) return true;
}
return false;
}
}