Package org.apache.chemistry.opencmis.server.impl.atompub

Source Code of org.apache.chemistry.opencmis.server.impl.atompub.AtomEntryParser$LightByteArrayOutputStream

/*
* Licensed to the Apache Software Foundation (ASF) under one
* or more contributor license agreements.  See the NOTICE file
* distributed with this work for additional information
* regarding copyright ownership.  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.
*/
package org.apache.chemistry.opencmis.server.impl.atompub;

import java.io.ByteArrayInputStream;
import java.io.ByteArrayOutputStream;
import java.io.IOException;
import java.io.InputStream;
import java.io.OutputStream;
import java.math.BigInteger;
import java.util.Iterator;
import java.util.List;
import java.util.Map;

import javax.xml.bind.JAXBElement;
import javax.xml.bind.Unmarshaller;
import javax.xml.namespace.QName;
import javax.xml.stream.XMLInputFactory;
import javax.xml.stream.XMLOutputFactory;
import javax.xml.stream.XMLStreamException;
import javax.xml.stream.XMLStreamReader;
import javax.xml.stream.XMLStreamWriter;

import org.apache.chemistry.opencmis.commons.PropertyIds;
import org.apache.chemistry.opencmis.commons.data.Acl;
import org.apache.chemistry.opencmis.commons.data.ContentStream;
import org.apache.chemistry.opencmis.commons.data.ObjectData;
import org.apache.chemistry.opencmis.commons.data.Properties;
import org.apache.chemistry.opencmis.commons.data.PropertyData;
import org.apache.chemistry.opencmis.commons.data.PropertyId;
import org.apache.chemistry.opencmis.commons.data.PropertyString;
import org.apache.chemistry.opencmis.commons.exceptions.CmisInvalidArgumentException;
import org.apache.chemistry.opencmis.commons.exceptions.CmisNotSupportedException;
import org.apache.chemistry.opencmis.commons.impl.Base64;
import org.apache.chemistry.opencmis.commons.impl.Constants;
import org.apache.chemistry.opencmis.commons.impl.Converter;
import org.apache.chemistry.opencmis.commons.impl.JaxBHelper;
import org.apache.chemistry.opencmis.commons.impl.dataobjects.ContentStreamImpl;
import org.apache.chemistry.opencmis.commons.impl.dataobjects.PropertiesImpl;
import org.apache.chemistry.opencmis.commons.impl.dataobjects.PropertyStringImpl;
import org.apache.chemistry.opencmis.commons.impl.jaxb.CmisObjectType;

/**
* Parser for Atom Entries.
*/
public class AtomEntryParser {

    private final static String TAG_ENTRY = "entry";
    private final static String TAG_TITLE = "title";
    private final static String TAG_OBJECT = "object";
    private final static String TAG_CONTENT = "content";
    private final static String TAG_BASE64 = "base64";
    private final static String TAG_MEDIATYPE = "mediatype";

    private final static String ATTR_SRC = "src";
    private final static String ATTR_TYPE = "type";

    protected boolean ignoreAtomContentSrc;

    private ObjectData object;
    private ContentStreamImpl atomContentStream;
    private ContentStreamImpl cmisContentStream;

    /**
     * Constructor.
     */
    public AtomEntryParser() {
    }

    /**
     * Constructor that immediately parses the given stream.
     */
    public AtomEntryParser(InputStream stream) throws Exception {
        parse(stream);
    }

    /**
     * Sets the flag controlling whether atom content src (external content) is
     * ignored. This flag is false by default (not ignored).
     */
    public void setIgnoreAtomContentSrc(boolean ignoreAtomContentSrc) {
        this.ignoreAtomContentSrc = ignoreAtomContentSrc;
    }

    /**
     * Returns the object.
     */
    public ObjectData getObject() {
        return object;
    }

    /**
     * Returns the properties of the object.
     */
    public Properties getProperties() {
        return (object == null ? null : object.getProperties());
    }

