All Downloads are FREE. Search and download functionalities are using the official Maven repository.
Please wait. This can take some minutes ...
Many resources are needed to download a project. Please understand that we have to compensate our server costs. Thank you in advance.
Project price only 1 $
You can buy this project and download/modify it how often you want.
org.magicwerk.brownies.jdom.XmlTransformer Maven / Gradle / Ivy
/*
* Copyright 2015 by Thomas Mauch
*
* 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.
*
* $Id$
*/
package org.magicwerk.brownies.jdom;
import java.util.Comparator;
import java.util.List;
import java.util.function.Predicate;
import org.jdom2.Attribute;
import org.jdom2.Comment;
import org.jdom2.Content;
import org.jdom2.DocType;
import org.jdom2.Document;
import org.jdom2.Element;
import org.jdom2.ProcessingInstruction;
import org.jdom2.Text;
import org.magicwerk.brownies.collections.GapList;
import org.magicwerk.brownies.collections.IList;
import org.magicwerk.brownies.core.CheckTools;
import org.slf4j.Logger;
import org.slf4j.LoggerFactory;
/**
* Transform XML content according to registered handlers.
*
* @author Thomas Mauch
* @version $Id$
*/
public class XmlTransformer {
/** Logger */
static final Logger LOG = LoggerFactory.getLogger(XmlTransformer.class);
static class NodeContext {
Element parent;
Object node;
NodeContext(Element parent, Object node) {
this.parent = parent;
this.node = node;
}
}
List> handlers = GapList.create();
boolean copyHandler = true;
Object currentNode;
IList currentNodeChildren;
Document dstDoc;
Element dstRootElem;
Element dstElem;
public List> getHandlers() {
return handlers;
}
public void setHandlers(List> handlers) {
this.handlers = handlers;
}
/**
* Transform document according to registered handlers.
*
* @param doc document to transform
* @return transformed document
*/
public Document transform(Document doc) {
doTransform(doc);
return dstDoc;
}
/**
* Transform element according to registered handlers.
*
* @param elem element to transform
* @return transformed element
*/
public Element transform(Element elem) {
doTransform(elem);
return dstRootElem;
}
void doTransform(Object node) {
dstDoc = null;
dstRootElem = null;
IList open = GapList.create(new NodeContext(null, node));
while (true) {
NodeContext nc = open.pollFirst();
if (nc == null) {
break;
}
List newProcessItems = handle(nc.parent, nc.node);
//LOG.debug("doTransform {}/{} -> {}", nc.parent, nc.node, dstElem);
for (int i = 0; i < newProcessItems.size(); i++) {
open.add(i, new NodeContext(dstElem, newProcessItems.get(i)));
}
}
}
List handle(Element parent, Object node) {
dstElem = parent;
currentNode = node;
currentNodeChildren = GapList.create();
// Apply handlers
doHandle(node);
// Handle attributes before children
currentNodeChildren.sort(new Comparator() {
@Override
public int compare(Object o1, Object o2) {
int n1 = (o1 instanceof Attribute) ? 0 : 1;
int n2 = (o2 instanceof Attribute) ? 0 : 1;
return n1 - n2;
}
});
return currentNodeChildren;
}
void doHandle(Object obj) {
CheckTools.checkTypeOf(obj, Document.class, Content.class, Attribute.class);
boolean done = false;
for (Handler> h : handlers) {
@SuppressWarnings("unchecked")
Handler handler = (Handler) h;
if (!testHandler(handler, obj)) {
continue;
}
done = handler.apply(obj, this);
if (done) {
break;
}
}
if (!done) {
if (copyHandler) {
applyAttributes();
applyContent();
add(copyNode(obj));
}
}
}
boolean testHandler(Handler handler, Object obj) {
try {
return handler.test(obj);
} catch (ClassCastException e) {
return false;
}
}
//
/**
* Copy node non-recursively and add it to the destination tree.
*
* @param node node to copy
*/
public void copy(Object node) {
add(copyNode(node));
}
/**
* Copy node recursively and add it to the destination tree.
*
* @param node node to copy
*/
public void copyOf(Object node) {
Object copy;
if (node instanceof Element) {
copy = copyElementRecursively((Element) node);
} else {
copy = copyNode(node);
}
add(copy);
}
/**
* Nest the element inside a new element with name newName.
*/
public void nestElement(Element elem, String newName) {
Element newElem = new Element(newName);
newElem.addContent(elem);
add(newElem);
}
/**
* Rename element to newName.
*/
public void renameElement(Element elem, String newName) {
Element newElem = XmlTransformer.copyElement(elem);
newElem.setName(newName);
add(newElem);
}
/**
* Rename attribute to newName.
*/
public void renameAttribute(Attribute attr, String newName) {
Attribute newAttr = XmlTransformer.copyAttribute(attr);
newAttr.setName(newName);
add(newAttr);
}
public void convertAttributeToElement(Attribute attr) {
convertAttributeToElement(attr, attr.getName());
}
public void convertAttributeToElement(Attribute attr, String newName) {
Element newElem = new Element(newName);
newElem.setText(attr.getValue());
add(newElem);
}
void convertElementToAttribute(Element elem) {
convertElementToAttribute(elem, elem.getName());
}
void convertElementToAttribute(Element elem, String newName) {
Attribute newAttr = new Attribute(newName, elem.getText());
add(newAttr);
}
//
public void applyNodes(Object... nodes) {
currentNodeChildren.addArray(nodes);
}
public void applyNodes(List nodes) {
currentNodeChildren.addAll(nodes);
}
/**
* Apply both attributes and content.
*/
public void applyAll() {
applyAttributes();
applyContent();
}
public void applyAttributes() {
Element elem = (currentNode instanceof Element) ? (Element) currentNode : null;
if (elem != null) {
currentNodeChildren.addAll(elem.getAttributes());
}
}
public void applyContent() {
if (currentNode instanceof Element) {
Element elem = (Element) currentNode;
currentNodeChildren.addAll(elem.getContent());
} else if (currentNode instanceof Document) {
Document doc = (Document) currentNode;
currentNodeChildren.addAll(doc.getContent());
}
}
public void applyElements() {
if (currentNode instanceof Element) {
Element elem = (Element) currentNode;
currentNodeChildren.addAll(elem.getChildren());
} else if (currentNode instanceof Document) {
Document doc = (Document) currentNode;
currentNodeChildren.add(doc.getRootElement());
}
}
public Element getDestinationElement() {
return dstElem;
}
/**
* Add result to destination tree.
*
* @param result result to add
*/
public void add(Object result) {
if (result == null) {
//
} else if (result instanceof Boolean && ((Boolean) result)) {
//
} else if (result instanceof Document) {
Document doc = (Document) result;
CheckTools.check(dstDoc == null, "Destination document already set");
dstDoc = doc;
} else if (result instanceof Element) {
Element elem = (Element) result;
if (dstElem != null) {
dstElem.addContent(elem);
} else {
if (dstDoc != null) {
dstDoc.setRootElement(elem);
} else {
dstDoc = new Document(elem);
}
}
dstElem = elem;
if (dstRootElem == null) {
dstRootElem = dstElem;
}
} else if (result instanceof Attribute) {
Attribute attr = (Attribute) result;
dstElem.setAttribute(attr);
} else if (result instanceof Content) {
Content cont = (Content) result;
if (dstElem == null) {
dstDoc.addContent(cont);
} else {
dstElem.addContent(cont);
}
} else {
throw new AssertionError("Invalid content: " + result.getClass());
}
}
/**
* Copy specified node (non recursively).
*
* @param node node to copy
* @return copy of node
*/
public static Object copyNode(Object node) {
if (node instanceof Document) {
return copyDocument((Document) node);
} else if (node instanceof Element) {
return copyElement((Element) node);
} else if (node instanceof Attribute) {
return copyAttribute((Attribute) node);
} else if (node instanceof Text) {
return copyText((Text) node);
} else if (node instanceof Comment) {
return copyComment((Comment) node);
} else if (node instanceof ProcessingInstruction) {
return copyProcessingInstruction((ProcessingInstruction) node);
} else if (node instanceof DocType) {
return copyDocType((DocType) node);
}
throw new AssertionError("Invalid content: " + node.getClass());
}
public static Document copyDocument(Document doc) {
Document copy = new Document();
return copy;
}
/**
* Copy element recursively (with attributes and nested content)
*
* @param elem element to copy
* @return created element
*/
public static Element copyElementRecursively(Element elem) {
Element copy = elem.clone();
return copy;
}
/**
* Copy element only (without attributes and nested content).
*
* @param elem element to copy
* @return created element
*/
public static Element copyElement(Element elem) {
Element copy = new Element(elem.getName(), elem.getNamespace());
return copy;
}
public static Attribute copyAttribute(Attribute attr) {
Attribute copy = new Attribute(attr.getName(), attr.getValue(), attr.getNamespace());
return copy;
}
public static Text copyText(Text text) {
Text copy = new Text(text.getText());
return copy;
}
public static Comment copyComment(Comment comm) {
Comment copy = new Comment(comm.getText());
return copy;
}
public static ProcessingInstruction copyProcessingInstruction(ProcessingInstruction pi) {
ProcessingInstruction copy = new ProcessingInstruction(pi.getTarget(), pi.getData());
return copy;
}
public static DocType copyDocType(DocType dt) {
DocType copy = dt.clone();
return copy;
}
//
/**
* Interface for handling nodes in a XML tree.
*/
public interface Handler {
/**
* Determines whether handler should be executed for the node.
*
* @param node node
* @return true if handler should be executed for the node
*/
boolean test(T node);
/**
* Execute handler for node.
* The handler is only executed if test() has returned true.
*
* @param node node
* @param xt XML transformer to use
* @return true if node has been processed and no more handlers should be executed, false to continue processing
*/
boolean apply(T node, XmlTransformer xt);
}
/**
* Abstract handler for elements.
*/
public static abstract class ElementHandler implements Handler {
Predicate predicate;
public ElementHandler(Predicate predicate) {
this.predicate = predicate;
}
@Override
public boolean test(Element node) {
return predicate == null || predicate.test(node);
}
}
/**
* Abstract handler for attributes.
*/
public static abstract class AttributeHandler implements Handler {
Predicate predicate;
public AttributeHandler(Predicate predicate) {
this.predicate = predicate;
}
@Override
public boolean test(Attribute node) {
return predicate == null || predicate.test(node);
}
}
}