com.quinsoft.zeidon.standardoe.ActivateOisFromXmlStream.java Source code

Java tutorial

Introduction

Here is the source code for com.quinsoft.zeidon.standardoe.ActivateOisFromXmlStream.java

Source

/**
This file is part of the Zeidon Java Object Engine (Zeidon JOE).
    
Zeidon JOE is free software: you can redistribute it and/or modify
it under the terms of the GNU Lesser General Public License as published by
the Free Software Foundation, either version 3 of the License, or
(at your option) any later version.
    
Zeidon JOE 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 Zeidon JOE.  If not, see <http://www.gnu.org/licenses/>.
    
Copyright 2009-2015 QuinSoft
 */
package com.quinsoft.zeidon.standardoe;

import java.io.InputStream;
import java.util.ArrayList;
import java.util.EnumSet;
import java.util.List;
import java.util.Stack;

import javax.xml.parsers.SAXParser;
import javax.xml.parsers.SAXParserFactory;

import org.apache.commons.lang3.StringUtils;
import org.xml.sax.Attributes;
import org.xml.sax.Locator;
import org.xml.sax.SAXException;
import org.xml.sax.helpers.AttributesImpl;
import org.xml.sax.helpers.DefaultHandler;

import com.quinsoft.zeidon.ActivateFlags;
import com.quinsoft.zeidon.Application;
import com.quinsoft.zeidon.CreateEntityFlags;
import com.quinsoft.zeidon.CursorPosition;
import com.quinsoft.zeidon.DeserializeOi;
import com.quinsoft.zeidon.StreamReader;
import com.quinsoft.zeidon.Task;
import com.quinsoft.zeidon.View;
import com.quinsoft.zeidon.ZeidonException;
import com.quinsoft.zeidon.objectdefinition.AttributeDef;
import com.quinsoft.zeidon.objectdefinition.EntityDef;
import com.quinsoft.zeidon.objectdefinition.LodDef;

/**
 * @author dgc
 *
 */
class ActivateOisFromXmlStream implements StreamReader {
    private static final EnumSet<CreateEntityFlags> CREATE_FLAGS = EnumSet.of(CreateEntityFlags.fNO_SPAWNING,
            CreateEntityFlags.fIGNORE_MAX_CARDINALITY, CreateEntityFlags.fDONT_UPDATE_OI,
            CreateEntityFlags.fDONT_INITIALIZE_ATTRIBUTES, CreateEntityFlags.fIGNORE_PERMISSIONS);

    private Task task;
    private InputStream inputStream;
    private boolean ignoreInvalidEntityNames;
    private boolean ignoreInvalidAttributeNames;

    private Application application;
    private LodDef lodDef;

    /**
     * Current view being read.
     */
    private ViewImpl view;

    /**
     * List of returned views.
     */
    private final List<View> viewList = new ArrayList<>();

    private EnumSet<ActivateFlags> control;
    private boolean incremental = false;
    private final Stack<Attributes> entityAttributes = new Stack<Attributes>();
    private final Stack<Attributes> attributeAttributes = new Stack<Attributes>();
    private final Stack<EntityDef> currentEntityStack = new Stack<EntityDef>();
    private EntityDef currentEntityDef;
    private StringBuilder characterBuffer;

    /**
     * Used to keep track of the instances that are flagged as selected in the input
     * stream.  Cursors will be set afterwards.
     */
    private List<EntityInstanceImpl> selectedInstances;

    /**
     * Keeps track of current location in SAX parser.
     */
    private Locator locator;

    private ViewImpl read() {
        try {
            SAXParserFactory factory = SAXParserFactory.newInstance();
            SAXParser saxParser = factory.newSAXParser();
            DefaultHandler handler = new SaxParserHandler();
            saxParser.parse(inputStream, handler);

            // If user wanted just one root remove others if we have more than one.
            // We don't want to abort the loading of entities in the middle of
            // the stream because that could throw off XML processing.
            EntityCursorImpl rootCursor = view.getViewCursor().getEntityCursor(lodDef.getRoot());
            if (control.contains(ActivateFlags.fSINGLE) && rootCursor.getEntityCount() > 1) {
                rootCursor.setFirst();
                while (rootCursor.setNext().isSet())
                    rootCursor.dropEntity();
                rootCursor.setFirst();
            }

            if (selectedInstances.size() > 0)
                setCursors();
            else
                view.reset();

            return view;
        } catch (Exception e) {
            ZeidonException ze = ZeidonException.wrapException(e);
            if (locator != null)
                ze.appendMessage("Line/col = %d/%d", locator.getLineNumber(), locator.getColumnNumber());

            throw ze;
        }
    }

