FOMDocument.java

/*
 * Licensed to the Apache Software Foundation (ASF) under one or more
 * contributor license agreements.  The ASF licenses this file to You
 * 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.  For additional information regarding
 * copyright in this work, please see the NOTICE file in the top level
 * directory of this distribution.
 */
package org.apache.abdera.parser.stax;

import java.io.ByteArrayOutputStream;
import java.io.IOException;
import java.io.OutputStream;
import java.io.OutputStreamWriter;
import java.util.ArrayList;
import java.util.Date;
import java.util.Iterator;
import java.util.List;

import javax.activation.MimeType;
import javax.xml.stream.XMLStreamException;

import org.apache.abdera.i18n.iri.IRI;
import org.apache.abdera.i18n.rfc4646.Lang;
import org.apache.abdera.model.Base;
import org.apache.abdera.model.Document;
import org.apache.abdera.model.Element;
import org.apache.abdera.model.ElementWrapper;
import org.apache.abdera.util.EntityTag;
import org.apache.abdera.util.XmlUtil;
import org.apache.abdera.util.XmlUtil.XMLVersion;
import org.apache.abdera.writer.Writer;
import org.apache.abdera.writer.WriterOptions;
import org.apache.axiom.fom.AbderaDocument;
import org.apache.axiom.om.OMComment;
import org.apache.axiom.om.OMDocument;
import org.apache.axiom.om.OMElement;
import org.apache.axiom.om.OMNode;
import org.apache.axiom.om.OMOutputFormat;
import org.apache.axiom.om.OMProcessingInstruction;
import org.apache.axiom.om.impl.intf.AxiomDocument;

@SuppressWarnings("unchecked")
public class FOMDocument<T extends Element> extends FOMSerializable implements AbderaDocument<T>, AxiomDocument {

    private static final long serialVersionUID = -3255339511063344662L;
    protected IRI base = null;
    protected MimeType contentType = null;
    protected Date lastModified = null;
    protected EntityTag etag = null;
    protected String language = null;
    protected String slug = null;
    protected boolean preserve = true;

    public T getRoot() {
        FOMFactory factory = (FOMFactory)getFactory();
        return (T)factory.getElementWrapper((T)this.getOMDocumentElement());
    }

    public Document<T> setRoot(T root) {
        if (root instanceof OMElement) {
            this.setOMDocumentElement((OMElement)root);
        } else if (root instanceof ElementWrapper) {
            this.setOMDocumentElement((OMElement)((ElementWrapper)root).getInternal());
        }
        return this;
    }

    public IRI getBaseUri() {
        return base;
    }

    public Document<T> setBaseUri(String base) {
        this.base = new IRI(base);
        return this;
    }

    public void writeTo(OutputStream out, WriterOptions options) throws IOException {
        Writer writer = this.getFactory().getAbdera().getWriter();
        writer.writeTo(this, out, options);
    }

    public void writeTo(java.io.Writer out, WriterOptions options) throws IOException {
        Writer writer = this.getFactory().getAbdera().getWriter();
        writer.writeTo(this, out, options);
    }

    public void writeTo(Writer writer, OutputStream out) throws IOException {
        writer.writeTo(this, out);
    }

    public void writeTo(Writer writer, java.io.Writer out) throws IOException {
        writer.writeTo(this, out);
    }

    public void writeTo(Writer writer, OutputStream out, WriterOptions options) throws IOException {
        writer.writeTo(this, out, options);
    }

    public void writeTo(Writer writer, java.io.Writer out, WriterOptions options) throws IOException {
        writer.writeTo(this, out, options);
    }

    public void writeTo(OutputStream out) throws IOException {
        String charset = getCharset();
        if (charset == null)
            charset = "UTF-8";
        Writer writer = getFactory().getAbdera().getWriter();
        writeTo(writer, new OutputStreamWriter(out, charset));
    }

    public void writeTo(java.io.Writer writer) throws IOException {
        Writer out = getFactory().getAbdera().getWriter();
        if (!(out instanceof FOMWriter)) {
            out.writeTo(this, writer);
        } else {
            try {
                OMOutputFormat outputFormat = new OMOutputFormat();
                if (this.getCharsetEncoding() != null)
                    outputFormat.setCharSetEncoding(this.getCharsetEncoding());
                this.serialize(writer, outputFormat);
            } catch (XMLStreamException e) {
                throw new FOMException(e);
            }
        }
    }

    public MimeType getContentType() {
        return contentType;
    }

    public Document<T> setContentType(String contentType) {
        try {
            this.contentType = new MimeType(contentType);
            if (this.contentType.getParameter("charset") != null)
                setCharset(this.contentType.getParameter("charset"));
        } catch (javax.activation.MimeTypeParseException e) {
            throw new org.apache.abdera.util.MimeTypeParseException(e);
        }
        return this;
    }

    public Date getLastModified() {
        return this.lastModified;
    }

    public Document<T> setLastModified(Date lastModified) {
        this.lastModified = lastModified;
        return this;
    }

