All Downloads are FREE. Search and download functionalities are using the official Maven repository.

org.pageseeder.diffx.format.DefaultXMLDiffOutput Maven / Gradle / Ivy

The newest version!
/*
 * Copyright 2010-2015 Allette Systems (Australia)
 * http://www.allette.com.au
 *
 * 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.
 */
package org.pageseeder.diffx.format;

import org.jetbrains.annotations.NotNull;
import org.pageseeder.diffx.api.Operator;
import org.pageseeder.diffx.token.*;
import org.pageseeder.diffx.token.impl.SpaceToken;
import org.pageseeder.diffx.xml.Namespace;
import org.pageseeder.xmlwriter.XMLWriterNSImpl;

import java.io.IOException;
import java.io.PrintWriter;
import java.io.UncheckedIOException;
import java.io.Writer;

/**
 * An XML formatter that tries to rectify the errors affecting the well-formedness of the XML.
 *
 * 

This class will always close the elements correctly by maintaining a stack of parent * elements. * *

Implementation note: this classes uses the namespace prefixes 'dfx' and 'del', in the * future it should be possible to configure which prefixes to use for each namespace, but * in this version the namespace prefix mapping is hardcoded. * *

A limitation of this output is that it cannot report inserted/deleted attributes * with a namespace prefix.

* * @author Christophe Lauret * @version 0.9.0 */ public final class DefaultXMLDiffOutput extends XMLDiffOutputBase implements XMLDiffOutput { /** * The output goes here. */ final XMLWriterNSImpl xml; /** * Required to keep track of namespaces */ private int level = 0; /** * Creates a new output on the standard output. * * @see System#out */ public DefaultXMLDiffOutput() { this(new PrintWriter(System.out)); } /** * Creates a new formatter using the specified writer. * * @param w The writer to use. */ public DefaultXMLDiffOutput(Writer w) { this.xml = new XMLWriterNSImpl(w, false); } @Override public void start() { try { if (this.includeXMLDeclaration) { this.xml.xmlDecl(); } } catch (IOException ex) { throw new UncheckedIOException(ex); } } @Override public void handle(@NotNull Operator operator, XMLToken token) throws UncheckedIOException, IllegalStateException { if (this.level == 0) { declareNamespaces(); } try { if (operator.isEdit()) { handleEdit(operator, token); } else { handleMatch(token); } if (token.getType() == XMLTokenType.START_ELEMENT) this.level++; else if (token.getType() == XMLTokenType.END_ELEMENT) this.level--; this.xml.flush(); } catch (IOException ex) { throw new UncheckedIOException(ex); } } @Override public void end() { try { this.xml.flush(); } catch (IOException ex) { throw new UncheckedIOException(ex); } } void handleMatch(XMLToken token) throws IOException { token.toXML(this.xml); } void handleEdit(Operator operator, XMLToken token) throws IOException { if (token.getType() == XMLTokenType.START_ELEMENT) { token.toXML(this.xml); // insert an attribute to specify operator this.xml.attribute(getDiffNamespace().getUri(), operator == Operator.INS ? "insert" : "delete", "true"); } else if (token.getType() == XMLTokenType.ATTRIBUTE) { AttributeToken attribute = (AttributeToken) token; // NB We can't report inserted/deleted attributes with namespaces if (operator == Operator.INS) { token.toXML(this.xml); if (hasNoPrefix(attribute)) this.xml.attribute(getDiffNamespace(Operator.INS).getUri(), attribute.getName(), "true"); } else { if (hasNoPrefix(attribute)) this.xml.attribute(getDiffNamespace(Operator.DEL).getUri(), attribute.getName(), attribute.getValue()); } } else if (token == SpaceToken.NEW_LINE) { // just output the new line if (operator == Operator.INS) { token.toXML(this.xml); } } else if (token.getType() == XMLTokenType.TEXT) { // wrap the characters in a element this.xml.openElement(getDiffNamespace().getUri(), toElement(operator), false); token.toXML(this.xml); this.xml.closeElement(); } else if (token.getType() == XMLTokenType.END_ELEMENT) { token.toXML(this.xml); } else { // Only include inserted content if (operator == Operator.INS) { token.toXML(this.xml); } } } private boolean hasNoPrefix(AttributeToken attribute) { if (attribute.getName().indexOf(':') != -1) return false; String prefix = this.namespaces.getPrefix(attribute.getNamespaceURI()); return prefix == null || prefix.isEmpty(); } /** * Write the namespace mapping to the XML output */ private void declareNamespaces() { Namespace diffNamespace = getDiffNamespace(); Namespace insNamespace = getDiffNamespace(Operator.INS); Namespace delNamespace = getDiffNamespace(Operator.DEL); this.xml.setPrefixMapping(diffNamespace.getUri(), diffNamespace.getPrefix()); this.xml.setPrefixMapping(insNamespace.getUri(), insNamespace.getPrefix()); this.xml.setPrefixMapping(delNamespace.getUri(), delNamespace.getPrefix()); if (this.namespaces != null) { for (Namespace namespace : this.namespaces) { this.xml.setPrefixMapping(namespace.getUri(), namespace.getPrefix()); } } } /** * @return "ins" for insertions or "del" for deletions */ private static String toElement(Operator operator) { return operator == Operator.INS ? "ins" : "del"; } }




© 2015 - 2025 Weber Informatics LLC | Privacy Policy