buildframework/helium/sf/java/diamonds/src/com/nokia/helium/diamonds/XMLMerger.java
/*
* Copyright (c) 2007-2008 Nokia Corporation and/or its subsidiary(-ies).
* All rights reserved.
* This component and the accompanying materials are made available
* under the terms of the License "Eclipse Public License v1.0"
* which accompanies this distribution, and is available
* at the URL "http://www.eclipse.org/legal/epl-v10.html".
*
* Initial Contributors:
* Nokia Corporation - initial contribution.
*
* Contributors:
*
* Description:
*
*/
package com.nokia.helium.diamonds;
import java.io.File;
import java.io.InputStream;
import java.io.FileNotFoundException;
import java.io.FileOutputStream;
import java.io.IOException;
import java.io.UnsupportedEncodingException;
import java.util.Iterator;
import java.util.List;
import org.apache.log4j.Logger;
import org.dom4j.Attribute;
import org.dom4j.Document;
import org.dom4j.DocumentException;
import org.dom4j.Element;
import org.dom4j.io.OutputFormat;
import org.dom4j.io.SAXReader;
import org.dom4j.io.XMLWriter;
/**
* This class implements an XML file merger. All node from an external XML with
* same format (same root node name) will get added to the source root element.
*/
public class XMLMerger {
private Logger log = Logger.getLogger(getClass());
private InputStream mergeStream;
private Document doc;
private Element root;
private File outputFile;
/**
* Create an XMLMerger, the merge file will be used as input and output.
*
* @param merge
* @throws XMLMergerException
*/
public XMLMerger(InputStream stream, File file) throws XMLMergerException {
try {
outputFile = file;
mergeStream = stream;
SAXReader reader = new SAXReader();
doc = reader.read(mergeStream);
root = doc.getRootElement();
} catch (DocumentException e) {
throw new XMLMergerException(e.getMessage());
}
}
public void merge(InputStream stream) throws XMLMergerException {
try {
SAXReader reader = new SAXReader();
Document dataDoc = reader.read(stream);
merge(dataDoc);
} catch (DocumentException e) {
throw new XMLMergerException(e.getMessage());
}
}
private void merge(Document dataDoc) throws XMLMergerException {
Element dataRoot = dataDoc.getRootElement();
if (!root.getName().equals(dataRoot.getName())) {
throw new XMLMergerException(
"Trying to merge incompatible xml format ('"
+ root.getName() + "'!='" + dataRoot.getName()
+ "')");
}
mergeNode(root, dataRoot);
write();
}
/**
* Add all sub element of data file into the merged file. If the root
* element name is different for the merged file an XMLMergerException is
* thrown.
*
* @param data
* the input file.
* @throws XMLMergerException
*/
public void merge(File data) throws XMLMergerException {
log.debug("Merging " + data.getAbsolutePath());
try {
SAXReader reader = new SAXReader();
Document dataDoc = reader.read(data);
Element dataRoot = dataDoc.getRootElement();
merge(dataDoc);
} catch (DocumentException e) {
throw new XMLMergerException(e.getMessage());
}
}
/**
* Merging two XML elements. It only keeps difference.
*
* @param dest
* @param src
*/
@SuppressWarnings("unchecked")
protected void mergeNode(Element dest, Element src) {
for (Iterator<Element> node = src.elements().iterator(); node.hasNext();) {
Element element = node.next();
List<Element> ses = dest.elements(element.getName());
boolean add = true;
for (Element se : ses) {
if (areSame(se, element)) {
log.debug("Element " + element.getName() + " already found in dest.");
add = false;
}
}
if (add) {
log.debug("Adding node " + element.getName() + " to " + dest.getName());
dest.add(element.detach());
} else if (ses.size() > 0) {
log.debug("Merging " + ses.get(0).getName() + " to " + element.getName());
mergeNode(ses.get(0), element);
}
}
}
/**
* Compare two elements name and attributes. Returns true if name and all
* attributes are matching, false otherwise.
*
* @param a
* @param b
* @return boolean
*/
@SuppressWarnings("unchecked")
protected boolean areSame(Element a, Element b) {
log.debug("areSame:" + a + " <=> " + b);
if (!a.getName().equals(b.getName())) {
return false;
}
log.debug("same attribute list size?");
if (a.attributes().size() != b.attributes().size()) {
return false;
}
log.debug("same attribute list?");
for (Iterator<Attribute> at = a.attributes().iterator(); at.hasNext();) {
Attribute attra = at.next();
Attribute attrb = b.attribute(attra.getName());
if (attrb == null || !attra.getValue().equals(attrb.getValue())) {
return false;
}
}
if (!a.getTextTrim().equals(b.getTextTrim())) {
return false;
}
return true;
}
/**
* Write the XML content back the file.
* @throws XMLMergerException
*/
protected void write() throws XMLMergerException {
try {
FileOutputStream fos = new FileOutputStream(outputFile);
OutputFormat format = OutputFormat.createPrettyPrint();
XMLWriter writer = new XMLWriter(fos, format);
writer.write(doc);
writer.flush();
} catch (FileNotFoundException e) {
throw new XMLMergerException(e.getMessage());
} catch (UnsupportedEncodingException e) {
throw new XMLMergerException(e.getMessage());
} catch (IOException e) {
throw new XMLMergerException(e.getMessage());
}
}
/**
* Exception class related to the XMLMerger.
*/
public class XMLMergerException extends Exception {
private static final long serialVersionUID = 7624650310086957316L;
/**
* Default constructor.
* @param msg error message
*/
public XMLMergerException(String msg) {
super(msg);
}
}
}