Package org.jboss.dna.jcr

Source Code of org.jboss.dna.jcr.JcrContentHandler

/*
* JBoss DNA (http://www.jboss.org/dna)
* See the COPYRIGHT.txt file distributed with this work for information
* regarding copyright ownership.  Some portions may be licensed
* to Red Hat, Inc. under one or more contributor license agreements.
* See the AUTHORS.txt file in the distribution for a full listing of
* individual contributors.
*
* JBoss DNA is free software. Unless otherwise indicated, all code in JBoss DNA
* is licensed to you under the terms of the GNU Lesser General Public License as
* published by the Free Software Foundation; either version 2.1 of
* the License, or (at your option) any later version.
*
* JBoss DNA is distributed in the hope that it will be useful,
* but WITHOUT ANY WARRANTY; without even the implied warranty of
* MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU
* Lesser General Public License for more details.
*
* You should have received a copy of the GNU Lesser General Public
* License along with this software; if not, write to the Free
* Software Foundation, Inc., 51 Franklin St, Fifth Floor, Boston, MA
* 02110-1301 USA, or see the FSF site: http://www.fsf.org.
*/
package org.jboss.dna.jcr;

import java.io.ByteArrayInputStream;
import java.io.InputStream;
import java.util.ArrayList;
import java.util.HashMap;
import java.util.List;
import java.util.Map;
import java.util.Stack;
import java.util.UUID;
import javax.jcr.ImportUUIDBehavior;
import javax.jcr.ItemExistsException;
import javax.jcr.ItemNotFoundException;
import javax.jcr.PathNotFoundException;
import javax.jcr.PropertyType;
import javax.jcr.RepositoryException;
import javax.jcr.Value;
import javax.jcr.ValueFactory;
import javax.jcr.ValueFormatException;
import javax.jcr.nodetype.ConstraintViolationException;
import net.jcip.annotations.NotThreadSafe;
import org.jboss.dna.common.text.TextDecoder;
import org.jboss.dna.common.text.XmlNameEncoder;
import org.jboss.dna.common.util.Base64;
import org.jboss.dna.graph.ExecutionContext;
import org.jboss.dna.graph.Location;
import org.jboss.dna.graph.property.Name;
import org.jboss.dna.graph.property.NameFactory;
import org.jboss.dna.graph.property.NamespaceRegistry;
import org.jboss.dna.graph.property.Path;
import org.xml.sax.Attributes;
import org.xml.sax.ContentHandler;
import org.xml.sax.SAXException;
import org.xml.sax.helpers.DefaultHandler;

/**
* Content handler that provides SAX-based event handling that maps incoming documents to the repository based on the
* functionality described in section 7.3 of the JCR 1.0.1 specification.
* <p>
* Each content handler is only intended to be used once and discarded. This class is <b>NOT</b> thread-safe.
* </p>
*
* @see JcrSession#getImportContentHandler(String, int)
* @see JcrWorkspace#getImportContentHandler(String, int)
*/
@NotThreadSafe
class JcrContentHandler extends DefaultHandler {

    /**
     * Encoder to properly escape XML names.
     *
     * @see XmlNameEncoder
     */
    protected static final TextDecoder SYSTEM_VIEW_NAME_DECODER = new XmlNameEncoder();

    protected static final TextDecoder DOCUMENT_VIEW_NAME_DECODER = new JcrDocumentViewExporter.JcrDocumentViewPropertyEncoder();

    private final NameFactory nameFactory;
    private final NamespaceRegistry namespaces;
    private final ValueFactory jcrValueFactory;
    private final JcrNodeTypeManager nodeTypes;
    private final javax.jcr.NamespaceRegistry jcrNamespaceRegistry;
    private final SaveMode saveMode;
    protected final int uuidBehavior;

    protected final String primaryTypeName;
    protected final String mixinTypesName;
    protected final String uuidName;

    private AbstractJcrNode currentNode;
    private ContentHandler delegate;

    private SessionCache cache;

    enum SaveMode {
        WORKSPACE,
        SESSION
    }

