com.quinsoft.zeidon.objectdefinition.ViewEntity.java Source code

Java tutorial

Introduction

Here is the source code for com.quinsoft.zeidon.objectdefinition.ViewEntity.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-2012 QuinSoft
 */
/**
 *
 */
package com.quinsoft.zeidon.objectdefinition;

import java.util.ArrayList;
import java.util.Collection;
import java.util.Collections;
import java.util.HashMap;
import java.util.List;
import java.util.Map;
import java.util.concurrent.ConcurrentMap;

import org.apache.commons.lang3.StringUtils;

import com.google.common.collect.MapMaker;
import com.quinsoft.zeidon.CacheMap;
import com.quinsoft.zeidon.EventListener;
import com.quinsoft.zeidon.UnknownViewAttributeException;
import com.quinsoft.zeidon.ZeidonException;
import com.quinsoft.zeidon.objectdefinition.LazyLoadConfig.LazyLoadFlags;
import com.quinsoft.zeidon.utils.CacheMapImpl;
import com.quinsoft.zeidon.utils.EventStackTrace;
import com.quinsoft.zeidon.utils.PortableFileReader;
import com.quinsoft.zeidon.utils.PortableFileReader.PortableFileAttributeHandler;

/**
 * @author DG
 *
 */
public class ViewEntity implements PortableFileAttributeHandler, CacheMap {
    private ViewOd viewOd;
    private ViewEntity prevHier;
    private ViewEntity nextHier;
    private ViewEntity parent;
    private ViewEntity prevSibling;
    private ViewEntity nextSibling;
    private String name;
    private int erEntityToken;
    private int erRelToken;
    private boolean erRelLink; // RelLink direction.  True = '1' from the XOD file.
    private final int level;
    private final int entityNumber;
    private List<ViewEntity> children;
    private List<ViewEntity> childrenHier;
    private EventListener eventListener;
    private ArrayList<ViewAttribute> activateOrdering;
    private Integer activateLimit;
    /**
     * List of the attributes in the order they are defined in the XOD file.
     */
    private final List<ViewAttribute> attributes = new ArrayList<ViewAttribute>();

    /**
     * Map of attributes by attribute name.
     */
    private final Map<String, ViewAttribute> attributeMap = new HashMap<String, ViewAttribute>();

    /**
     * Map of attributes by ER attribute token.
     */
    private final Map<Long, ViewAttribute> erAttributeMap = new HashMap<Long, ViewAttribute>();

    /**
     * This map keeps track of entities that have been checked to see if 'this' entity
     * is an attribute superset.
     *    Key = View entity of 'child' entity.
     *  Value = true if 'this' entity is a superset.
     *
     *  This is maintained at run-time which is why we need it to be a concurrent map.
     */
    private final ConcurrentMap<ViewEntity, Boolean> attributeSuperset = new MapMaker().concurrencyLevel(2)
            .weakKeys().makeMap();

    private final CacheMap cacheMap = new CacheMapImpl();

    // Permission flags.
    private boolean create = false;
    private boolean include = false;
    private boolean exclude = false;
    private boolean delete = false;
    private boolean update = false;
    private boolean includeSrc = false;

    private final List<ViewAttribute> keys;
    private ViewAttribute genKey;
    private ViewAttribute autoSeq;
    private Collection<ViewAttribute> hashKeyAttributes;
    private boolean parentDelete = false;
    private boolean restrictParentDelete = false;
    private boolean checkRestrictedDelete = false;
    private boolean derived = false;
    private boolean derivedPath = false;
    private boolean recursive = false;
    private boolean recursiveParent = false;
    private boolean duplicateEntity = false;
    private int persistentAttributeCount;
    private int workAttributeCount;
    private DataRecord dataRecord;
    private ViewEntity recursiveParentViewEntity = null;
    private int minCardinality;
    private int maxcardinality;
    private boolean hasInitializedAttributes = false;

    // Flags to help debug OI.
    private boolean debugIncrementalFlag; // If true, then pop up a message when we change an incremental flag.

    private final LazyLoadConfig lazyLoadConfig;

