Package org.xorm

Source Code of org.xorm.RelationshipProxy$RelationshipProxyIterator

/*
  $Header: /cvsroot/xorm/xorm/src/org/xorm/RelationshipProxy.java,v 1.33 2004/02/05 23:04:55 seifertd Exp $

  This file is part of XORM.

  XORM is free software; you can redistribute it and/or modify
  it under the terms of the GNU General Public License as published by
  the Free Software Foundation; either version 2 of the License, or
  (at your option) any later version.

  XORM 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 General Public License for more details.

  You should have received a copy of the GNU General Public License
  along with XORM; if not, write to the Free Software
  Foundation, Inc., 59 Temple Place, Suite 330, Boston, MA  02111-1307  USA
*/
package org.xorm;

import java.util.AbstractCollection;
import java.util.ArrayList;
import java.util.Collection;
import java.util.HashMap;
import java.util.Iterator;
import java.util.logging.Logger;
import javax.jdo.JDOFatalUserException;

import org.xorm.datastore.Column;
import org.xorm.datastore.DataFetchGroup;
import org.xorm.datastore.Row;
import org.xorm.datastore.Table;
import org.xorm.query.BoundExpression;
import org.xorm.query.AndCondition;
import org.xorm.query.Condition;
import org.xorm.query.Operator;
import org.xorm.query.QueryImpl;
import org.xorm.query.Selector;
import org.xorm.query.SimpleCondition;

/**
* Represents a one-to-many or many-to-many relationship.  An instance
* of RelationshipProxy is constructed in
* InterfaceInvocationHandler.invokeCollectionGet().
*/
public class RelationshipProxy extends CollectionProxy {
    protected static Logger logger = Logger.getLogger("org.xorm.RelationshipProxy");

    // The field on the owner object that references this collection
    protected boolean txnManaged;
    protected RelationshipMapping mapping;
    protected InterfaceInvocationHandler owner;

    // Additional fields used for many-to-many tracking
    protected Collection deletedRows;
    protected Collection newRows;

    // Arguments that need to be translated into parameters in a filtered
    // relationship query
    protected Object[] args = null;
   
    /** Returns the underlying relationship mapping this represents. */
    public RelationshipMapping getRelationshipMapping() { return mapping; }

    /** Returns the owner of this directional relationship. */
    public InterfaceInvocationHandler getOwner() { return owner; }

    /** Creates a new proxy backed by the data in the rows collection. */
    public RelationshipProxy(InterfaceManager mgr,
                             RelationshipMapping mapping,
                             InterfaceInvocationHandler owner,
                             ClassMapping classMapping,
                             Object[] args) {
        super(mgr, classMapping);
        this.mapping = mapping;
        this.owner = owner;
        this.txnManaged = owner.isTransactional();
        if (mapping.isMToN()) {
            this.newRows = new ArrayList();
            this.deletedRows = new ArrayList();
        }
        this.args = args;
        this.selector = getSelector();
    }

    /** Overrides CollectionProxy implementation. */
    public Class getElementType() {
        return mapping.getSource().getElementClass();
    }