    JcrContentHandler( JcrSession session,
                       Path parentPath,
                       int uuidBehavior,
                       SaveMode saveMode ) throws PathNotFoundException, RepositoryException {
        assert session != null;
        assert parentPath != null;
        assert uuidBehavior == ImportUUIDBehavior.IMPORT_UUID_CREATE_NEW
               || uuidBehavior == ImportUUIDBehavior.IMPORT_UUID_COLLISION_REMOVE_EXISTING
               || uuidBehavior == ImportUUIDBehavior.IMPORT_UUID_COLLISION_REPLACE_EXISTING
               || uuidBehavior == ImportUUIDBehavior.IMPORT_UUID_COLLISION_THROW;

        ExecutionContext context = session.getExecutionContext();
        this.namespaces = context.getNamespaceRegistry();
        this.nameFactory = context.getValueFactories().getNameFactory();
        this.uuidBehavior = uuidBehavior;

        this.saveMode = saveMode;
        switch (this.saveMode) {
            case SESSION:
                cache = session.cache();
                break;
            case WORKSPACE:
                cache = new SessionCache(session);
                break;
        }
        assert cache != null;

        try {
            this.currentNode = cache.findJcrNode(null, parentPath);
        } catch (ItemNotFoundException e) {
            throw new PathNotFoundException(e.getLocalizedMessage(), e);
        }
        this.jcrValueFactory = session.getValueFactory();
        this.nodeTypes = session.nodeTypeManager();
        this.jcrNamespaceRegistry = session.workspace().getNamespaceRegistry();

        this.primaryTypeName = JcrLexicon.PRIMARY_TYPE.getString(this.namespaces);
        this.mixinTypesName = JcrLexicon.MIXIN_TYPES.getString(this.namespaces);
        this.uuidName = JcrLexicon.UUID.getString(this.namespaces);
    }

    protected final NamespaceRegistry namespaces() {
        return namespaces;
    }

    protected final JcrNodeTypeManager nodeTypes() {
        return nodeTypes;
    }

    protected final JcrNodeType nodeTypeFor( String name ) {
        return nodeTypes.getNodeType(nameFor(name));
    }

    protected final Name nameFor( String name ) {
        return nameFactory.create(name);
    }

    protected final Value valueFor( String value,
                                    int type ) throws ValueFormatException {
        return jcrValueFactory.createValue(value, type);
    }

    protected final Value valueFor( InputStream stream ) {
        return jcrValueFactory.createValue(stream);
    }

    protected final SessionCache cache() {
        return cache;
    }

    /**
     * {@inheritDoc}
     *
     * @see org.xml.sax.ContentHandler#characters(char[], int, int)
     */
    @Override
    public void characters( char[] ch,
                            int start,
                            int length ) throws SAXException {
        assert this.delegate != null;
        delegate.characters(ch, start, length);
    }

    /**
     * {@inheritDoc}
     *
     * @see org.xml.sax.helpers.DefaultHandler#endDocument()
     */
    @Override
    public void endDocument() throws SAXException {
        if (saveMode == SaveMode.WORKSPACE) {
            try {
                cache.save();
            } catch (RepositoryException e) {
                throw new EnclosingSAXException(e);
            }
        }
        super.endDocument();
    }

    /**
     * {@inheritDoc}
     *
     * @see org.xml.sax.ContentHandler#endElement(java.lang.String, java.lang.String, java.lang.String)
     */
    @Override
    public void endElement( String uri,
                            String localName,
                            String name ) throws SAXException {
        assert this.delegate != null;
        delegate.endElement(uri, localName, name);
    }

    /**
     * {@inheritDoc}
     *
     * @see org.xml.sax.ContentHandler#startElement(java.lang.String, java.lang.String, java.lang.String, org.xml.sax.Attributes)
     */
    @Override
    public void startElement( String uri,
                              String localName,
                              String name,
                              Attributes atts ) throws SAXException {
        checkDelegate(uri);
        assert this.delegate != null;

        delegate.startElement(uri, localName, name, atts);
    }