    public ViewEntity(ViewOd viewOd, int level) {
        this.viewOd = viewOd;
        this.entityNumber = viewOd.getEntityCount();
        this.level = level;
        keys = new ArrayList<ViewAttribute>();
        lazyLoadConfig = new LazyLoadConfig();
    }

    @Override
    public void setAttribute(PortableFileReader reader) {
        String attributeName = reader.getAttributeName();

        switch (attributeName.charAt(0)) {
        case 'A':
            if (reader.getAttributeName().equals("ACT_LIMIT")) {
                activateLimit = Integer.parseInt(reader.getAttributeValue());
            }
            break;

        case 'C':
            if (reader.getAttributeName().equals("CREATE")) {
                create = reader.getAttributeValue().startsWith("Y");
            } else if (reader.getAttributeName().equals("CARDMAX")) {
                maxcardinality = Integer.parseInt(reader.getAttributeValue());
            } else if (reader.getAttributeName().equals("CARDMIN")) {
                minCardinality = Integer.parseInt(reader.getAttributeValue());
            }
            break;

        case 'D':
            if (reader.getAttributeName().equals("DELETE")) {
                delete = reader.getAttributeValue().startsWith("Y");
            } else if (reader.getAttributeName().equals("DERIVED")) {
                derived = true;
                derivedPath = true;
            } else if (reader.getAttributeName().equals("DEBUGCHG")) {
                // Set up a listener to write stack trace when an entity is changed.
                if (StringUtils.startsWithIgnoreCase(reader.getAttributeValue(), "Y"))
                    eventListener = new EventStackTrace();
            }
            if (reader.getAttributeName().equals("DEBUGINCRE")) {
                debugIncrementalFlag = StringUtils.startsWithIgnoreCase(reader.getAttributeValue(), "Y");
            } else if (reader.getAttributeName().equals("DUPENTIN")) {
                duplicateEntity = true;
            }

            break;

        case 'E':
            if (reader.getAttributeName().equals("ERENT_TOK")) {
                erEntityToken = Integer.parseInt(reader.getAttributeValue());
            } else if (reader.getAttributeName().equals("ERREL_TOK")) {
                erRelToken = Integer.parseInt(reader.getAttributeValue());
            } else if (reader.getAttributeName().equals("ERREL_LINK")) {
                erRelLink = reader.getAttributeValue().equals("1");
            } else if (reader.getAttributeName().equals("EXCLUDE")) {
                exclude = reader.getAttributeValue().startsWith("Y");
            }
            break;

        case 'I':
            if (reader.getAttributeName().equals("INCLUDE")) {
                include = reader.getAttributeValue().startsWith("Y");
            } else if (reader.getAttributeName().equals("INCLSRC")) {
                includeSrc = reader.getAttributeValue().startsWith("Y");
            }
            break;

        case 'L':
            if (reader.getAttributeName().equals("LAZYLOAD")) {
                getLazyLoadConfig().setFlag(LazyLoadFlags.IS_LAZYLOAD);
                if (getParent() == null)
                    throw new ZeidonException("LAZYLOAD is invalid for root entity");

                LazyLoadConfig parentConfig = getParent().getLazyLoadConfig();
                parentConfig.setFlag(LazyLoadFlags.HAS_LAZYLOAD_CHILD);
            }
            break;

        case 'N':
            if (reader.getAttributeName().equals("NAME")) {
                name = reader.getAttributeValue().intern();
            }
            break;

        case 'P':
            if (reader.getAttributeName().equals("PDELETE")) {
                switch (reader.getAttributeValue().charAt(0)) {
                case 'D':
                    parentDelete = true;
                    break;

                case 'R':
                    restrictParentDelete = true;
                    getParent().setCheckRestrictedDelete(true);
                    break;

                // It looks like we're supposed to ignore 'E'.
                }
            }
            break;

        case 'R':
            if (reader.getAttributeName().equals("RECURSIVE")) {
                // Check to see if this entity is recursive.
                for (ViewEntity search = parent; search != null; search = search.getParent()) {
                    if (search.getErEntityToken() == erEntityToken) {
                        search.recursiveParent = true;
                        this.recursive = true;
                        this.recursiveParentViewEntity = search;
                        break;
                    }
                }

                if (!recursive)
                    throw new ZeidonException(
                            "Internal error: Recursive flag is set but no recursive parent found. %s", this);
            }
            break;

        case 'U':
            if (reader.getAttributeName().equals("UPDATE")) {
                update = reader.getAttributeValue().startsWith("Y");
            }
            break;
        }
    }

