org.eclipse.smila.datamodel.xml.DOMRecordWriter.java Source code

Java tutorial

Introduction

Here is the source code for org.eclipse.smila.datamodel.xml.DOMRecordWriter.java

Source

/***********************************************************************************************************************
 * Copyright (c) 2008, 2011 Attensity Europe GmbH and brox IT Solutions GmbH. 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: Juergen Schumacher (empolis GmbH) - initial API and implementation Andreas Weber (Attensity Europe
 * GmbH) - data model simplification
 **********************************************************************************************************************/

package org.eclipse.smila.datamodel.xml;

import java.util.Iterator;
import java.util.Map.Entry;
import java.util.Set;

import javax.xml.XMLConstants;

import org.apache.commons.lang.StringUtils;
import org.eclipse.smila.datamodel.Any;
import org.eclipse.smila.datamodel.Any.ValueType;
import org.eclipse.smila.datamodel.AnyMap;
import org.eclipse.smila.datamodel.AnySeq;
import org.eclipse.smila.datamodel.Record;
import org.eclipse.smila.datamodel.Value;
import org.w3c.dom.Document;
import org.w3c.dom.Element;

/**
 * A utility class to create DOM representations of SMILA records.
 * 
 * @author jschumacher
 */
public class DOMRecordWriter {

    /** switch to true to add newlines for better readability, but poorer performance. */
    private final boolean _printPretty;

    /**
     * create new RecordBuilder.
     */
    public DOMRecordWriter() {
        this(false);
    }

    /**
     * create new RecordBuilder with custom printPretty flag.
     * 
     * @param printPretty
     *          printPretty flag
     */
    public DOMRecordWriter(final boolean printPretty) {
        _printPretty = printPretty;
    }

    /**
     * Append a Record element describing the given record to the given parent element. The record element is appended as
     * a new last child to the parent element.
     * 
     * @param parent
     *          the parent element to append to.
     * @param record
     *          the record to transform.
     * 
     * @return the appended element
     */
    public Element appendRecord(final Element parent, final Record record) {
        final Document factory = parent.getOwnerDocument();
        newline(factory, parent);
        final Element recordElement = buildRecord(factory, record);
        recordElement.setAttribute(XmlConstants.ATTRIBUTE_XMLNSREC, XmlConstants.NAMESPACE_RECORD);
        parent.appendChild(recordElement);
        newline(factory, parent);
        return recordElement;

    }

    /**
     * Append a RecordList element describing the given record list to the given parent element. The record list element
     * is appended as a new last child to the parent element.
     * 
     * @param parent
     *          the parent element to append to.
     * @param records
     *          the record list to transform.
     * 
     * @return the appended element
     */
    public Element appendRecordList(final Element parent, final Iterable<Record> records) {
        final Document factory = parent.getOwnerDocument();
        newline(factory, parent);
        final Element recordListElement = appendElement(factory, parent, XmlConstants.TAG_RECORDLIST);
        for (final Record record : records) {
            final Element recordElement = buildRecord(factory, record);
            recordListElement.appendChild(recordElement);
            newline(factory, parent);
        }
        return recordListElement;
    }

    /**
     * Append a RecordList element describing the given record list to the given parent element. The record list element
     * is appended as a new last child to the parent element.
     * 
     * @param parent
     *          the parent element to append to.
     * @param records
     *          the record list to transform.
     * 
     * @return the appended element
     */
    public Element appendRecordList(final Element parent, final Record[] records) {
        final Document factory = parent.getOwnerDocument();
        newline(factory, parent);
        final Element recordListElement = appendElement(factory, parent, XmlConstants.TAG_RECORDLIST);
        for (final Record record : records) {
            final Element recordElement = buildRecord(factory, record);
            recordListElement.appendChild(recordElement);
            newline(factory, parent);
        }
        return recordListElement;
    }

    /**
     * build Record element.
     * 
     * @param factory
     *          factory for creating DOM elements
     * @param record
     *          the record to transform
     * 
     * @return the created element.
     */
    public Element buildRecord(final Document factory, final Record record) {
        final Element recordElement = factory.createElementNS(XmlConstants.NAMESPACE_RECORD,
                XmlConstants.TAG_RECORD);
        recordElement.setAttribute(XmlConstants.ATTRIBUTE_VERSION, XmlConstants.SCHEMA_VERSION_RECORD);

        newline(factory, recordElement);

        if (record.getMetadata() != null) {
            final Set<Entry<String, Any>> entries = record.getMetadata().entrySet();
            for (final Entry<String, Any> entry : entries) {
                if (entry.getValue().isMap()) {
                    appendMap(factory, recordElement, ((AnyMap) entry.getValue()), entry.getKey());
                } else if (entry.getValue().isSeq()) {
                    appendSeq(factory, recordElement, ((AnySeq) entry.getValue()), entry.getKey());
                } else { // Value
                    appendVal(factory, recordElement, ((Value) entry.getValue()), entry.getKey());
                }
            }
        }

        if (record.hasAttachments()) {
            final Iterator<String> attachmentNames = record.getAttachmentNames();
            while (attachmentNames.hasNext()) {
                final String attachmentName = attachmentNames.next();
                appendTextElement(factory, recordElement, XmlConstants.TAG_ATTACHMENT, attachmentName);
            }
        }

        return recordElement;
    }

    /**
     * Builds the record as document element.
     * 
     * @param factory
     *          the factory
     * @param record
     *          the record
     */
    public void buildRecordAsDocumentElement(final Document factory, final Record record) {
        final Element recordElement = buildRecord(factory, record);
        recordElement.setAttribute(XmlConstants.ATTRIBUTE_VERSION, XmlConstants.SCHEMA_VERSION_RECORD);
        recordElement.setAttributeNS(XMLConstants.XMLNS_ATTRIBUTE_NS_URI, XmlConstants.ATTRIBUTE_XMLNSREC,
                XmlConstants.NAMESPACE_RECORD);
        factory.appendChild(recordElement);
    }