    private void checkDelegate( String namespaceUri ) {
        if (delegate != null) return;

        if (JcrSvLexicon.Namespace.URI.equals(namespaceUri)) {
            this.delegate = new SystemViewContentHandler(this.currentNode);
        } else {
            this.delegate = new DocumentViewContentHandler(this.currentNode);
        }
    }

    /**
     * {@inheritDoc}
     *
     * @see org.xml.sax.ContentHandler#startPrefixMapping(java.lang.String, java.lang.String)
     */
    @Override
    public void startPrefixMapping( String prefix,
                                    String uri ) throws SAXException {
        try {
            // Read from the workspace's DNA registry, as its semantics are more friendly
            String existingUri = namespaces.getNamespaceForPrefix(prefix);

            if (existingUri != null) {
                if (existingUri.equals(uri)) {
                    // prefix/uri mapping is already in registry
                    return;
                }
                throw new RepositoryException("Prefix " + prefix + " is already permanently mapped");
            }
            // Register through the JCR workspace to ensure consistency
            this.jcrNamespaceRegistry.registerNamespace(prefix, uri);
        } catch (RepositoryException re) {
            throw new EnclosingSAXException(re);
        }
    }

    class EnclosingSAXException extends SAXException {

        /**
         */
        private static final long serialVersionUID = -1044992767566435542L;

        /**
         * @param e
         */
        EnclosingSAXException( Exception e ) {
            super(e);

        }

    }

    private class SystemViewContentHandler extends DefaultHandler {
        private final Stack<AbstractJcrNode> parentStack;

        private final String svNameName;
        private final String svTypeName;

        private String currentNodeName;
        private String currentPropName;
        private int currentPropType;

        private StringBuffer valueBuffer;
        private final Map<String, List<Value>> currentProps;

        /**
         * @param currentNode
         */
        SystemViewContentHandler( AbstractJcrNode currentNode ) {
            super();
            this.parentStack = new Stack<AbstractJcrNode>();
            this.parentStack.push(currentNode);

            this.currentProps = new HashMap<String, List<Value>>();
            this.valueBuffer = new StringBuffer();

            this.svNameName = JcrSvLexicon.NAME.getString(namespaces());
            this.svTypeName = JcrSvLexicon.TYPE.getString(namespaces());
        }

        /**
         * {@inheritDoc}
         *
         * @see org.xml.sax.ContentHandler#startElement(java.lang.String, java.lang.String, java.lang.String,
         *      org.xml.sax.Attributes)
         */
        @Override
        public void startElement( String uri,
                                  String localName,
                                  String name,
                                  Attributes atts ) throws SAXException {
            if ("node".equals(localName)) {
                if (currentNodeName != null) {
                    addNodeIfPending();
                }

                currentNodeName = atts.getValue(SYSTEM_VIEW_NAME_DECODER.decode(svNameName));
            } else if ("property".equals(localName)) {
                currentPropName = atts.getValue(SYSTEM_VIEW_NAME_DECODER.decode(svNameName));
                currentPropType = PropertyType.valueFromName(atts.getValue(svTypeName));
                currentProps.put(currentPropName, new ArrayList<Value>());
            } else if (!"value".equals(localName)) {
                throw new IllegalStateException("Unexpected element '" + name + "' in system view");
            }
        }