    /**
     * Returns the Id of the object.
     */
    public String getId() {
        Properties properties = getProperties();
        if (properties == null) {
            return null;
        }

        Map<String, PropertyData<?>> propertiesMap = properties.getProperties();
        if (propertiesMap == null) {
            return null;
        }

        PropertyData<?> property = propertiesMap.get(PropertyIds.OBJECT_ID);
        if (property instanceof PropertyId) {
            return ((PropertyId) property).getFirstValue();
        }

        return null;
    }

    /**
     * Returns the ACL of the object.
     */
    public Acl getAcl() {
        return (object == null ? null : object.getAcl());
    }

    /**
     * Returns the policy id list of the object.
     */
    public List<String> getPolicyIds() {
        if ((object == null) || (object.getPolicyIds() == null)) {
            return null;
        }

        return object.getPolicyIds().getPolicyIds();
    }

    /**
     * Returns the content stream.
     */
    public ContentStream getContentStream() {
        return (cmisContentStream == null ? atomContentStream : cmisContentStream);
    }

    /**
     * Parses the stream.
     */
    public void parse(InputStream stream) throws Exception {
        object = null;
        atomContentStream = null;
        cmisContentStream = null;

        if (stream == null) {
            return;
        }

        XMLInputFactory factory = XMLInputFactory.newInstance();
        factory.setProperty(XMLInputFactory.IS_COALESCING, "false");
        XMLStreamReader parser = factory.createXMLStreamReader(stream);

        while (true) {
            int event = parser.getEventType();
            if (event == XMLStreamReader.START_ELEMENT) {
                QName name = parser.getName();

                if (Constants.NAMESPACE_ATOM.equals(name.getNamespaceURI()) && (TAG_ENTRY.equals(name.getLocalPart()))) {
                    parseEntry(parser);
                    break;
                } else {
                    throw new CmisInvalidArgumentException("XML is not an Atom entry!");
                }
            }

            if (!next(parser)) {
                break;
            }
        }

        parser.close();
    }

    /**
     * Parses an Atom entry.
     */
    private void parseEntry(XMLStreamReader parser) throws Exception {
        String atomTitle = null;

        next(parser);

        // walk through all tags in entry
        while (true) {
            int event = parser.getEventType();
            if (event == XMLStreamReader.START_ELEMENT) {
                QName name = parser.getName();

                if (Constants.NAMESPACE_RESTATOM.equals(name.getNamespaceURI())) {
                    if (TAG_OBJECT.equals(name.getLocalPart())) {
                        parseObject(parser);
                    } else if (TAG_CONTENT.equals(name.getLocalPart())) {
                        parseCmisContent(parser);
                    } else {
                        skip(parser);
                    }
                } else if (Constants.NAMESPACE_ATOM.equals(name.getNamespaceURI())) {
                    if (TAG_CONTENT.equals(name.getLocalPart())) {
                        parseAtomContent(parser);
                    } else if (TAG_TITLE.equals(name.getLocalPart())) {
                        atomTitle = readText(parser);
                    } else {
                        skip(parser);
                    }
                } else {
                    skip(parser);
                }
            } else if (event == XMLStreamReader.END_ELEMENT) {
                break;
            } else {
                if (!next(parser)) {
                    break;
                }
            }
        }

        // overwrite cmis:name with Atom title
        if ((object != null) && (object.getProperties() != null) && (atomTitle != null) && (atomTitle.length() > 0)) {
            PropertyString nameProperty = new PropertyStringImpl(PropertyIds.NAME, atomTitle);
            ((PropertiesImpl) object.getProperties()).replaceProperty(nameProperty);
        }
    }

    /**
     * Parses a CMIS object.
     */
    private void parseObject(XMLStreamReader parser) throws Exception {
        Unmarshaller u = JaxBHelper.createUnmarshaller();
        JAXBElement<CmisObjectType> jaxbObject = u.unmarshal(parser, CmisObjectType.class);

        if (jaxbObject != null) {
            object = Converter.convert(jaxbObject.getValue());
        }
    }

