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

Java tutorial

Introduction

Here is the source code for com.quinsoft.zeidon.objectdefinition.ViewAttribute.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.io.Serializable;
import java.lang.reflect.Constructor;
import java.lang.reflect.Method;

import org.apache.commons.lang3.StringUtils;

import com.quinsoft.zeidon.Application;
import com.quinsoft.zeidon.AttributeInstance;
import com.quinsoft.zeidon.ObjectEngine;
import com.quinsoft.zeidon.Task;
import com.quinsoft.zeidon.TaskQualification;
import com.quinsoft.zeidon.View;
import com.quinsoft.zeidon.ZeidonException;
import com.quinsoft.zeidon.domains.Domain;
import com.quinsoft.zeidon.utils.PortableFileReader;
import com.quinsoft.zeidon.utils.PortableFileReader.PortableFileAttributeHandler;

/**
 * @author DG
 *
 */
public class ViewAttribute implements PortableFileAttributeHandler, Serializable {
    private static final long serialVersionUID = 1L;
    private final ViewEntity viewEntity;
    private String name;
    private Long erAttributeToken;
    private InternalType type;
    private Integer length;
    private int token;
    private String initialValue;

    // Derived operation fields.
    public static final int DERIVED_SET = 1;
    public static final int DERIVED_GET = 2;

    // Following fields are protected instead of private to allow for quicker access when
    // reference by inner classes.
    protected String derivedOperationName;
    protected String derivedOperationClassName;

    private final ThreadLocal<DerivedOper> derivedOperations = new ThreadLocal<DerivedOper>();

    private AttributeHashKeyType hashKeyType = AttributeHashKeyType.NONE;

    /**
     * The unique number for the entity which is the index of the attribute for all
     * attributes.
     */
    private int attributeNumber;
    private boolean hidden;
    private boolean persistent;
    private boolean key;
    private boolean foreignKey;
    private boolean autoSeq;
    private boolean genKey;
    private boolean update;
    private boolean required;
    private boolean debugChange;
    private Domain domain;
    private String domainName;
    private ViewEntity hashKeyParent;
    private Boolean isSequencingAscending = Boolean.TRUE;

    public ViewAttribute(ViewEntity viewEntity) {
        super();
        this.viewEntity = viewEntity;
    }

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