    protected Selector getSelector() {
        Object ownerPKey = owner.getObjectId();
        if (ownerPKey instanceof TransientKey) {
            return null;
        }

        Column columnToUse = null;
           
        if (mapping.getSource().getColumn() != null) {
            columnToUse = mapping.getSource().getColumn();
        }
        else if (mapping.getFilter() != null) {
            // In this case, source wasn't specified, but target
            // is always specified...and in this case should be
            // the primary key column of the owner class (see
            // ModelMapping.parseRelationship).
            columnToUse = mapping.getTarget().getColumn();
        }
        else {
            // How did we get here?  An exception should have been
            // thrown in ModelMapping.parseRelationship
            throw new JDOFatalUserException(I18N.msg("E_collection_no_source"));
        }
 
        Selector selector = new Selector
            (columnToUse.getTable(),
             new SimpleCondition(columnToUse, Operator.EQUAL, ownerPKey));
       
        if (mapping.getFilter() != null) {
            logger.fine("Filter: " + mapping.getFilter());
            logger.fine("Imports: " + mapping.getImports());
            logger.fine("Parameters: " + mapping.getParameters());
            logger.fine("Variables: " + mapping.getVariables());
            if (mapping.getParameters() != null &&
                !mapping.getParameters().equals("") &&
                (args == null || args.length == 0)) {
                throw new NullPointerException(I18N.msg("E_missing_arguments"));
            }

            //logger.fine("Class Mapping Table: " + classMapping.getTable().getName());
            Class filterTargetClass = classMapping.getMappedClass();
            //logger.fine("Filter Target Class: " + filterTargetClass.getName());
           
            QueryImpl query = new QueryImpl(mgr);
            query.setClass(filterTargetClass);
            query.setFilter(mapping.getFilter());

            // We support the implicit "owner" parameter in filters.
            // The owner refers to the object instance off which the
            // collection field getter is being called.  We need to add
            // it to the parameters that we set on the query.
            String parameters = mapping.getParameters();
            ClassMapping ownerClassMapping = owner.getClassMapping();
            Class ownerClass = ownerClassMapping.getMappedClass();
            String ownerClassName = ownerClass.getName();
            //ownerClassName = ownerClassName.substring(ownerClassName.lastIndexOf('.') + 1);
            //logger.fine("OwnerClassName: " + ownerClassName);

            if (mapping.getImports() != null) {
                query.declareImports(mapping.getImports());
            }
            if (parameters == null || parameters.equals("")) {
                // No parameters, just this owner param
                parameters = ownerClassName + " owner";
            }
            else {
                // Append this owner param to the existing parameters
                parameters += ", " + ownerClassName + " owner";
            }
            logger.fine("Modified Parameters: " + parameters);
            query.declareParameters(parameters);

            if (mapping.getVariables() != null) {
                query.declareVariables(mapping.getVariables());
            }

            if (mapping.getOrdering() != null) {
                query.setOrdering(mapping.getOrdering());
            }

            query.compile();
            //logger.fine("Filter Query: " + query);

            BoundExpression bound = query.getBoundExpression();
            int paramCount = 0;
            if (args != null) {
                while (paramCount < args.length) {
                    bound.bindParameter(paramCount, args[paramCount]);
                    ++paramCount;
                }
            }

            // Always bind the implicit "owner" parameter
            bound.bindParameter(paramCount++, owner.getProxy());

            //logger.fine(paramCount + " parameter(s) bound");

            /*
            logger.info("Main Selector Table Before: " +
                        selector.getTable().getName());
            logger.info("Main Selector Before: " + selector);
            */

            Selector filterSelector = bound.getSelector();

            /*
            logger.info("Filter Selector Table: " +
                        filterSelector.getTable().getName());
            logger.info("Filter Selector: " + filterSelector);
            */

            if (mapping.getSource().getColumn() != null) {
                //logger.fine("Joining filter selector with running selector");
                /*
                Condition join = new SimpleCondition(mapping.getTarget().getColumn(), Operator.EQUAL, filterSelector);
                selector.setCondition(new AndCondition(join, selector.getCondition()));
                */
                selector.merge(filterSelector, Operator.ANDC);
            }
            else {
                // This is a free-floating collection getter query.
                // There is no link table, there is no source.
                // Just use the filter as the selector.
                //logger.fine("Replacing selector with filter selector");
                selector = filterSelector;
            }

            /*
            logger.info("Main Selector Table After: " +
                        selector.getTable().getName());
            logger.info("Main Selector After: " + selector);
            */
        }

        // This is a hack until there is real OrderedSet/List support.
        // key="order-by" can be specified in JDO metadata extensions
        String orderBy = mapping.getOrderBy();
        if (orderBy != null) {
            int spacePos = orderBy.indexOf(' ');
            String colName;
            int orderingType = Selector.Ordering.ASCENDING;
            if (spacePos != -1) {
                colName = orderBy.substring(0, spacePos);
                if ((spacePos + 1) < orderBy.length()) {
                    String type = orderBy.substring(spacePos+1);
                    orderingType = "ASC".equalsIgnoreCase(type) ? Selector.Ordering.ASCENDING : Selector.Ordering.DESCENDING;
                }
            }
            else {
                colName = orderBy;
            }
            Selector.Ordering[] ordering = new Selector.Ordering[1];
            ordering[0] = new Selector.Ordering(selector.getTable().getColumnByName(colName), orderingType);
            selector.setOrdering(ordering);
        }

        DataFetchGroup dfg = null;
        FetchGroupManager fgm = mgr.getInterfaceManagerFactory()
            .getFetchGroupManager();
        if (mapping.isMToN()) {
            // For many-to-many mappings, select all from the join table,
            // and then the default fetch group of the target table
            dfg = new DataFetchGroup(selector.getTable().getColumns());
            dfg.addSubgroup(mapping.getTarget().getColumn(),
                            fgm.getDataFetchGroup(classMapping));
        } else {
            dfg = fgm.getDataFetchGroup(classMapping);
        }
        selector.require(dfg);

        return selector;
    }

    /**
     * Ensures that the proxy is registered in a transaction if necessary.
     */
    protected void forceTransaction() {
        if (owner.isPersistent()) {
            TransactionImpl txn = (TransactionImpl) owner.getInterfaceManager().currentTransaction();
            if (txn.isActive()) {
                txn.attachRelationship(this);
                txnManaged = true;
            } else {
                throw new JDOFatalUserException(I18N.msg("E_modify_collection"));
            }
        }
    }