    private boolean isYes(String str) {
        if (StringUtils.isBlank(str))
            return false;

        switch (str.toUpperCase().charAt(0)) {
        case 'Y':
        case '1':
        case 'T':
            return true;

        default:
            return false;
        }
    }

    /**
     * The view has been loaded from the stream and it was indicated that there are
     * cursor selections.  Reset them.
     */
    private void setCursors() {
        for (EntityInstanceImpl ei : selectedInstances) {
            EntityDef entityDef = ei.getEntityDef();
            EntityCursorImpl cursor = view.cursor(entityDef);

            // Use setEntityInstance() because we are setting all cursors.  This is
            // faster than using setCursor().
            cursor.setEntityInstance(ei);
        }
    }

    /**
     * Called to handle the zOI entity.
     *
     * @param qName
     * @param attributes
     */
    private void createOi(String qName, Attributes attributes) {
        String appName = attributes.getValue("appName");
        if (StringUtils.isBlank(appName))
            throw new ZeidonException("zOI element does not specify appName");

        application = task.getApplication(appName);
        String odName = attributes.getValue("objectName");
        if (StringUtils.isBlank(odName))
            throw new ZeidonException("zOI element does not specify objectName");

        lodDef = application.getLodDef(task, odName);
        view = (ViewImpl) task.activateEmptyObjectInstance(lodDef);
        viewList.add(view);

        String increFlags = attributes.getValue("increFlags");
        if (!StringUtils.isBlank(increFlags))
            incremental = isYes(increFlags);

        String rootCount = attributes.getValue("totalRootCount");
        if (!StringUtils.isBlank(rootCount))
            view.setTotalRootCount(Integer.parseInt(rootCount));

        // Create a list to keep track of selected instances.
        selectedInstances = new ArrayList<>();
    }

    private void createEntity(String entityName, Attributes attributes) {
        currentEntityDef = lodDef.getEntityDef(entityName);
        currentEntityStack.push(currentEntityDef);
        EntityCursorImpl cursor = view.cursor(currentEntityDef);
        cursor.createEntity(CursorPosition.LAST, CREATE_FLAGS);

        // If we're setting incremental flags, save them for later.  Create
        // a copy of the AttributesImpl because the original gets reused.
        if (incremental)
            entityAttributes.push(new AttributesImpl(attributes));
    }

    private void setAttribute(String attributeName, Attributes attributes) {
        characterBuffer = new StringBuilder();

        if (incremental)
            attributeAttributes.push(attributes);
    }

    private class SaxParserHandler extends DefaultHandler {
        // this will be called when XML-parser starts reading
        // XML-data; here we save reference to current position in XML:
        @Override
        public void setDocumentLocator(Locator locator) {
            ActivateOisFromXmlStream.this.locator = locator;
        }

        @Override
        public void startElement(String uri, String localName, String qName, Attributes attributes)
                throws SAXException {
            if (StringUtils.equalsIgnoreCase(qName, "zOIs")) {
                // Nothing to do.
                return;
            }

            if (StringUtils.equalsIgnoreCase(qName, "zOI")) {
                createOi(qName, attributes);
                return;
            }

            // If we get here then we better have a view.
            if (view == null)
                throw new ZeidonException("XML stream does not specify zOI element");

            if (currentEntityDef != null && currentEntityDef.getAttribute(qName, false) != null) {
                setAttribute(qName, attributes);
                return;
            }

            // Is the element name an entity name?
            if (lodDef.getEntityDef(qName, false) != null) {
                createEntity(qName, attributes);
                return;
            }

            // If we get here then we don't know what we have.  If user has specified
            // that we're to ignore entity or attribute errors then we'll just assume that
            // this element is an old entity/attribute name and we can ignore it.
            if (ignoreInvalidAttributeNames || ignoreInvalidEntityNames)
                return;

            throw new ZeidonException("Unknown XML element: %s", qName);
        }