        switch (attributeName.charAt(0)) {
        case 'A':
            if (reader.getAttributeName().equals("AUTO_SEQ")) {
                autoSeq = true;
                viewEntity.setAutoSeq(this);
            }
            break;

        case 'D':
            // DERIVEDC is the name of the Java class that declares the derived function.
            // Java only (e.g. not in the C OE).
            if (reader.getAttributeName().equals("DERIVEDC")) {
                derivedOperationClassName = reader.getAttributeValue().intern();
            } else if (reader.getAttributeName().equals("DERIVEDF")) {
                derivedOperationName = reader.getAttributeValue().intern();
            } else if (reader.getAttributeName().equals("DOMAIN")) {
                Application app = viewEntity.getViewOd().getApplication();
                domainName = reader.getAttributeValue().intern();
                domain = app.getDomain(domainName);
            } else if (reader.getAttributeName().equals("DEBUGCHG")) {
                // requires commons-lang-2.4.jar or later
                debugChange = StringUtils.startsWithIgnoreCase(reader.getAttributeValue(), "Y");
            }
            break;

        case 'E':
            if (reader.getAttributeName().equals("ERATT_TOK")) {
                erAttributeToken = Long.parseLong(reader.getAttributeValue());
            }
            break;

        case 'F':
            if (reader.getAttributeName().equals("FORKEY")) {
                foreignKey = reader.getAttributeValue().toUpperCase().startsWith("Y");
            }
            break;

        case 'G':
            if (reader.getAttributeName().equals("GENKEY")) {
                genKey = true;
                viewEntity.setGenKey(this);
                viewEntity.getViewOd().setHasGenKey(true);
            }
            break;

        case 'H':
            if (reader.getAttributeName().equals("HASHKEY")) {
                if (hashKeyParent == null)
                    hashKeyParent = getViewEntity().getParent();

                hashKeyType = AttributeHashKeyType.valueOf(reader.getAttributeValue());
                if (hashKeyType != AttributeHashKeyType.NONE)
                    viewEntity.addHashKeyAttribute(this);
            } else if (reader.getAttributeName().equals("HASHKEY_PARENT")) {
                String entityName = reader.getAttributeValue();
                for (hashKeyParent = getViewEntity()
                        .getParent(); hashKeyParent != null; hashKeyParent = hashKeyParent.getParent()) {
                    if (hashKeyParent.getName().equals(entityName))
                        break;
                }

                if (hashKeyParent == null)
                    throw new ZeidonException("Unknown hashkey parent %s", entityName);
            } else if (reader.getAttributeName().equals("HIDDEN")) {
                hidden = reader.getAttributeValue().toUpperCase().startsWith("Y");
            }
            break;

        case 'I':
            if (reader.getAttributeName().equals("INIT")) {
                initialValue = reader.getAttributeValue();
                viewEntity.setHasInitializedAttributes(true);
            }

        case 'K':
            if (reader.getAttributeName().equals("KEY")) {
                key = true;
                viewEntity.addKey(this);
            }
            break;

        case 'L':
            if (reader.getAttributeName().equals("LTH")) {
                length = Integer.parseInt(reader.getAttributeValue());
            }
            break;

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

        case 'P':
            if (reader.getAttributeName().equals("PERSIST")) {
                persistent = reader.getAttributeValue().startsWith("Y");
            }
            break;

        case 'R':
            if (reader.getAttributeName().equals("REQUIRED")) {
                required = reader.getAttributeValue().startsWith("Y");
            }
            break;

        case 'S':
            if (reader.getAttributeName().equals("SEQUENCING")) {
                int position = Integer.parseInt(reader.getAttributeValue());

                // Find the first parent that can have multiple children.  If a parent has
                // max cardinality of 1 then it can't be ordered.
                ViewEntity search = viewEntity;
                while (search.getMaxCardinality() == 1)
                    search = search.getParent();

                search.addSequencingAttribute(this, position);
            } else if (reader.getAttributeName().equals("SEQ_AD")) {
                isSequencingAscending = reader.getAttributeValue().toUpperCase().startsWith("A");
            }
            break;

        case 'T':
            if (reader.getAttributeName().equals("TYPE")) {
                type = InternalType.mapCode(reader.getAttributeValue());
            }
            break;

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

        case 'X':
            if (reader.getAttributeName().equals("XVAATT_TOK")) {
                token = Integer.parseInt(reader.getAttributeValue());
            }
            break;
        }
    }

    public ViewEntity getViewEntity() {
        return viewEntity;
    }

    public String getName() {
        return name;
    }

    public Long getErAttributeToken() {
        return erAttributeToken;
    }

    public InternalType getType() {
        return type;
    }

    /**
     * @return Returns max length of this attribute (if applicable). May return null
     *          in which case the max length defaults to the domain definition.
     */
    public Integer getLength() {
        return length;
    }

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

    public int getToken() {
        return token;
    }

    public boolean isHidden() {
        return hidden;
    }

    public boolean isPersistent() {
        return persistent;
    }

    public boolean isRequired() {
        return required;
    }

    public int getAttributeNumber() {
        return attributeNumber;
    }

    /**
     * Loads the class that calls the derived operation from cache.  If the
     * object doesn't exist in the cache, create one.
     *
     * @param task
     * @return
     */
    private synchronized DerivedOper getDerivedOperation(Task task) {

        DerivedOper derivedOperation = derivedOperations.get();
        if (derivedOperation == null) {
            derivedOperation = loadDerivedOperation(task);
            derivedOperations.set(derivedOperation);
        }

        return derivedOperation;
    }

    private DerivedOper loadDerivedOperation(Task view) {
        DerivedOper derivedOperation = null;

        try {
            ObjectEngine oe = view.getObjectEngine();
            ClassLoader classLoader = oe.getClassLoader(derivedOperationClassName);
            Class<? extends Object> operationsClass;
            operationsClass = classLoader.loadClass(derivedOperationClassName);

            Constructor<? extends Object> constructor;
            try {
                // First try getting a constructor that uses a view.  Most derived operations are defined
                // in the Object code so it must be created with a view.
                constructor = operationsClass.getConstructor(CONSTRUCTOR_ARG_TYPES1);
            } catch (NoSuchMethodException e) {
                // We couldn't find a constructor that takes a view, so try to find one that
                // takes a TaskQualification.  A derived attribute could be defined in a global
                // operations class, which uses a TaskQual for the constructor.
                try {
                    constructor = operationsClass.getConstructor(CONSTRUCTOR_ARG_TYPES2);
                } catch (NoSuchMethodException e2) {
                    // One more time: try with no arguments.
                    constructor = operationsClass.getConstructor();
                }
            }

            // First try to find a method with the exact name of derivedOperationName.  This is the
            // old-style method as generated by VML.
            Method method;
            try {
                method = operationsClass.getMethod(derivedOperationName, ARGUMENT_TYPES1);
                derivedOperation = new DerivedOper(constructor, method);
            } catch (NoSuchMethodException e) {
                // Try with the JOE-specific parameter list.
                method = operationsClass.getMethod(derivedOperationName, ARGUMENT_TYPES2);

                // If we get here then it worked!
                derivedOperation = new NewDerivedOper(constructor, method);
            }

            return derivedOperation;
        } catch (Exception e) {
            throw ZeidonException.prependMessage(e, "Error loading Derived operation '%s.%s' for %s",
                    derivedOperationClassName, derivedOperationName, toString());
        }
    }

    public boolean isKey() {
        return key;
    }

    public boolean isForeignKey() {
        return foreignKey;
    }

    public boolean isAutoSeq() {
        return autoSeq;
    }

    public boolean isGenKey() {
        return genKey;
    }

    public boolean isUpdate() {
        return update;
    }

    public boolean isDerived() {
        return derivedOperationClassName != null;
    }

    public boolean isDebugChange() {
        return debugChange;
    }

    public Domain getDomain() {
        return domain;
    }

    /**
     * This calls the derived attribute function to set the internal attribute value.
     * The attribute value when then be retrieved by a get-attr method.
     *
     * @param view
     * @throws Exception
     */
    public void executeDerivedAttributeForGet(View view) {
        getDerivedOperation(view.getTask()).getDerivedValue(view);
    }

    public void executeDerivedAttributeForGet(AttributeInstance attributeInstance) {
        getDerivedOperation(attributeInstance.getTask()).getDerivedValue(attributeInstance);
    }

    static private final Class<?>[] ARGUMENT_TYPES1 = new Class<?>[] { View.class, String.class, String.class,
            Integer.class };
    static private final Class<?>[] ARGUMENT_TYPES2 = new Class<?>[] { AttributeInstance.class };
    static private final Class<?>[] CONSTRUCTOR_ARG_TYPES1 = new Class<?>[] { View.class };
    static private final Class<?>[] CONSTRUCTOR_ARG_TYPES2 = new Class<?>[] { TaskQualification.class };

    /**
     * Call a derived operation as it's defined in Java generated from VML.
     *
     * @author dg
     *
     */
    private class DerivedOper {
        protected final Method method;
        protected final Constructor<? extends Object> constructor;

        private DerivedOper(Constructor<? extends Object> constructor, Method method) {
            this.constructor = constructor;
            this.method = method;
        }

        private void getDerivedValue(View view) {
            callDerivedOperation(view);
        }

        private void getDerivedValue(AttributeInstance attributeInstance) {
            callDerivedOperation(attributeInstance);
        }

        void callDerivedOperation(View view) {
            try {
                Object oper = constructor.newInstance(view);
                Object[] argList = new Object[] { view, getViewEntity().getName(), getName(), DERIVED_GET };
                method.invoke(oper, argList);
            } catch (Throwable e) {
                throw ZeidonException.prependMessage(e, "Oper: %s", this).prependViewAttribute(ViewAttribute.this);
            }
        }

        void callDerivedOperation(AttributeInstance attributeInstance) {
            callDerivedOperation(attributeInstance.getView());
        }

        @Override
        public String toString() {
            return derivedOperationClassName + "." + derivedOperationName;
        }
    }

    /**
     * Call a derived operation as it's defined in native java.  Note that in this case
     * the Derived Object is instantiated as a singleton and reused.
     *
     * @author dg
     *
     */
    private class NewDerivedOper extends DerivedOper {
        private final Object oper;

        private NewDerivedOper(Constructor<? extends Object> constructor, Method method) throws Exception {
            super(constructor, method);
            oper = constructor.newInstance();
        }

        @Override
        void callDerivedOperation(View view) {
            AttributeInstance ai = view.cursor(getViewEntity()).getAttribute(ViewAttribute.this);
            callDerivedOperation(ai);
        }

        @Override
        void callDerivedOperation(AttributeInstance attributeInstance) {
            try {
                Object[] argList = new Object[] { attributeInstance };
                Object value = method.invoke(oper, argList);
                attributeInstance.setDerivedValue(value);
            } catch (Throwable e) {
                throw ZeidonException.prependMessage(e, "Oper: %s", this).prependViewAttribute(ViewAttribute.this);
            }
        }
    }

    public ViewAttribute getNextViewAttribute() {
        int idx = getAttributeNumber() + 1;
        if (idx == viewEntity.getAttributeCount())
            return null;

        return viewEntity.getAttribute(idx);
    }

    /**
     * @return the initialValue
     */
    public String getInitialValue() {
        return initialValue;
    }

    /**
     * @return the hashKeyType
     */
    public AttributeHashKeyType getHashKeyType() {
        return hashKeyType;
    }

    /**
     * For attributes that are a hashkey under a parent, this returns the parent.
     * Otherwise returns null.
     *
     * @return
     */
    public ViewEntity getHashKeyParent() {
        return hashKeyParent;
    }

    /**
     * Returns true if this ViewAttribute is a sequencing attribute in ascending order, false
     * if it's descending, and null if it's not a sequencing attribute.  See
     * ViewEntity.getSequencingAttributes() for more.
     *
     * @return
     */
    public Boolean isSequencingAscending() {
        return isSequencingAscending;
    }
}