/* * Copyright 2005 Werner Guttmann * * 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. * * $Id$ */ package org.castor.persist.resolver; import java.util.ArrayList; import java.util.Iterator; import java.util.List; import org.castor.persist.ProposedEntity; import org.castor.persist.TransactionContext; import org.castor.persist.UpdateAndRemovedFlags; import org.castor.persist.UpdateFlags; import org.castor.persist.proxy.CollectionProxy; import org.castor.persist.proxy.LazyHashSet; import org.castor.persist.proxy.LazyCollection; import org.exolab.castor.jdo.PersistenceException; import org.exolab.castor.mapping.AccessMode; import org.exolab.castor.persist.ClassMolder; import org.exolab.castor.persist.ClassMolderHelper; import org.exolab.castor.persist.FieldMolder; import org.exolab.castor.persist.OID; import org.exolab.castor.persist.spi.Identity; /** * Implementation of {@link org.castor.persist.resolver.ResolverStrategy} for many relations. * This class carries behaviour common to 1:M and M:N relations. * * @author <a href="mailto:werner DOT guttmann AT gmx DOT net">Werner Guttmann</a> * @since 0.9.9 */ public abstract class ManyRelationResolver extends BaseRelationResolver { private final int _fieldIndex; /** * Creates an instance of ManyRelationResolver. * * @param classMolder Associated {@link ClassMolder} * @param fieldMolder Associated {@link FieldMolder} * @param fieldIndex Field index within all fields of parent class molder. */ public ManyRelationResolver(final ClassMolder classMolder, final FieldMolder fieldMolder, final int fieldIndex) { super(classMolder, fieldMolder); _fieldIndex = fieldIndex; } /** * @see org.castor.persist.resolver.ResolverStrategy * #create(org.castor.persist.TransactionContext, * java.lang.Object) */ public final Object create(final TransactionContext tx, final Object object) { ClassMolder fieldClassMolder = _fieldMolder.getFieldClassMolder(); Object o = _fieldMolder.getValue(object, tx.getClassLoader()); if (o != null) { return ClassMolderHelper.getIdsList(tx, fieldClassMolder, o); } return null; } /** * @see org.castor.persist.resolver.ResolverStrategy#markCreate( * org.castor.persist.TransactionContext, org.exolab.castor.persist.OID, * java.lang.Object) */ public abstract boolean markCreate(final TransactionContext tx, final OID oid, final Object object) throws PersistenceException; /** * @see org.castor.persist.resolver.ResolverStrategy#preStore( * org.castor.persist.TransactionContext, org.exolab.castor.persist.OID, * java.lang.Object, int, java.lang.Object) */ public abstract UpdateFlags preStore(final TransactionContext tx, final OID oid, final Object object, final int timeout, final Object field) throws PersistenceException; /** * @see org.castor.persist.resolver.ResolverStrategy#store( * org.castor.persist.TransactionContext, java.lang.Object, java.lang.Object) */ public final Object store(final TransactionContext tx, final Object object, final Object field) { // nothing to do .... return null; } /** * @see org.castor.persist.resolver.ResolverStrategy#update( * org.castor.persist.TransactionContext, org.exolab.castor.persist.OID, * java.lang.Object, org.exolab.castor.mapping.AccessMode, java.lang.Object) */ public abstract void update(final TransactionContext tx, final OID oid, final Object object, final AccessMode suggestedAccessMode, final Object field) throws PersistenceException; /** * @see org.castor.persist.resolver.ResolverStrategy#updateCache( * org.castor.persist.TransactionContext, org.exolab.castor.persist.OID, * java.lang.Object) */ public final Object updateCache(final TransactionContext tx, final OID oid, final Object object) { ClassMolder fieldClassMolder = _fieldMolder.getFieldClassMolder(); Object value = _fieldMolder.getValue(object, tx.getClassLoader()); if (value != null) { if (!(value instanceof LazyCollection)) { return ClassMolderHelper.getIdsList(tx, fieldClassMolder, value); } else { LazyCollection lazy = (LazyCollection) value; return lazy.getIdsList(); } } return null; } /** * @see org.castor.persist.resolver.ResolverStrategy#markDelete( * org.castor.persist.TransactionContext, java.lang.Object, java.lang.Object) */ public abstract void markDelete(final TransactionContext tx, final Object object, final Object field) throws PersistenceException; /** * @see org.castor.persist.resolver.ResolverStrategy#revertObject( * org.castor.persist.TransactionContext, org.exolab.castor.persist.OID, * java.lang.Object, java.lang.Object) */ public final void revertObject(final TransactionContext tx, final OID oid, final Object object, final Object field) throws PersistenceException { Object o = field; if (o == null) { _fieldMolder.setValue(object, null, tx.getClassLoader()); } else if (!_fieldMolder.isLazy()) { // <-- fix for bug #1046 ClassMolder fieldClassMolder = _fieldMolder.getFieldClassMolder(); Class collectionType = _fieldMolder.getCollectionType(); ArrayList v = (ArrayList) field; if (v != null) { if (collectionType.isArray()) { Object[] arrayValue = (Object[]) java.lang.reflect.Array .newInstance(collectionType.getComponentType(), v .size()); for (int j = 0; j < v.size(); j++) { arrayValue[j] = tx.fetch(fieldClassMolder, (Identity) v.get(j), null); } _fieldMolder.setValue(object, arrayValue, tx .getClassLoader()); } else { CollectionProxy cp = CollectionProxy.create(_fieldMolder, object, tx.getClassLoader()); // clear collection _fieldMolder.setValue(object, cp.getCollection(), tx .getClassLoader()); for (int j = 0; j < v.size(); j++) { Object obj = tx.fetch(fieldClassMolder, (Identity) v.get(j), null); if (obj != null) { cp.add((Identity) v.get(j), obj); } } cp.close(); // fieldMolder.setValue( object, cp.getCollection() ); } } else { _fieldMolder.setValue(object, null, tx.getClassLoader()); } } else { List<Identity> list = (List<Identity>) field; ClassMolder fieldClassMolder = _fieldMolder.getFieldClassMolder(); LazyHashSet<Object> relcol = new LazyHashSet<Object>(tx, fieldClassMolder, list); _fieldMolder.setValue(object, relcol, tx.getClassLoader()); } } /** * @see org.castor.persist.resolver.ResolverStrategy#expireCache( * org.castor.persist.TransactionContext, java.lang.Object) */ public final void expireCache(final TransactionContext tx, final Object field) throws PersistenceException { // field is one-to-many and many-to-many type. All the related // objects will be expired ClassMolder fieldClassMolder = _fieldMolder.getFieldClassMolder(); ArrayList v = (ArrayList) field; if (v != null) { for (int j = 0; j < v.size(); j++) { tx.expireCache(fieldClassMolder, (Identity) v.get(j)); } } } /** * @see org.castor.persist.resolver.ResolverStrategy#load( * org.castor.persist.TransactionContext, org.exolab.castor.persist.OID, * org.castor.persist.ProposedEntity, org.exolab.castor.mapping.AccessMode) */ public final void load(final TransactionContext tx, final OID oid, final ProposedEntity proposedObject, final AccessMode suggestedAccessMode) throws PersistenceException { // field is one-to-many and many-to-many type. All the related // object will be loaded and put in a Collection. And, the // collection will be set as the field. ClassMolder fieldClassMolder = _fieldMolder.getFieldClassMolder(); if (!_fieldMolder.isLazy()) { // lazy loading is not specified, load all objects into // the collection and set the Collection as the data object // field. ArrayList v = (ArrayList) proposedObject.getField(_fieldIndex); if (v != null) { // simple array type support Class collectionType = _fieldMolder.getCollectionType(); if (collectionType.isArray()) { Object[] value = (Object[]) java.lang.reflect.Array .newInstance(collectionType.getComponentType(), v.size()); for (int j = 0; j < v.size(); j++) { ProposedEntity proposedValue = new ProposedEntity(fieldClassMolder); value[j] = tx.load((Identity) v.get(j), proposedValue, suggestedAccessMode); } _fieldMolder.setValue(proposedObject.getEntity(), value, tx .getClassLoader()); } else { CollectionProxy cp = CollectionProxy.create(_fieldMolder, proposedObject.getEntity(), tx.getClassLoader()); for (int j = 0; j < v.size(); j++) { ProposedEntity proposedValue = new ProposedEntity(fieldClassMolder); cp.add((Identity) v.get(j), tx.load((Identity) v.get(j), proposedValue, suggestedAccessMode)); } cp.close(); } } else { _fieldMolder.setValue(proposedObject.getEntity(), null, tx .getClassLoader()); } } else { // lazy loading is specified. Related object will not be loaded. // A lazy collection with all the identity of the related object // will constructed and set as the data object's field. List<Identity> list = (List<Identity>) proposedObject.getField(_fieldIndex); LazyHashSet<Object> relcol = new LazyHashSet<Object>(tx, fieldClassMolder, list); _fieldMolder.setValue(proposedObject.getEntity(), relcol, tx .getClassLoader()); } } /** * @see org.castor.persist.resolver.ResolverStrategy#postCreate( * org.castor.persist.TransactionContext, org.exolab.castor.persist.OID, * java.lang.Object, java.lang.Object, org.exolab.castor.persist.spi.Identity) */ public abstract Object postCreate(final TransactionContext tx, final OID oid, final Object object, final Object field, final Identity createdId) throws PersistenceException; /** * @see org.castor.persist.resolver.ResolverStrategy#removeRelation( * org.castor.persist.TransactionContext, java.lang.Object, * org.exolab.castor.persist.ClassMolder, java.lang.Object) */ public final UpdateAndRemovedFlags removeRelation(final TransactionContext tx, final Object object, final ClassMolder relatedMolder, final Object relatedObject) { UpdateAndRemovedFlags flags = new UpdateAndRemovedFlags(); // remove the object from the collection ClassMolder fieldClassMolder = _fieldMolder.getFieldClassMolder(); ClassMolder relatedBaseMolder = relatedMolder; while (fieldClassMolder != relatedBaseMolder && relatedBaseMolder != null) { relatedBaseMolder = relatedBaseMolder.getExtends(); } if (fieldClassMolder == relatedBaseMolder) { boolean changed = false; Object related = _fieldMolder.getValue(object, tx.getClassLoader()); if (related instanceof LazyCollection) { LazyCollection lazy = (LazyCollection) related; changed = lazy.remove(relatedObject); } else { Iterator itor = ClassMolderHelper.getIterator(related); while (itor.hasNext()) { Object o = itor.next(); if (o == relatedObject) { changed = true; itor.remove(); } } } if (changed) { flags.setUpdateCache(true); flags.setUpdatePersist(false); flags.setRemoved(true); } } return flags; } }