org.apache.ojb.broker.metadata.ObjectReferenceDescriptor.java Source code

Java tutorial

Introduction

Here is the source code for org.apache.ojb.broker.metadata.ObjectReferenceDescriptor.java

Source

package org.apache.ojb.broker.metadata;

/* Copyright 2002-2005 The Apache Software Foundation
 *
 * Licensed 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.
 */

import java.util.Hashtable;
import java.util.Iterator;
import java.util.Vector;

import org.apache.commons.lang.builder.ToStringBuilder;
import org.apache.ojb.broker.OJBRuntimeException;
import org.apache.ojb.broker.PersistenceBrokerException;
import org.apache.ojb.broker.core.proxy.ProxyHelper;
import org.apache.ojb.broker.metadata.fieldaccess.PersistentField;

/**
 * Describes a Field containing a reference to another class. Provides handling for foreign keys etc.
 * <br>
 * Note: Be careful when use references of this class or caching instances of this class,
 * because instances could become invalid (see {@link MetadataManager}).
 *
 * @author <a href="mailto:thma@apache.org">Thomas Mahler<a>
 *
 */
public class ObjectReferenceDescriptor extends AttributeDescriptorBase implements XmlCapable {
    private static final long serialVersionUID = 5561562217150972131L;

    public static final int CASCADE_NONE = 17;
    public static final int CASCADE_LINK = 19;
    public static final int CASCADE_OBJECT = 23;

    private Class m_ClassOfItems = null;
    private Vector m_ForeignKeyFields = new Vector();
    private boolean m_CascadeRetrieve = true;
    private int m_CascadeStore = CASCADE_NONE;
    private int m_CascadeDelete = CASCADE_NONE;
    private int m_ProxyPrefetchingLimit = 50;

    private Class m_ProxyOfItems = null;
    private boolean m_LookedUpProxy = false;
    private boolean m_OtmDependent = false;

    /**
     * holds the foreign-key field descriptor array for a specified class
     */
    private Hashtable fkFieldMap = new Hashtable();
    /**
     * define loading strategy of the resulting object
     */
    private boolean lazy = false;
    /**
     * if true relationship is refreshed when owner is found in cache
     */
    private boolean refresh = false;

    /**
     *
     */
    public ObjectReferenceDescriptor(ClassDescriptor descriptor) {
        super(descriptor);
    }

    /**
     *
     */
    public Class getItemProxyClass() throws PersistenceBrokerException {
        if (!m_LookedUpProxy) {
            m_ProxyOfItems = getClassDescriptor().getRepository().getDescriptorFor(m_ClassOfItems).getProxyClass();
            m_LookedUpProxy = true;
        }
        return m_ProxyOfItems;
    }

    /**
     *
     */
    public FieldDescriptor[] getForeignKeyFieldDescriptors(ClassDescriptor cld) {
        FieldDescriptor[] foreignKeyFieldDescriptors;
        if ((foreignKeyFieldDescriptors = (FieldDescriptor[]) fkFieldMap.get(cld)) == null) {
            // 1. collect vector of indices of Fk-Fields
            Vector v = getForeignKeyFields();
            // 2. get FieldDescriptor for each index from Class-descriptor
            // 2A. In a many-to-many relationship foreignkeyfields vector will be null.
            if (v != null) {
                Vector ret;
                if (cld.isInterface()) {
                    //exchange interface class descriptor with first concrete
                    //class
                    Vector extents = cld.getExtentClasses();
                    Class firstConcreteClass = (Class) extents.get(0);
                    cld = getClassDescriptor().getRepository().getDescriptorFor(firstConcreteClass);
                }
                ret = new Vector();

                Iterator iter = v.iterator();
                while (iter.hasNext()) {
                    Object fk = iter.next();
                    FieldDescriptor fkfd = null;
                    /*
                         OJB-55
                         it's possible that the FK field is declared in the super classes of this object,
                         so we can search for a valid field in super class-descriptor
                         */
                    ClassDescriptor tmp = cld;
                    while (tmp != null) {
                        if (fk instanceof Integer) {
                            Integer index = (Integer) fk;
                            fkfd = cld.getFieldDescriptorByIndex(index.intValue());
                        } else {
                            fkfd = tmp.getFieldDescriptorByName((String) fk);
                        }
                        if (fkfd != null) {
                            break;
                        } else {
                            tmp = tmp.getSuperClassDescriptor();
                        }
                    }

                    if (fkfd == null) {
                        throw new OJBRuntimeException("Incorrect or not found field reference name '" + fk
                                + "' in descriptor " + this + " for class-descriptor '"
                                + (cld != null ? cld.getClassNameOfObject() + "'" : "'null'"));
                    }
                    ret.add(fkfd);
                }
                foreignKeyFieldDescriptors = (FieldDescriptor[]) ret.toArray(new FieldDescriptor[ret.size()]);
                fkFieldMap.put(cld, foreignKeyFieldDescriptors);
            }
        }
        return foreignKeyFieldDescriptors;
    }