    public Object clone() {
        Document<T> doc = getFactory().newDocument();
        OMDocument omdoc = (OMDocument)doc;
        for (Iterator i = getChildren(); i.hasNext();) {
            OMNode node = (OMNode)i.next();
            switch (node.getType()) {
                case OMNode.COMMENT_NODE:
                    OMComment comment = (OMComment)node;
                    getOMFactory().createOMComment(omdoc, comment.getValue());
                    break;
                // TODO: Decide what to do with this code; it will no longer work in Axiom 1.2.14 (because of AXIOM-437).
                //       On the other hand, since we filter out DTDs, this code is never triggered.
//                case OMNode.DTD_NODE:
//                    OMDocType doctype = (OMDocType)node;
//                    factory.createOMDocType(omdoc, doctype.getValue());
//                    break;
                case OMNode.ELEMENT_NODE:
                    Element el = (Element)node;
                    omdoc.addChild((OMNode)el.clone());
                    break;
                case OMNode.PI_NODE:
                    OMProcessingInstruction pi = (OMProcessingInstruction)node;
                    getOMFactory().createOMProcessingInstruction(omdoc, pi.getTarget(), pi.getValue());
                    break;
            }
        }
        return doc;
    }

    public String getCharset() {
        String enc = this.getXMLEncoding();
        return enc == null ? "utf-8" : enc;
    }

    public Document<T> setCharset(String charset) {
        this.setXMLEncoding(charset);
        this.setCharsetEncoding(charset);
        return this;
    }

    public String[] getProcessingInstruction(String target) {
        List<String> values = new ArrayList<String>();
        for (Iterator i = getChildren(); i.hasNext();) {
            OMNode node = (OMNode)i.next();
            if (node.getType() == OMNode.PI_NODE) {
                OMProcessingInstruction pi = (OMProcessingInstruction)node;
                if (pi.getTarget().equalsIgnoreCase(target))
                    values.add(pi.getValue());
            }
        }
        return values.toArray(new String[values.size()]);
    }

    public Document<T> addProcessingInstruction(String target, String value) {
        OMProcessingInstruction pi = this.getOMFactory().createOMProcessingInstruction(null, target, value);
        if (this.getOMDocumentElement() != null) {
            this.getOMDocumentElement().insertSiblingBefore(pi);
        } else {
            this.addChild(pi);
        }
        return this;
    }

    public Document<T> addStylesheet(String href, String media) {
        if (media == null) {
            addProcessingInstruction("xml-stylesheet", "href=\"" + href + "\"");
        } else {
            addProcessingInstruction("xml-stylesheet", "href=\"" + href + "\" media=\"" + media + "\"");
        }
        return this;
    }

    public <X extends Base> X addComment(String value) {
        OMComment comment = this.getOMFactory().createOMComment(null, value);
        if (this.getOMDocumentElement() != null) {
            this.getOMDocumentElement().insertSiblingBefore(comment);
        } else {
            this.addChild(comment);
        }
        return (X)this;
    }

    public EntityTag getEntityTag() {
        return etag;
    }

    public Document<T> setEntityTag(EntityTag tag) {
        this.etag = tag;
        return this;
    }

    public Document<T> setEntityTag(String tag) {
        this.etag = new EntityTag(tag);
        return this;
    }

    public String getLanguage() {
        return language;
    }

    public Lang getLanguageTag() {
        String lang = getLanguage();
        return (lang != null) ? new Lang(lang) : null;
    }

    public Document<T> setLanguage(String lang) {
        this.language = lang;
        return this;
    }

    public String getSlug() {
        return slug;
    }

    public Document<T> setSlug(String slug) {
        this.slug = slug;
        return this;
    }

    public boolean getMustPreserveWhitespace() {
        return preserve;
    }

    public Document<T> setMustPreserveWhitespace(boolean preserve) {
        this.preserve = preserve;
        return this;
    }

    public XMLVersion getXmlVersion() {
        return XmlUtil.getVersion(getXMLVersion());
    }

    public WriterOptions getDefaultWriterOptions() {
        return new FOMWriter().getDefaultWriterOptions();
    }

    /**
     * Ensure that the underlying streams are fully parsed. We might eventually need to find a more efficient way of
     * doing this, but for now, calling toString() will ensure that this particular object is fully parsed and ready to
     * be modified.
     */
    public <X extends Base> X complete() {
        if (!isComplete() && getRoot() != null)
            getRoot().complete();
        return (X)this;
    }

    public void writeTo(String writer, OutputStream out) throws IOException {
        writeTo(getFactory().getAbdera().getWriterFactory().getWriter(writer), out);
    }

    public void writeTo(String writer, java.io.Writer out) throws IOException {
        writeTo(getFactory().getAbdera().getWriterFactory().getWriter(writer), out);
    }

    public void writeTo(String writer, OutputStream out, WriterOptions options) throws IOException {
        writeTo(getFactory().getAbdera().getWriterFactory().getWriter(writer), out, options);
    }

    public void writeTo(String writer, java.io.Writer out, WriterOptions options) throws IOException {
        writeTo(getFactory().getAbdera().getWriterFactory().getWriter(writer), out, options);
    }

    public String toFormattedString() {
        try {
            ByteArrayOutputStream out = new ByteArrayOutputStream();
            writeTo("prettyxml", out);
            return new String(out.toByteArray(), "UTF-8");
        } catch (Exception e) {
            return toString();
        }
    }
}