    /**
     * Extract the content stream.
     */
    private void parseAtomContent(XMLStreamReader parser) throws Exception {
        atomContentStream = new ContentStreamImpl();

        // read attributes
        String type = "text";
        for (int i = 0; i < parser.getAttributeCount(); i++) {
            QName attrName = parser.getAttributeName(i);
            if (ATTR_TYPE.equals(attrName.getLocalPart())) {
                atomContentStream.setMimeType(parser.getAttributeValue(i));
                if (parser.getAttributeValue(i) != null) {
                    type = parser.getAttributeValue(i).trim().toLowerCase();
                }
            } else if (ATTR_SRC.equals(attrName.getLocalPart())) {
                if (ignoreAtomContentSrc) {
                    atomContentStream = null;
                    skip(parser);
                    return;
                }
                throw new CmisNotSupportedException("External content not supported!");
            }
        }

        byte[] bytes = null;
        if (type.equals("text") || type.equals("html")) {
            bytes = readText(parser).getBytes("UTF-8");
        } else if (type.equals("xhtml")) {
            bytes = copy(parser);
        } else if (type.endsWith("/xml") || type.endsWith("+xml")) {
            bytes = copy(parser);
        } else if (type.startsWith("text/")) {
            bytes = readText(parser).getBytes("UTF-8");
        } else {
            LightByteArrayOutputStream lbs = readBase64(parser);
            atomContentStream.setStream(lbs.getInputStream());
            atomContentStream.setLength(BigInteger.valueOf(lbs.getSize()));
        }

        if (bytes != null) {
            atomContentStream.setStream(new ByteArrayInputStream(bytes));
            atomContentStream.setLength(BigInteger.valueOf(bytes.length));
        }
    }

    /**
     * Extract the content stream.
     */
    private void parseCmisContent(XMLStreamReader parser) throws Exception {
        cmisContentStream = new ContentStreamImpl();

        next(parser);

        // walk through all tags in content
        while (true) {
            int event = parser.getEventType();
            if (event == XMLStreamReader.START_ELEMENT) {
                QName name = parser.getName();

                if (Constants.NAMESPACE_RESTATOM.equals(name.getNamespaceURI())) {
                    if (TAG_MEDIATYPE.equals(name.getLocalPart())) {
                        cmisContentStream.setMimeType(readText(parser));
                    } else if (TAG_BASE64.equals(name.getLocalPart())) {
                        LightByteArrayOutputStream lbs = readBase64(parser);
                        cmisContentStream.setStream(lbs.getInputStream());
                        cmisContentStream.setLength(BigInteger.valueOf(lbs.getSize()));
                    } else {
                        skip(parser);
                    }
                } else {
                    skip(parser);
                }
            } else if (event == XMLStreamReader.END_ELEMENT) {
                break;
            } else {
                if (!next(parser)) {
                    break;
                }
            }
        }

        next(parser);
    }

    /**
     * Parses a tag that contains text.
     */
    private String readText(XMLStreamReader parser) throws Exception {
        StringBuilder sb = new StringBuilder();

        next(parser);

        while (true) {
            int event = parser.getEventType();
            if (event == XMLStreamReader.END_ELEMENT) {
                break;
            } else if (event == XMLStreamReader.CHARACTERS) {
                String s = parser.getText();
                if (s != null) {
                    sb.append(s);
                }
            } else if (event == XMLStreamReader.START_ELEMENT) {
                throw new RuntimeException("Unexpected tag: " + parser.getName());
            }

            if (!next(parser)) {
                break;
            }
        }

        next(parser);

        return sb.toString();
    }