    public String getName() {
        return name;
    }

    public int getErEntityToken() {
        return erEntityToken;
    }

    public ViewOd getViewOd() {
        return viewOd;
    }

    public void setViewOD(ViewOd viewOd) {
        this.viewOd = viewOd;
    }

    public ViewEntity getPrevHier() {
        return prevHier;
    }

    public void setPrevHier(ViewEntity prevHier) {
        this.prevHier = prevHier;
    }

    public ViewEntity getNextHier() {
        return nextHier;
    }

    public void setNextHier(ViewEntity nextHier) {
        this.nextHier = nextHier;
    }

    public ViewEntity getParent() {
        return parent;
    }

    void setParent(ViewEntity parent) {
        this.parent = parent;
        if (parent.derivedPath)
            derivedPath = true;

        if (parent.children == null)
            parent.children = new ArrayList<ViewEntity>();

        LazyLoadConfig parentConfig = parent.getLazyLoadConfig();
        if (parentConfig.isLazyLoad()) {
            getLazyLoadConfig().setFlag(LazyLoadFlags.HAS_LAZYLOAD_PARENT);
            getLazyLoadConfig().setLazyLoadParent(getParent());
        } else if (parentConfig.hasLazyLoadParent()) {
            getLazyLoadConfig().setFlag(LazyLoadFlags.HAS_LAZYLOAD_PARENT);
            getLazyLoadConfig().setLazyLoadParent(parentConfig.getLazyLoadParent());
        }

        parent.children.add(this);
    }

    public ViewEntity getPrevSibling() {
        return prevSibling;
    }

    public void setPrevSibling(ViewEntity prevSibling) {
        this.prevSibling = prevSibling;
    }

    public ViewEntity getNextSibling() {
        return nextSibling;
    }

    public void setNextSibling(ViewEntity nextSibling) {
        this.nextSibling = nextSibling;
    }

    public int getLevel() {
        return level;
    }

    void addViewAttribute(ViewAttribute viewAttribute) {
        attributes.add(viewAttribute);
        attributeMap.put(viewAttribute.getName(), viewAttribute);
        erAttributeMap.put(viewAttribute.getErAttributeToken(), viewAttribute);
    }

    public int getAttributeCount() {
        return attributes.size();
    }

    public ViewAttribute getAttribute(String attribName) {
        return getAttribute(attribName, true);
    }

    public ViewAttribute getAttribute(String attribName, boolean required) {
        ViewAttribute attrib = attributeMap.get(attribName);
        if (attrib == null && required)
            throw new UnknownViewAttributeException(this, attribName);

        return attrib;
    }

    public ViewAttribute getAttribute(int attributeNumber) {
        if (attributeNumber >= attributes.size())
            throw new ZeidonException("Attribute index %d out of range for %s.", attributeNumber, viewOd.getName());

        return attributes.get(attributeNumber);
    }

    public ViewAttribute getAttributeByErToken(long erToken) {
        return erAttributeMap.get(erToken);
    }

    public List<ViewAttribute> getAttributes() {
        return Collections.unmodifiableList(attributes);
    }

    public int getHierIndex() {
        return entityNumber;
    }

    @Override
    public String toString() {
        return viewOd.toString() + "." + name;
    }

    public boolean isParentDelete() {
        return parentDelete;
    }

    /**
     * If true, then the parent of this entity cannot be deleted when this entity
     * instance exists.
     *
     * @return
     */
    public boolean isRestrictParentDelete() {
        return restrictParentDelete;
    }

    /**
     * If this flag is true, make sure none of the child entities have
     * the parent delete restrict flag set when the entity is being deleted.
     *
     * @return
     */
    public boolean isCheckRestrictedDelete() {
        return checkRestrictedDelete;
    }