        private void addNodeIfPending() throws SAXException {
            if (currentNodeName != null) {
                try {
                    AbstractJcrNode parentNode = parentStack.peek();

                    UUID uuid = null;
                    List<Value> rawUuid = currentProps.get(uuidName);

                    if (rawUuid != null) {
                        assert rawUuid.size() == 1;
                        uuid = UUID.fromString(rawUuid.get(0).getString());
                       
                        try {
                            // Deal with any existing node ...
                            AbstractJcrNode existingNodeWithUuid = cache().findJcrNode(Location.create(uuid));
                            switch (uuidBehavior) {
                                case ImportUUIDBehavior.IMPORT_UUID_COLLISION_REPLACE_EXISTING:
                                    parentNode = existingNodeWithUuid.getParent();
                                    existingNodeWithUuid.remove();
                                    break;
                                case ImportUUIDBehavior.IMPORT_UUID_CREATE_NEW:
                                    uuid = UUID.randomUUID();
                                    break;
                                case ImportUUIDBehavior.IMPORT_UUID_COLLISION_REMOVE_EXISTING:
                                    if (existingNodeWithUuid.path().isAtOrAbove(parentStack.firstElement().path())) {
                                        throw new ConstraintViolationException(
                                                                               JcrI18n.cannotRemoveParentNodeOfTarget.text(existingNodeWithUuid.getPath(),
                                                                                                                           uuid,
                                                                                                                           parentStack.firstElement().getPath()));
                                    }
                                    existingNodeWithUuid.remove();
                                    break;
                                case ImportUUIDBehavior.IMPORT_UUID_COLLISION_THROW:
                                    throw new ItemExistsException(
                                                                  JcrI18n.itemAlreadyExistsWithUuid.text(uuid,
                                                                                                         cache().session().workspace().getName(),
                                                                                                         existingNodeWithUuid.getPath()));
                            }
                        } catch (ItemNotFoundException e) {
                            // there wasn't an existing item, so just continue
                        }
                       
                    }

                    String typeName = currentProps.get(primaryTypeName).get(0).getString();
                    AbstractJcrNode newNode = parentNode.editor().createChild(nameFor(currentNodeName), uuid, nameFor(typeName));
                    SessionCache.NodeEditor newNodeEditor = newNode.editor();

                    for (Map.Entry<String, List<Value>> entry : currentProps.entrySet()) {
                        if (entry.getKey().equals(primaryTypeName)) {
                            continue;
                        }

                        if (entry.getKey().equals(mixinTypesName)) {
                            for (Value value : entry.getValue()) {
                                JcrNodeType mixinType = nodeTypeFor(value.getString());
                                newNodeEditor.addMixin(mixinType);
                            }
                            continue;
                        }

                        if (entry.getKey().equals(uuidName)) {
                            continue;
                        }

                        List<Value> values = entry.getValue();

                        if (values.size() == 1) {
                            newNodeEditor.setProperty(nameFor(entry.getKey()), (JcrValue)values.get(0));
                        } else {
                            newNodeEditor.setProperty(nameFor(entry.getKey()),
                                                      values.toArray(new Value[values.size()]),
                                                      PropertyType.UNDEFINED);
                        }
                    }

                    parentStack.push(newNode);
                    currentProps.clear();
                } catch (RepositoryException re) {
                    throw new EnclosingSAXException(re);
                }
            }
        }

        @Override
        public void endElement( String uri,
                                String localName,
                                String name ) throws SAXException {
            if ("node".equals(localName)) {
                addNodeIfPending();
                currentNodeName = null;
                parentStack.pop();
            } else if ("value".equals(localName)) {
                String s = valueBuffer.toString();
                try {
                    if (currentPropType == PropertyType.BINARY) {
                        ByteArrayInputStream is = new ByteArrayInputStream(Base64.decode(s, Base64.URL_SAFE));
                        currentProps.get(currentPropName).add(valueFor(is));
                    } else {
                        currentProps.get(currentPropName).add(valueFor(SYSTEM_VIEW_NAME_DECODER.decode(s), currentPropType));
                    }
                } catch (RepositoryException re) {
                    throw new EnclosingSAXException(re);
                }
                valueBuffer = new StringBuffer();
            }
        }

        /**
         * {@inheritDoc}
         *
         * @see org.xml.sax.ContentHandler#characters(char[], int, int)
         */
        @Override
        public void characters( char[] ch,
                                int start,
                                int length ) {
            valueBuffer.append(ch, start, length);

        }
    }

    private class DocumentViewContentHandler extends DefaultHandler {
        private final Stack<AbstractJcrNode> parentStack;

        /**
         * @param currentNode
         */
        DocumentViewContentHandler( AbstractJcrNode currentNode ) {
            super();
            this.parentStack = new Stack<AbstractJcrNode>();
            parentStack.push(currentNode);
        }