    /** Adds the given object (proxy object) to the collection. */
    public boolean add(Object o) {
        // Sanity checking
        if (o == null) throw new NullPointerException();
        if (!getElementType().isInstance(o)) {
            throw new IllegalArgumentException(I18N.msg("E_element_type", getElementType().getName()));
        }

        if (!txnManaged) {
            forceTransaction();
        }

        InterfaceInvocationHandler handler = InterfaceInvocationHandler.getHandler(o);
        Row row = new Row(mapping.getSource().getColumn().getTable());
        row.setValue(mapping.getSource().getColumn(), owner.getObjectId());
        row.setValue(mapping.getTarget().getColumn(), handler.getObjectId());
        getRows().add(row);
        if (!owner.isDirty()) {
            owner.makeDirty();
        }
        rowToProxy.put(row, o);
        if (mapping.isMToN()) {
            markAsNew(row);
        }

        // Transient objects that are added to a persistent relationship
        // become persistent.
        if (owner.isPersistent() && !handler.isPersistent()) {
            handler.makePersistent(mgr);
        }
        return true;
    }

    /** Removes the given object (proxy object) from the collection. */
    public boolean remove(Object o) {
        if (!txnManaged) {
            forceTransaction();
        }

        InterfaceInvocationHandler handler = InterfaceInvocationHandler.getHandler(o);
        Object targetKey = handler.getObjectId();
 
        // Find the row that matches the targetKey
        Iterator i = getRows().iterator();
        while (i.hasNext()) {
            Row row = (Row) i.next();
            if (targetKey.equals(row.getValue(mapping.getTarget().getColumn()))) {
                if (mapping.isMToN()) {
                    markAsDeleted(row);
                }
                i.remove();
                rowToProxy.remove(row);
                if (!owner.isDirty()) {
                    owner.makeDirty();
                }
                return true;
            }
        }
        return false; // not found
    }

    /**
     * Removes all rows.  This is slightly more straightforward than
     * iterating through and removing each element (but that works too).
     */
    public void clear() {
        if (!txnManaged) {
            forceTransaction();
        }

        Iterator i = getRows().iterator();
        if (i.hasNext()) {
            if (!owner.isDirty()) {
                owner.makeDirty();
            }

            while (i.hasNext()) {
                Row row = (Row) i.next();
                if (mapping.isMToN()) {
                    markAsDeleted(row);
                }
                i.remove();
            }
        }
        rowToProxy.clear();
    }

    /**
     * Removes any transactional context associated with this proxy,
     * clearing the list of new and deleted rows.
     */
    void exitTransaction(boolean commit) {
        txnManaged = false;
        if (mapping.isMToN()) {
            if (!commit && rows != null) {
                rows.removeAll(newRows);
                rows.addAll(deletedRows);
            }
            newRows.clear();
            deletedRows.clear();
        }
    }

    /** Called when an object ID changes. */
    public void notifyIDChanged(Object oldID, Object newID) {
        // Go through Rows and look for references to oldID
        Iterator i = getRows().iterator();
        Column c1 = mapping.getTarget().getColumn();
        Column c2 = mapping.getSource().getColumn();
        while (i.hasNext()) {
            Row row = (Row) i.next();
            if (oldID.equals(row.getValue(c1))) {
                row.setValue(c1, newID);
            }
            if (oldID.equals(row.getValue(c2))) {
                row.setValue(c2, newID);
            }
        }
        // TODO do we need to tell all contained items?
    }

    public boolean dependsOn(InterfaceInvocationHandler other) {
        // See if any of the contents match
        Iterator i = new ProxyIterator();
        while (i.hasNext()) {
            Object obj = i.next();
            // Check if its key is new
            InterfaceInvocationHandler handler = InterfaceInvocationHandler.getHandler(obj);
            Object idObj = handler.getObjectId();
            if (idObj instanceof TransientKey) {
                // check depends on
                if (handler.dependsOn(other)) {
                    return true;
                }
            }
        }
        return false;
    }

    protected Column getKeyColumn() {
        return mapping.getTarget().getColumn();
    }

    protected class RelationshipProxyIterator extends ProxyIterator {
        public void remove() {
            if (!txnManaged) {
                forceTransaction();
            }
            if (mapping.isMToN()) {
                markAsDeleted(lastRow);
            }
            inner.remove();
            if (!owner.isDirty()) {
                owner.makeDirty();
            }
            rowToProxy.remove(lastRow);
        }
    }

    public Iterator iterator() {
        return iterator = new RelationshipProxyIterator();
    }

    // Methods for Many-to-Many support
    /** Marks a row for removal. */
    void markAsDeleted(Row row) {
        if (newRows.contains(row)) {
            // NEW_DELETED in this case means just pretend it never happened
            newRows.remove(row);
        } else {
            deletedRows.add(row);
        }
    }

    /** Marks a row as newly created. */
    void markAsNew(Row row) {
        if (deletedRows.contains(row)) {
            deletedRows.remove(row);
        } else {
            newRows.add(row);
        }
    }
   
    /**
     * Retrieve the collection of rows that were created in the
     * current transaction.
     */
    Collection getNewRows() { return newRows; }

    /**
     * Retrieve the collection of rows that were removed in the
     * current transaction.
     */
    Collection getDeletedRows() { return deletedRows; }

}
TOP

Related Classes of org.xorm.RelationshipProxy$RelationshipProxyIterator

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.