    private void setCheckRestrictedDelete(boolean b) {
        checkRestrictedDelete = b;
    }

    public int getChildCount() {
        return children == null ? 0 : children.size();
    }

    protected void setSiblingsForChildren() {
        if (children == null)
            return;

        for (int i = 0; i < children.size(); i++) {
            if (i > 0)
                children.get(i).setPrevSibling(children.get(i - 1));

            if (i < children.size() - 1)
                children.get(i).setNextSibling(children.get(i + 1));

            children.get(i).setSiblingsForChildren();
        }
    }

    public boolean isDerived() {
        return derived;
    }

    public boolean isPersistent() {
        return (!isDerived()) && (!isDerivedPath());
    }

    /**
     * Returns a list of the direct children of this entity.
     * @return
     */
    public List<ViewEntity> getChildren() {
        if (children == null)
            return Collections.emptyList();

        return children;
    }

    /**
     * Returns a list of all the children under the current entity
     * in hier order.
     * @return
     */
    public synchronized List<ViewEntity> getChildrenHier() {
        if (children == null)
            return Collections.emptyList();

        if (childrenHier != null)
            return childrenHier;

        List<ViewEntity> list = new ArrayList<ViewEntity>();
        for (ViewEntity child = this.getNextHier(); child != null
                && child.getLevel() > this.getLevel(); child = child.getNextHier()) {
            list.add(child);
        }

        childrenHier = list;

        return list;
    }

    /**
     * Returns the last child hierarchically under this entity.  If this entity has
     * no children, then returns itself.
     *
     * @return
     */
    public ViewEntity getLastChildHier() {
        if (children == null)
            return this;

        List<ViewEntity> list = getChildrenHier();
        return list.get(list.size() - 1);
    }

    public int getErRelToken() {
        return erRelToken;
    }

    public boolean isErRelLink() {
        return erRelLink;
    }

    public int getPersistentAttributeCount() {
        return persistentAttributeCount;
    }

    void setPersistentAttributeCount(int persistentAttributeCount) {
        this.persistentAttributeCount = persistentAttributeCount;
    }

    public int getWorkAttributeCount() {
        return workAttributeCount;
    }

    void setWorkAttributeCount(int workAttributeCount) {
        this.workAttributeCount = workAttributeCount;
    }

    public boolean isCreate() {
        return create;
    }

    public DataRecord getDataRecord() {
        return dataRecord;
    }

    void setDataRecord(DataRecord dataRecord) {
        this.dataRecord = dataRecord;
    }

    public ViewAttribute getGenKey() {
        return genKey;
    }

    public boolean isDerivedPath() {
        return derivedPath;
    }

    public boolean isInclude() {
        return include;
    }

    public boolean isIncludeSource() {
        return includeSrc;
    }

    public boolean isExclude() {
        return exclude;
    }

    public boolean isDelete() {
        return delete;
    }

    public boolean isUpdate() {
        return update;
    }

    void setGenKey(ViewAttribute viewAttribute) {
        genKey = viewAttribute;
    }

    public boolean isRecursive() {
        return recursive;
    }

    public boolean isRecursiveParent() {
        return recursiveParent;
    }

    public boolean isDebugIncremental() {
        return debugIncrementalFlag;
    }

    void setAutoSeq(ViewAttribute autoSeq) {
        this.autoSeq = autoSeq;
    }

    public ViewAttribute getAutoSeq() {
        return autoSeq;
    }

    public List<ViewAttribute> getKeys() {
        return keys;
    }

    void addKey(ViewAttribute key) {
        keys.add(key);
    }

    /**
     * If this entity is recursive, this returns the recursive parent, null otherwise.
     *
     * @return
     */
    public ViewEntity getRecursiveParentViewEntity() {
        return recursiveParentViewEntity;
    }

    /**
     * If this ViewEntity is recursive then this returns the recursive parent,
     * otherwise returns 'this'.
     *
     * @return
     */
    public ViewEntity getBaseViewEntity() {
        if (getRecursiveParentViewEntity() != null)
            return getRecursiveParentViewEntity();

        return this;
    }

    public int getMinCardinality() {
        return minCardinality;
    }