    /**
     * Returns an Object array of all FK field values of the specified object.
     * If the specified object is an unmaterialized Proxy, it will be materialized
     * to read the FK values.
     *
     * @throws MetadataException if an error occours while accessing ForeingKey values on obj
     */
    public Object[] getForeignKeyValues(Object obj, ClassDescriptor mif) throws PersistenceBrokerException {
        FieldDescriptor[] fks = getForeignKeyFieldDescriptors(mif);
        // materialize object only if FK fields are declared
        if (fks.length > 0)
            obj = ProxyHelper.getRealObject(obj);
        Object[] result = new Object[fks.length];
        for (int i = 0; i < result.length; i++) {
            FieldDescriptor fmd = fks[i];
            PersistentField f = fmd.getPersistentField();

            // BRJ: do NOT convert.
            // conversion is done when binding the sql-statement
            //
            // FieldConversion fc = fmd.getFieldConversion();
            // Object val = fc.javaToSql(f.get(obj));

            result[i] = f.get(obj);
        }
        return result;
    }

    /**
     *
     */
    public Class getItemClass() {
        return m_ClassOfItems;
    }

    /**
     * @return the fully qualified name of the item class for this descriptor.
     */
    public String getItemClassName() {
        return this.m_ClassOfItems != null ? this.m_ClassOfItems.getName() : null;
    }

    /**
     * sets the item class
     * @param c the items class object
     */
    public void setItemClass(Class c) {
        m_ClassOfItems = c;
    }

    /**
     *
     */
    public Vector getForeignKeyFields() {
        return m_ForeignKeyFields;
    }

    /**
     *
     */
    public void setForeignKeyFields(Vector vec) {
        m_ForeignKeyFields = vec;
    }

    /**
     * add a foreign key field ID
     */
    public void addForeignKeyField(int newId) {
        if (m_ForeignKeyFields == null) {
            m_ForeignKeyFields = new Vector();
        }
        m_ForeignKeyFields.add(new Integer(newId));
    }

    /**
     * add a foreign key field
     */
    public void addForeignKeyField(String newField) {
        if (m_ForeignKeyFields == null) {
            m_ForeignKeyFields = new Vector();
        }
        m_ForeignKeyFields.add(newField);
    }

    /**
     * Gets the refresh.
     * @return Returns a boolean
     */
    public boolean isRefresh() {
        return refresh;
    }

    /**
     * Sets the refresh.
     * @param refresh The refresh to set
     */
    public void setRefresh(boolean refresh) {
        this.refresh = refresh;
    }

    /**
     * Gets the lazy.
     * @return Returns a boolean
     */
    public boolean isLazy() {
        return lazy;
    }

    /**
     * Sets the lazy.
     * @param lazy The lazy to set
     */
    public void setLazy(boolean lazy) {
        this.lazy = lazy;
    }

    /**
     *
     */
    public boolean getCascadeRetrieve() {
        return m_CascadeRetrieve;
    }

    /**
     *
     */
    public void setCascadeRetrieve(boolean b) {
        m_CascadeRetrieve = b;
    }

    /**
     *
     */
    public int getCascadingStore() {
        return m_CascadeStore;
    }

    /**
     *
     */
    public void setCascadingStore(int cascade) {
        m_CascadeStore = cascade;
    }

    public void setCascadingStore(String value) {
        setCascadingStore(getCascadeStoreValue(value));
    }

    /**
     * @deprecated use {@link #getCascadingStore} instead.
     */
    public boolean getCascadeStore() {
        return getCascadingStore() == CASCADE_OBJECT;
    }

    /**
     * @deprecated use {@link #setCascadingStore(int)} instead.
     */
    public void setCascadeStore(boolean cascade) {
        if (cascade) {
            setCascadingStore(getCascadeStoreValue("true"));
        } else {
            setCascadingStore(getCascadeStoreValue("false"));
        }
    }