        /**
         * {@inheritDoc}
         *
         * @see org.xml.sax.ContentHandler#startElement(java.lang.String, java.lang.String, java.lang.String,
         *      org.xml.sax.Attributes)
         */
        @Override
        public void startElement( String uri,
                                  String localName,
                                  String name,
                                  Attributes atts ) throws SAXException {
            try {
                String primaryTypeName = atts.getValue(JcrContentHandler.this.primaryTypeName);
                String rawUuid = atts.getValue(uuidName);
                UUID uuid = (rawUuid != null ? UUID.fromString(rawUuid) : null);
                AbstractJcrNode parentNode = parentStack.peek();

                if (uuid != null) {
                    try {
                        // Deal with any existing node ...
                        AbstractJcrNode existingNodeWithUuid = cache().findJcrNode(Location.create(uuid));
                        switch (uuidBehavior) {
                            case ImportUUIDBehavior.IMPORT_UUID_COLLISION_REPLACE_EXISTING:
                                parentNode = existingNodeWithUuid.getParent();
                                existingNodeWithUuid.remove();
                                break;
                            case ImportUUIDBehavior.IMPORT_UUID_CREATE_NEW:
                                uuid = UUID.randomUUID();
                                break;
                            case ImportUUIDBehavior.IMPORT_UUID_COLLISION_REMOVE_EXISTING:
                                if (existingNodeWithUuid.path().isAtOrAbove(parentStack.firstElement().path())) {
                                    throw new ConstraintViolationException();
                                }
                                existingNodeWithUuid.remove();
                                break;
                            case ImportUUIDBehavior.IMPORT_UUID_COLLISION_THROW:
                                throw new ItemExistsException();
                        }
                    } catch (ItemNotFoundException e) {
                        // there wasn't an existing item, so just continue
                    }
                }

                name = DOCUMENT_VIEW_NAME_DECODER.decode(name);
                AbstractJcrNode currentNode = parentNode.editor().createChild(nameFor(name), uuid, nameFor(primaryTypeName));
                SessionCache.NodeEditor currentNodeEditor = currentNode.editor();

                for (int i = 0; i < atts.getLength(); i++) {
                    if (JcrContentHandler.this.primaryTypeName.equals(atts.getQName(i))) {
                        continue;
                    }

                    if (mixinTypesName.equals(atts.getQName(i))) {
                        JcrNodeType mixinType = nodeTypeFor(atts.getValue(i));
                        currentNodeEditor.addMixin(mixinType);
                        continue;
                    }

                    if (uuidName.equals(atts.getQName(i))) {
                        continue;
                    }

                    // We may want to use the workspace context here so that we only use the permanent namespace mappings
                    // Name propName = session.executionContext.getValueFactories().getNameFactory().create(atts.getQName(i));
                    // String value = DOCUMENT_VIEW_NAME_DECODER.decode(atts.getValue(i));
                    String value = atts.getValue(i);
                    String propertyName = DOCUMENT_VIEW_NAME_DECODER.decode(atts.getQName(i));
                    currentNodeEditor.setProperty(nameFor(propertyName), (JcrValue)valueFor(value, PropertyType.STRING));
                }

                parentStack.push(currentNode);
            } catch (RepositoryException re) {
                throw new EnclosingSAXException(re);
            }
        }

        @Override
        public void endElement( String uri,
                                String localName,
                                String name ) {
            parentStack.pop();
        }

        /**
         * {@inheritDoc}
         *
         * @see org.xml.sax.ContentHandler#characters(char[], int, int)
         */
        @Override
        public void characters( char[] ch,
                                int start,
                                int length ) throws SAXException {
            try {
                AbstractJcrNode parentNode = parentStack.peek();
                AbstractJcrNode currentNode = parentNode.editor()
                                                        .createChild(JcrLexicon.XMLTEXT, null, JcrNtLexicon.UNSTRUCTURED);
                String s = new String(ch, start, length);
                currentNode.editor().setProperty(JcrLexicon.XMLCHARACTERS, (JcrValue)valueFor(s, PropertyType.STRING));

            } catch (RepositoryException re) {
                throw new EnclosingSAXException(re);
            }
        }
    }
}
TOP

Related Classes of org.jboss.dna.jcr.JcrContentHandler

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.