    /**
     * Parses a tag that contains base64 encoded content.
     */
    private LightByteArrayOutputStream readBase64(XMLStreamReader parser) throws Exception {
        LightByteArrayOutputStream bufferStream = new LightByteArrayOutputStream();
        Base64.OutputStream b64stream = new Base64.OutputStream(bufferStream, Base64.DECODE);

        next(parser);

        while (true) {
            int event = parser.getEventType();
            if (event == XMLStreamReader.END_ELEMENT) {
                break;
            } else if (event == XMLStreamReader.CHARACTERS) {
                String s = parser.getText();
                if (s != null) {
                    b64stream.write(s.getBytes("US-ASCII"));
                }
            } else if (event == XMLStreamReader.START_ELEMENT) {
                throw new RuntimeException("Unexpected tag: " + parser.getName());
            }

            if (!next(parser)) {
                break;
            }
        }

        next(parser);

        return bufferStream;
    }

    /**
     * Copies a subtree into a stream.
     */
    private byte[] copy(XMLStreamReader parser) throws Exception {
        // create a writer
        ByteArrayOutputStream out = new ByteArrayOutputStream();
        XMLStreamWriter writer = XMLOutputFactory.newInstance().createXMLStreamWriter(out);

        writer.writeStartDocument();

        // copy subtree
        int level = 1;
        while (next(parser)) {
            int event = parser.getEventType();
            if (event == XMLStreamReader.START_ELEMENT) {
                copyStartElement(parser, writer);
                level++;
            } else if (event == XMLStreamReader.CHARACTERS) {
                writer.writeCharacters(parser.getText());
            } else if (event == XMLStreamReader.COMMENT) {
                writer.writeComment(parser.getText());
            } else if (event == XMLStreamReader.CDATA) {
                writer.writeCData(parser.getText());
            } else if (event == XMLStreamReader.END_ELEMENT) {
                level--;
                if (level == 0) {
                    break;
                }
                writer.writeEndElement();
            } else {
                break;
            }
        }

        writer.writeEndDocument();

        next(parser);

        return out.toByteArray();
    }

    /**
     * Copies a XML start element.
     */
    private void copyStartElement(XMLStreamReader parser, XMLStreamWriter writer) throws Exception {
        String namespaceUri = parser.getNamespaceURI();
        String prefix = parser.getPrefix();
        String localName = parser.getLocalName();

        // write start element
        if (namespaceUri != null) {
            if ((prefix == null) || (prefix.length() == 0)) {
                writer.writeStartElement(localName);
            } else {
                writer.writeStartElement(prefix, localName, namespaceUri);
            }
        } else {
            writer.writeStartElement(localName);
        }

        // set namespaces
        for (int i = 0; i < parser.getNamespaceCount(); i++) {
            addNamespace(writer, parser.getNamespacePrefix(i), parser.getNamespaceURI(i));
        }
        addNamespaceIfMissing(writer, prefix, namespaceUri);

        // write attributes
        for (int i = 0; i < parser.getAttributeCount(); i++) {
            String attrNamespaceUri = parser.getAttributeNamespace(i);
            String attrPrefix = parser.getAttributePrefix(i);
            String attrName = parser.getAttributeLocalName(i);
            String attrValue = parser.getAttributeValue(i);

            if ((attrNamespaceUri == null) || (attrNamespaceUri.trim().length() == 0)) {
                writer.writeAttribute(attrName, attrValue);
            } else if ((attrPrefix == null) || (attrPrefix.trim().length() == 0)) {
                writer.writeAttribute(attrNamespaceUri, attrName, attrValue);
            } else {
                addNamespaceIfMissing(writer, attrPrefix, attrNamespaceUri);
                writer.writeAttribute(attrPrefix, attrNamespaceUri, attrName, attrValue);
            }
        }
    }

    /**
     * Checks if the given prefix is assigned to the given namespace.
     */
    @SuppressWarnings("unchecked")
    private void addNamespaceIfMissing(XMLStreamWriter writer, String prefix, String namespaceUri) throws Exception {
        if ((namespaceUri == null) || (namespaceUri.trim().length() == 0)) {
            return;
        }

        if (prefix == null) {
            prefix = "";
        }

        Iterator<String> iter = (Iterator<String>) writer.getNamespaceContext().getPrefixes(namespaceUri);
        if (iter == null) {
            return;
        }

        while (iter.hasNext()) {
            String p = iter.next();
            if ((p != null) && (p.equals(prefix))) {
                return;
            }
        }

        addNamespace(writer, prefix, namespaceUri);
    }