    /**
     *
     */
    public int getCascadingDelete() {
        return m_CascadeDelete;
    }

    /**
     *
     */
    public void setCascadingDelete(int cascade) {
        m_CascadeDelete = cascade;
    }

    public void setCascadingDelete(String value) {
        setCascadingDelete(getCascadeDeleteValue(value));
    }

    /**
     * @deprecated use {@link #getCascadingDelete} instead.
     */
    public boolean getCascadeDelete() {
        return getCascadingDelete() == CASCADE_OBJECT;
    }

    /**
     * @deprecated use {@link #setCascadingDelete(int)}
     */
    public void setCascadeDelete(boolean cascade) {
        if (cascade) {
            setCascadingDelete(getCascadeDeleteValue("true"));
        } else {
            setCascadingDelete(getCascadeDeleteValue("false"));
        }
    }

    protected int getCascadeStoreValue(String cascade) {
        if (cascade.equalsIgnoreCase(RepositoryTags.CASCADE_NONE_STR)) {
            return CASCADE_NONE;
        } else if (cascade.equalsIgnoreCase(RepositoryTags.CASCADE_LINK_STR)) {
            return CASCADE_LINK;
        } else if (cascade.equalsIgnoreCase(RepositoryTags.CASCADE_OBJECT_STR)) {
            return CASCADE_OBJECT;
        } else if (cascade.equalsIgnoreCase("true")) {
            return CASCADE_OBJECT;
        } else if (cascade.equalsIgnoreCase("false")) {
            /*
            in old implementation the FK values of an 1:1 relation are always
            set. Thus we choose 'link' instead of 'none'
            The CollectionDescriptor should override this behaviour.
            */
            return CASCADE_LINK;
        } else {
            throw new OJBRuntimeException("Invalid value! Given value was '" + cascade + "', expected values are: "
                    + RepositoryTags.CASCADE_NONE_STR + ", " + RepositoryTags.CASCADE_LINK_STR + ", "
                    + RepositoryTags.CASCADE_OBJECT_STR + " ('false' and 'true' are deprecated but still valid)");
        }
    }

    protected int getCascadeDeleteValue(String cascade) {
        if (cascade.equalsIgnoreCase(RepositoryTags.CASCADE_NONE_STR)) {
            return CASCADE_NONE;
        } else if (cascade.equalsIgnoreCase(RepositoryTags.CASCADE_LINK_STR)) {
            return CASCADE_LINK;
        } else if (cascade.equalsIgnoreCase(RepositoryTags.CASCADE_OBJECT_STR)) {
            return CASCADE_OBJECT;
        } else if (cascade.equalsIgnoreCase("true")) {
            return CASCADE_OBJECT;
        } else if (cascade.equalsIgnoreCase("false")) {
            return CASCADE_NONE;
        } else {
            throw new OJBRuntimeException("Invalid value! Given value was '" + cascade + "', expected values are: "
                    + RepositoryTags.CASCADE_NONE_STR + ", " + RepositoryTags.CASCADE_LINK_STR + ", "
                    + RepositoryTags.CASCADE_OBJECT_STR + " ('false' and 'true' are deprecated but still valid)");
        }
    }

    public String getCascadeAsString(int cascade) {
        String result = null;
        switch (cascade) {
        case CASCADE_NONE:
            result = RepositoryTags.CASCADE_NONE_STR;
            break;
        case CASCADE_LINK:
            result = RepositoryTags.CASCADE_LINK_STR;
            break;
        case CASCADE_OBJECT:
            result = RepositoryTags.CASCADE_OBJECT_STR;
            break;
        }
        return result;
    }

    public int getProxyPrefetchingLimit() {
        return m_ProxyPrefetchingLimit;
    }

    public void setProxyPrefetchingLimit(int proxyPrefetchingLimit) {
        m_ProxyPrefetchingLimit = proxyPrefetchingLimit;
    }

    /**
     *
     */
    public boolean getOtmDependent() {
        return m_OtmDependent;
    }

    /**
     *
     */
    public void setOtmDependent(boolean b) {
        m_OtmDependent = b;
    }

    /**
     * Returns <code>true</code> if this descriptor was used to
     * describe a reference to a super class of an object.
     *
     * @return always <code>false</code> for this instance.
     */
    public boolean isSuperReferenceDescriptor() {
        return false;
    }