    /**
     * append Map element.
     * 
     * @param factory
     *          factory for creating DOM elements.
     * @param parentElement
     *          the parent element to append to.
     * @param anyMap
     *          the AnyMap object to append as XML.
     * @param key
     *          a possible key, if we are within a map (maybe 'null')
     */
    private void appendMap(final Document factory, final Element parentElement, final AnyMap anyMap,
            final String key) {
        final Element attributeElement = appendElement(factory, parentElement, XmlConstants.TAG_MAP);
        if (key != null) {
            attributeElement.setAttribute(XmlConstants.ATTRIBUTE_KEY, key);
        }
        if (!anyMap.isEmpty()) {
            final Iterator<String> childKeys = anyMap.keySet().iterator();
            while (childKeys.hasNext()) {
                final String childKey = childKeys.next();
                final Any any = anyMap.get(childKey);
                if (any.isMap()) {
                    appendMap(factory, attributeElement, ((AnyMap) any), childKey);
                } else if (any.isSeq()) {
                    appendSeq(factory, attributeElement, ((AnySeq) any), childKey);
                } else { // Value
                    appendVal(factory, attributeElement, ((Value) any), childKey);
                }
            }
        }
    }

    /**
     * append Seq element.
     * 
     * @param factory
     *          factory for creating DOM elements.
     * @param parentElement
     *          the parent element to append to.
     * @param anySeq
     *          the AnySeq object to append as XML.
     * @param key
     *          a possible key, if we are within a map (maybe 'null')
     */
    private void appendSeq(final Document factory, final Element parentElement, final AnySeq anySeq,
            final String key) {
        final Element attributeElement = appendElement(factory, parentElement, XmlConstants.TAG_SEQ);
        if (key != null) {
            attributeElement.setAttribute(XmlConstants.ATTRIBUTE_KEY, key);
        }
        if (!anySeq.isEmpty()) {
            final Iterator<Any> seqElements = anySeq.iterator();
            while (seqElements.hasNext()) {
                final Any any = seqElements.next();
                if (any.isMap()) {
                    appendMap(factory, attributeElement, ((AnyMap) any), null);
                } else if (any.isSeq()) {
                    appendSeq(factory, attributeElement, ((AnySeq) any), null);
                } else { // Value
                    appendVal(factory, attributeElement, ((Value) any), null);
                }
            }
        }
    }

    /**
     * append Value to parent element.
     * 
     * @param factory
     *          factory for creating DOM elements.
     * @param parentElement
     *          the parent element to append to.
     * @param value
     *          the value to append
     * @param key
     *          a possible key, if we are within a map (maybe 'null')
     */
    private void appendVal(final Document factory, final Element parentElement, final Value value,
            final String key) {
        final Element valueElement = appendTextElement(factory, parentElement, XmlConstants.TAG_VAL,
                value.asString());
        if (key != null) {
            valueElement.setAttribute(XmlConstants.ATTRIBUTE_KEY, key);
        }
        // write no 'type' attribute for strings
        if (value.getValueType() != ValueType.STRING) {
            // LONG, DATE, ... -> type 'long', 'date'
            valueElement.setAttribute(XmlConstants.ATTRIBUTE_TYPE, value.getValueType().name().toLowerCase());
        }
    }

    /**
     * create an empty element and append it to the parent element.
     * 
     * @param factory
     *          factory for creating DOM elements.
     * @param parentElement
     *          element to append to
     * @param tag
     *          name of element
     * 
     * @return new element
     */
    private Element appendElement(final Document factory, final Element parentElement, final String tag) {
        final Element element = factory.createElementNS(XmlConstants.NAMESPACE_RECORD, tag);
        newline(factory, element);
        parentElement.appendChild(element);
        newline(factory, parentElement);
        return element;
    }

    /**
     * append an element containing a text node.
     * 
     * @param factory
     *          the DOM element factory to use
     * @param element
     *          the element to append to
     * @param name
     *          the qualified name of the new element
     * @param text
     *          the text content to add
     * 
     * @return the new element
     */
    private Element appendTextElement(final Document factory, final Element element, final String name,
            final String text) {
        final Element textElement = factory.createElementNS(XmlConstants.NAMESPACE_RECORD, name);
        textElement.appendChild(factory.createTextNode(cleanText(text)));
        element.appendChild(textElement);
        newline(factory, element);
        return textElement;
    }

    /**
     * append a newline text element if printPretty is activated.
     * 
     * @param factory
     *          factory to use.
     * @param element
     *          element to append to.
     */
    private void newline(final Document factory, final Element element) {
        if (_printPretty) {
            element.appendChild(factory.createTextNode("\n"));
        }
    }

    /**
     * @param text
     *          input text (XML)
     * @return XML text cleaned from characters forbidden in XML 1.1 specification
     */
    protected static String cleanText(final String text) {
        // (Andreas Weber) from XML 1.1 spec:
        // Due to potential problems with APIs, #x0 is still forbidden both directly and as a character reference.
        // (jschumacher) this uses StringUtils.replace and not String.replace(char, char) because the latter always
        // creates a new string instance, while the first return the input string instance as result, if the string
        // to be replaced is not contained (at least in commons.lang 2.4). It's better to safe unnecessary instance
        // creations in the most cases (no replacement at all) than to have a bit faster replacement method in very
        // rare cases.
        return StringUtils.replace(text, "\u0000", " ");
    }

}