    /**
     * Adds a namespace to a XML element.
     */
    private void addNamespace(XMLStreamWriter writer, String prefix, String namespaceUri) throws Exception {
        if ((prefix == null) || (prefix.trim().length() == 0)) {
            writer.setDefaultNamespace(namespaceUri);
            writer.writeDefaultNamespace(namespaceUri);
        } else {
            writer.setPrefix(prefix, namespaceUri);
            writer.writeNamespace(prefix, namespaceUri);
        }
    }

    /**
     * Skips a tag or subtree.
     */
    private void skip(XMLStreamReader parser) throws Exception {
        int level = 1;
        while (next(parser)) {
            int event = parser.getEventType();
            if (event == XMLStreamReader.START_ELEMENT) {
                level++;
            } else if (event == XMLStreamReader.END_ELEMENT) {
                level--;
                if (level == 0) {
                    break;
                }
            }
        }

        next(parser);
    }

    private boolean next(XMLStreamReader parser) throws Exception {
        if (parser.hasNext()) {
            try {
                parser.next();
            } catch (XMLStreamException e) {
                return false;
            }
            return true;
        }

        return false;
    }

    private static class LightByteArrayOutputStream extends OutputStream {
        private final static int MAX_GROW = 10 * 1024 * 1024;

        private byte[] buf = null;
        private int size = 0;

        public LightByteArrayOutputStream() {
            this(64 * 1024);
        }

        public LightByteArrayOutputStream(int initSize) {
            if (initSize < 0) {
                throw new IllegalArgumentException("Negative initial size: " + initSize);
            }
            buf = new byte[initSize];
        }

        private void expand(int i) {
            if (size + i <= buf.length) {
                return;
            }

            int newSize = ((size + i) * 2 < MAX_GROW ? (size + i) * 2 : buf.length + i + MAX_GROW);
            byte[] newbuf = new byte[newSize];
            System.arraycopy(buf, 0, newbuf, 0, size);
            buf = newbuf;
        }

        public int getSize() {
            return size;
        }

        @Override
        public void write(byte buffer[]) {
            write(buffer, 0, buffer.length);
        }

        @Override
        public synchronized void write(byte[] buffer, int offset, int len) {
            if (len == 0) {
                return;
            }

            expand(len);
            System.arraycopy(buffer, offset, buf, size, len);
            size += len;
        }

        @Override
        public void write(int oneByte) {
            if (size == buf.length) {
                expand(1);
            }

            buf[size++] = (byte) oneByte;
        }

        public InputStream getInputStream() {
            return new InputStream() {

                private int pos = 0;

                @Override
                public int available() {
                    return size - pos;
                }

                @Override
                public int read() {
                    return (pos < size) && (buf != null) ? (buf[pos++] & 0xff) : -1;
                }

                @Override
                public int read(byte[] b, int off, int len) {
                    if ((pos >= size) || (buf == null)) {
                        return -1;
                    }

                    if ((pos + len) > size) {
                        len = (size - pos);
                    }

                    System.arraycopy(buf, pos, b, off, len);
                    pos += len;

                    return len;
                }

                @Override
                public long skip(long n) {
                    if ((pos + n) > size) {
                        n = size - pos;
                    }

                    if (n < 0) {
                        return 0;
                    }

                    pos += n;

                    return n;
                }

                @Override
                public void close() throws IOException {
                    buf = null;
                }
            };
        }
    }
}
TOP

Related Classes of org.apache.chemistry.opencmis.server.impl.atompub.AtomEntryParser$LightByteArrayOutputStream

TOP
Copyright © 2018 www.massapi.com. All rights reserved.
All source code are property of their respective owners. Java is a trademark of Sun Microsystems, Inc and owned by ORACLE Inc. Contact coftware#gmail.com.