    /**
     * Returns <em>true</em> if a foreign key constraint to the referenced object is
     * declared, else <em>false</em> is returned.
     */
    public boolean hasConstraint() {
        /*
        arminw: Currently we don't have a ForeignKey descriptor object and
        a official xml-element to support FK settings. As a workaround I introduce
        a custom-attribute to handle FK settings in collection-/reference-decriptor
        */
        String result = getAttribute("constraint");
        return result != null && result.equalsIgnoreCase("true");
    }

    /**
     * Set a foreign key constraint flag for this reference - see {@link #hasConstraint()}
     * @param constraint If set <em>true</em>, signals a foreign key constraint in database. 
     */
    public void setConstraint(boolean constraint) {
        addAttribute("constraint", constraint ? "true" : "false");
    }

    public String toString() {
        return new ToStringBuilder(this).append("cascade_retrieve", getCascadeRetrieve())
                .append("cascade_store", getCascadeAsString(m_CascadeStore))
                .append("cascade_delete", getCascadeAsString(m_CascadeDelete)).append("is_lazy", lazy)
                .append("class_of_Items", m_ClassOfItems).toString();
    }

    /*
     * @see XmlCapable#toXML()
     */
    public String toXML() {
        RepositoryTags tags = RepositoryTags.getInstance();
        String eol = System.getProperty("line.separator");

        // opening tag
        StringBuffer result = new StringBuffer(1024);
        result.append("      ");
        result.append(tags.getOpeningTagNonClosingById(REFERENCE_DESCRIPTOR));
        result.append(eol);

        // attributes
        // name
        String name = this.getAttributeName();
        if (name == null) {
            name = RepositoryElements.TAG_SUPER;
        }
        result.append("        ");
        result.append(tags.getAttribute(FIELD_NAME, name));
        result.append(eol);

        // class-ref
        result.append("        ");
        result.append(tags.getAttribute(REFERENCED_CLASS, this.getItemClassName()));
        result.append(eol);

        // proxyReference is optional
        if (isLazy()) {
            result.append("        ");
            result.append(tags.getAttribute(PROXY_REFERENCE, "true"));
            result.append(eol);
            result.append("        ");
            result.append(tags.getAttribute(PROXY_PREFETCHING_LIMIT, "" + this.getProxyPrefetchingLimit()));
            result.append(eol);
        }

        //reference refresh is optional, disabled by default
        if (isRefresh()) {
            result.append("        ");
            result.append(tags.getAttribute(REFRESH, "true"));
            result.append(eol);
        }

        //auto retrieve
        result.append("        ");
        result.append(tags.getAttribute(AUTO_RETRIEVE, "" + getCascadeRetrieve()));
        result.append(eol);

        //auto update
        result.append("        ");
        result.append(tags.getAttribute(AUTO_UPDATE, getCascadeAsString(getCascadingStore())));
        result.append(eol);

        //auto delete
        result.append("        ");
        result.append(tags.getAttribute(AUTO_DELETE, getCascadeAsString(getCascadingDelete())));
        result.append(eol);

        //otm-dependent is optional, disabled by default
        if (getOtmDependent()) {
            result.append("        ");
            result.append(tags.getAttribute(OTM_DEPENDENT, "true"));
            result.append(eol);
        }

        // close opening tag
        result.append("      >");
        result.append(eol);

        // elements
        // write foreignkey elements
        for (int i = 0; i < getForeignKeyFields().size(); i++) {
            Object obj = getForeignKeyFields().get(i);
            if (obj instanceof Integer) {
                String fkId = obj.toString();
                result.append("        ");
                result.append(tags.getOpeningTagNonClosingById(FOREIGN_KEY));
                result.append(" ");
                result.append(tags.getAttribute(FIELD_ID_REF, fkId));
                result.append("/>");
                result.append(eol);
            } else {
                String fk = (String) obj;
                result.append("        ");
                result.append(tags.getOpeningTagNonClosingById(FOREIGN_KEY));
                result.append(" ");
                result.append(tags.getAttribute(FIELD_REF, fk));
                result.append("/>");
                result.append(eol);
            }
        }

        // closing tag
        result.append("      ");
        result.append(tags.getClosingTagById(REFERENCE_DESCRIPTOR));
        result.append(eol);
        return result.toString();
    }
}