        @Override
        public void endElement(String uri, String localName, String qName) throws SAXException {
            // Is the element an attribute name?
            AttributeDef attributeDef = currentEntityDef.getAttribute(qName, false);
            if (attributeDef != null) {
                if (characterBuffer == null) {
                    // If we get here then we should be in a situation where an attribute name
                    // is the same as its containing entity.  We've already read the attribute
                    // so qName should be the entity name.  Verify.
                    assert lodDef.getEntityDef(qName, false) != null : "Unexpected null characterBuffer";
                } else {
                    EntityInstanceImpl ei = view.cursor(attributeDef.getEntityDef()).getEntityInstance();
                    ei.getAttribute(attributeDef).setInternalValue(characterBuffer.toString(),
                            !attributeDef.isKey());

                    characterBuffer = null; // Indicates we've read the attribute.

                    if (incremental) {
                        Attributes attributes = attributeAttributes.pop();
                        ei.getAttribute(attributeDef).setIsUpdated(isYes(attributes.getValue("updated")));
                    } else {
                        // If we just set the key then we'll assume the entity has
                        // already been created.
                        if (attributeDef.isKey())
                            ei.setIncrementalFlags(IncrementalEntityFlags.UPDATED);
                    }

                    return;
                }
            }

            // Is the element name an entity name?
            if (lodDef.getEntityDef(qName, false) != null) {
                assert qName.equals(currentEntityDef.getName()) : "Mismatching entity names in XML";

                if (incremental) {
                    EntityInstanceImpl ei = view.cursor(qName).getEntityInstance();
                    Attributes attributes = entityAttributes.pop();

                    ei.setUpdated(isYes(attributes.getValue("updated")));
                    ei.setCreated(isYes(attributes.getValue("created")));
                    ei.setIncluded(isYes(attributes.getValue("included")));
                    ei.setExcluded(isYes(attributes.getValue("excluded")));
                    ei.setDeleted(isYes(attributes.getValue("deleted")));
                    if (isYes(attributes.getValue("incomplete")))
                        ei.setIncomplete(null);
                    if (isYes(attributes.getValue("selected")))
                        selectedInstances.add(ei);
                    String lazyLoaded = attributes.getValue("lazyLoaded");
                    if (!StringUtils.isBlank(lazyLoaded)) {
                        String[] names = lazyLoaded.split(",");
                        for (String name : names)
                            ei.getEntitiesLoadedLazily().add(lodDef.getEntityDef(name));
                    }
                }

                // The top of the stack equals currentEntityDef.  Pop it off the stack and
                // set currentEntityDef to the next item in the stack.
                currentEntityStack.pop();
                if (currentEntityDef.getParent() != null) // Is it root?
                    currentEntityDef = currentEntityStack.peek();
                return;
            }

            if (StringUtils.equalsIgnoreCase(qName, "zOI")) {
                return;
            }

            if (StringUtils.equalsIgnoreCase(qName, "zOIs")) {
                return;
            }

            throw new ZeidonException("Unexpected qname: %s", qName);
        } // endElement

        @Override
        public void characters(char[] ch, int start, int length) throws SAXException {
            if (characterBuffer == null)
                return; // We don't need to capture the characters.

            characterBuffer.append(ch, start, length);
        }
    } // class SaxParserHandler

    @Override
    public List<View> readFromStream(DeserializeOi options) {
        this.task = options.getTask();
        control = options.getFlags();
        this.inputStream = options.getInputStream();
        ;
        ignoreInvalidEntityNames = control.contains(ActivateFlags.fIGNORE_ENTITY_ERRORS);
        ignoreInvalidAttributeNames = control.contains(ActivateFlags.fIGNORE_ATTRIB_ERRORS);
        read();
        return viewList;
    }
}