    public int getMaxCardinality() {
        return maxcardinality;
    }

    /* (non-Javadoc)
     * @see com.quinsoft.zeidon.CacheMap#putCacheMap(java.lang.Class, java.lang.Object)
     */
    @Override
    public <T> T putCacheMap(Class<T> key, T value) {
        return cacheMap.putCacheMap(key, value);
    }

    /* (non-Javadoc)
     * @see com.quinsoft.zeidon.CacheMap#getCacheMap(java.lang.Class)
     */
    @Override
    public <T> T getCacheMap(Class<T> key) {
        return cacheMap.getCacheMap(key);
    }

    /**
     * If true, then there are other entities in this View OD that have duplicate relationships
     * that may need to be relinked after activation.
     */
    public boolean isDuplicateEntity() {
        return duplicateEntity;
    }

    /**
     * @return the eventListener
     */
    public EventListener getEventListener() {
        return eventListener;
    }

    /**
     * @param eventListener the eventListener to set
     */
    public void setEventListener(EventListener eventListener) {
        this.eventListener = eventListener;
    }

    /**
     * @return the hasInitializedAttributes
     */
    public boolean hasInitializedAttributes() {
        return hasInitializedAttributes;
    }

    /**
     * @param hasInitializedAttributes the hasInitializedAttributes to set
     */
    public void setHasInitializedAttributes(boolean hasInitializedAttributes) {
        this.hasInitializedAttributes = hasInitializedAttributes;
    }

    /**
     * Returns true if 'this' ViewEntity has all the persistent attributes of
     * 'otherEntity'.  Intended to be used by includeProcessing.
     *
     * @param otherEntity
     * @return
     */
    public boolean isAttributeSuperset(ViewEntity otherEntity) {
        if (getErEntityToken() != otherEntity.getErEntityToken())
            throw new ZeidonException("Entities do not have matching ER Entity Tokens.").prependViewEntity(this)
                    .prependMessage("Other entity = %s", otherEntity);

        // Have we already determined the superset status for this entity?
        if (attributeSuperset.containsKey(otherEntity))
            return attributeSuperset.get(otherEntity); // Yes, so return it.

        // Determine if 'this' entity is a superset.
        Boolean isSuperset = Boolean.TRUE;
        for (ViewAttribute viewAttribute : otherEntity.getAttributes()) {
            if (viewAttribute.isPersistent()) {
                if (getAttribute(viewAttribute.getName(), false) == null) {
                    isSuperset = Boolean.FALSE; // Use the constant value to preclude concurrency issues.
                    break;
                }
            }
        }

        // Store the answer in a concurrent map.  We don't care if there's thread
        // collision because the answer will be the same in either case.
        attributeSuperset.put(otherEntity, isSuperset);

        return isSuperset;
    }

    /**
     * @return the hashKeyAttribute
     */
    public Collection<ViewAttribute> getHashKeyAttributes() {
        return hashKeyAttributes;
    }

    /**
     * @param hashKeyAttribute the hashKeyAttribute to set
     */
    void addHashKeyAttribute(ViewAttribute hashKeyAttribute) {
        if (hashKeyAttributes == null)
            hashKeyAttributes = new ArrayList<ViewAttribute>();

        hashKeyAttributes.add(hashKeyAttribute);
    }

    /**
     * @return the loadIncrementally
     */
    public LazyLoadConfig getLazyLoadConfig() {
        return lazyLoadConfig;
    }

    public List<ViewAttribute> getSequencingAttributes() {
        return activateOrdering;
    }

    /**
     * Add the view attribute to the list of ordering attributes in the position
     * 'position'.  Note, position is 1-based.
     *
     * @param viewAttribute
     * @param position
     */
    void addSequencingAttribute(ViewAttribute viewAttribute, int position) {
        if (activateOrdering == null)
            activateOrdering = new ArrayList<ViewAttribute>();

        while (activateOrdering.size() < position)
            activateOrdering.add(null);

        activateOrdering.set(position - 1, viewAttribute);
    }

    /**
     * @return the activateLimit
     */
    public Integer getActivateLimit() {
        return activateLimit;
    }
}