package org.apache.ojb.broker.util;
/* Copyright 2002-2004 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.sql.PreparedStatement;
import java.sql.ResultSet;
import java.sql.SQLException;
import java.util.Collection;
import java.util.Iterator;
import java.util.List;
import java.util.Map;
import java.util.StringTokenizer;
import java.util.WeakHashMap;
import java.util.ArrayList;
import org.apache.commons.collections.iterators.ArrayIterator;
import org.apache.commons.collections.CollectionUtils;
import org.apache.ojb.broker.Identity;
import org.apache.ojb.broker.ManageableCollection;
import org.apache.ojb.broker.OJBRuntimeException;
import org.apache.ojb.broker.PBKey;
import org.apache.ojb.broker.PersistenceBrokerException;
import org.apache.ojb.broker.PersistenceBrokerSQLException;
import org.apache.ojb.broker.MtoNImplementor;
import org.apache.ojb.broker.accesslayer.StatementManagerIF;
import org.apache.ojb.broker.accesslayer.sql.SqlExistStatement;
import org.apache.ojb.broker.core.PersistenceBrokerImpl;
import org.apache.ojb.broker.core.ValueContainer;
import org.apache.ojb.broker.core.proxy.IndirectionHandler;
import org.apache.ojb.broker.core.proxy.ProxyHelper;
import org.apache.ojb.broker.metadata.ClassDescriptor;
import org.apache.ojb.broker.metadata.CollectionDescriptor;
import org.apache.ojb.broker.metadata.FieldDescriptor;
import org.apache.ojb.broker.metadata.FieldHelper;
import org.apache.ojb.broker.metadata.MetadataException;
import org.apache.ojb.broker.metadata.MetadataManager;
import org.apache.ojb.broker.metadata.ObjectReferenceDescriptor;
import org.apache.ojb.broker.metadata.fieldaccess.PersistentField;
import org.apache.ojb.broker.platforms.Platform;
import org.apache.ojb.broker.query.Criteria;
import org.apache.ojb.broker.query.MtoNQuery;
import org.apache.ojb.broker.query.Query;
import org.apache.ojb.broker.query.QueryByCriteria;
import org.apache.ojb.broker.query.QueryBySQL;
import org.apache.ojb.broker.query.ReportQueryByCriteria;
import org.apache.ojb.broker.query.ReportQueryByMtoNCriteria;
import org.apache.ojb.broker.util.logging.LoggerFactory;
import org.apache.ojb.broker.util.sequence.SequenceManagerException;
/**
* This class contains helper methods primarily used by the {@link org.apache.ojb.broker.PersistenceBroker}
* implementation (e.g. contains methods to assign the the values of 'autoincrement' fields).
* <br/>
* Furthermore it was used to introduce new features related to {@link org.apache.ojb.broker.PersistenceBroker} - these
* new features and services (if they stand the test of time) will be moved to separate services in future.
*
* @author <a href="mailto:armin@codeAuLait.de">Armin Waibel</a>
* @version $Id: BrokerHelper.java,v 1.57.2.11 2005/04/03 02:14:45 arminw Exp $
*/
public class BrokerHelper
{
public static final String REPOSITORY_NAME_SEPARATOR = "#";
private PersistenceBrokerImpl m_broker;
public BrokerHelper(PersistenceBrokerImpl broker)
{
this.m_broker = broker;
}
/**
* splits up the name string and extract db url,
* user name and password and build a new PBKey
* instance - the token '#' is used to separate
* the substrings.
* @throws PersistenceBrokerException if given name was <code>null</code>
*/
public static PBKey extractAllTokens(String name)
{
if(name == null)
{
throw new PersistenceBrokerException("Could not extract PBKey, given argument is 'null'");
}
String user = null;
String passwd = null;
StringTokenizer tok = new StringTokenizer(name, REPOSITORY_NAME_SEPARATOR);
String dbName = tok.nextToken();
if(tok.hasMoreTokens())
{
user = tok.nextToken();
if(user != null && user.trim().equals(""))
{
user = null;
}
}
if(tok.hasMoreTokens())
{
if(user != null)
passwd = tok.nextToken();
}
if(user != null && passwd == null)
{
passwd = "";
}
PBKey key = new PBKey(dbName, user, passwd);
return key;
}
/**
* Check if the user of the given PBKey was <code>null</code>, if so we try to
* get user/password from the jdbc-connection-descriptor matching the given
* PBKey.getAlias().
*/
public static PBKey crossCheckPBKey(PBKey key)
{
if(key.getUser() == null)
{
PBKey defKey = MetadataManager.getInstance().connectionRepository().getStandardPBKeyForJcdAlias(key.getAlias());
if(defKey != null)
{
return defKey;
}
}
return key;
}
/**
* Answer the real ClassDescriptor for anObj
* ie. aCld may be an Interface of anObj, so the cld for anObj is returned
*/
private ClassDescriptor getRealClassDescriptor(ClassDescriptor aCld, Object anObj)
{
ClassDescriptor result;
if(aCld.getClassOfObject() == anObj.getClass())
{
result = aCld;
}
else
{
result = aCld.getRepository().getDescriptorFor(anObj.getClass());
}
return result;
}
/**
* Returns an Array with an Objects PK VALUES if convertToSql is true, any
* associated java-to-sql conversions are applied. If the Object is a Proxy
* or a VirtualProxy NO conversion is necessary.
*
* @param objectOrProxy
* @param convertToSql
* @return Object[]
* @throws PersistenceBrokerException
*/
public ValueContainer[] getKeyValues(ClassDescriptor cld, Object objectOrProxy, boolean convertToSql) throws PersistenceBrokerException
{
IndirectionHandler handler = ProxyHelper.getIndirectionHandler(objectOrProxy);
if(handler != null)
{
return getKeyValues(cld, handler.getIdentity(), convertToSql); //BRJ: convert Identity
}
else
{
ClassDescriptor realCld = getRealClassDescriptor(cld, objectOrProxy);
return getValuesForObject(realCld.getPkFields(), objectOrProxy, convertToSql);
}
}
/**
* Return primary key values of given Identity object.
*
* @param cld
* @param oid
* @return Object[]
* @throws PersistenceBrokerException
*/
public ValueContainer[] getKeyValues(ClassDescriptor cld, Identity oid) throws PersistenceBrokerException
{
return getKeyValues(cld, oid, true);
}
/**
* Return key Values of an Identity
* @param cld
* @param oid
* @param convertToSql
* @return Object[]
* @throws PersistenceBrokerException
*/
public ValueContainer[] getKeyValues(ClassDescriptor cld, Identity oid, boolean convertToSql) throws PersistenceBrokerException
{
FieldDescriptor[] pkFields = cld.getPkFields();
ValueContainer[] result = new ValueContainer[pkFields.length];
Object[] pkValues = oid.getPrimaryKeyValues();
try
{
for(int i = 0; i < result.length; i++)
{
FieldDescriptor fd = pkFields[i];
Object cv = pkValues[i];
if(convertToSql)
{
// BRJ : apply type and value mapping
cv = fd.getFieldConversion().javaToSql(cv);
}
result[i] = new ValueContainer(cv, fd.getJdbcType());
}
}
catch(Exception e)
{
throw new PersistenceBrokerException("Can't generate primary key values for given Identity " + oid, e);
}
return result;
}
/**
* returns an Array with an Objects PK VALUES, with any java-to-sql
* FieldConversion applied. If the Object is a Proxy or a VirtualProxy NO
* conversion is necessary.
*
* @param objectOrProxy
* @return Object[]
* @throws PersistenceBrokerException
*/
public ValueContainer[] getKeyValues(ClassDescriptor cld, Object objectOrProxy) throws PersistenceBrokerException
{
return getKeyValues(cld, objectOrProxy, true);
}
/**
* Decide if the given object value represents 'null'.<br/>
*
* - If given value is 'null' itself, true will be returned<br/>
*
* - If given value is instance of Number with value 0 and the field-descriptor
* represents a primitive field, true will be returned<br/>
*
* - If given value is instance of String with length 0 and the field-descriptor
* is a primary key, true will be returned<br/>
*/
public boolean representsNull(FieldDescriptor fld, Object aValue)
{
if(aValue == null) return true;
boolean result = false;
if(((aValue instanceof Number) && (((Number) aValue).longValue() == 0)))
{
Class type = fld.getPersistentField().getType();
/*
AnonymousPersistentFields will *always* have a null type according to the
javadoc comments in AnonymousPersistentField.getType() and never represents
a primitve java field with value 0, thus we return always 'false' in this case.
(If the value object is null, the first check above return true)
*/
if(type != null)
{
result = type.isPrimitive();
}
}
// TODO: Do we need this check?? String could be nullified, why should we assume
// it's 'null' on empty string?
else if((aValue instanceof String) && (((String) aValue).length() == 0))
{
result = fld.isPrimaryKey();
}
return result;
}
/**
* Detect if the given object has a PK field represents a 'null' value.
*/
public boolean hasNullPKField(ClassDescriptor cld, Object obj)
{
FieldDescriptor[] fields = cld.getPkFields();
boolean hasNull = false;
// an unmaterialized proxy object can never have nullified PK's
IndirectionHandler handler = ProxyHelper.getIndirectionHandler(obj);
if(handler == null || handler.alreadyMaterialized())
{
if(handler != null) obj = handler.getRealSubject();
FieldDescriptor fld;
for(int i = 0; i < fields.length; i++)
{
fld = fields[i];
hasNull = representsNull(fld, fld.getPersistentField().get(obj));
if(hasNull) break;
}
}
return hasNull;
}
/**
* Set an autoincremented value in given object field that has already
* had a field conversion run on it, if an value for the given field is
* already set, it will be overridden - no further checks are done.
* <p>
* The data type of the value that is returned by this method is
* compatible with the java-world. The return value has <b>NOT</b>
* been run through a field conversion and converted to a corresponding
* sql-type.
*
* @return the autoincremented value set on given object
* @throws PersistenceBrokerException if there is an erros accessing obj field values
*/
private Object setAutoIncrementValue(FieldDescriptor fd, Object obj)
{
PersistentField f = fd.getPersistentField();
try
{
// lookup SeqMan for a value matching db column an
Object result = m_broker.serviceSequenceManager().getUniqueValue(fd);
// reflect autoincrement value back into object
f.set(obj, result);
return result;
}
catch(MetadataException e)
{
throw new PersistenceBrokerException(
"Error while trying to autoincrement field " + f.getDeclaringClass() + "#" + f.getName(),
e);
}
catch(SequenceManagerException e)
{
throw new PersistenceBrokerException("Could not get key value", e);
}
}
/**
* Get the values of the fields for an obj
* @param fields
* @param obj
* @throws PersistenceBrokerException
*/
public ValueContainer[] getValuesForObject(FieldDescriptor[] fields, Object obj, boolean convertToSql) throws PersistenceBrokerException
{
ValueContainer[] result = new ValueContainer[fields.length];
for(int i = 0; i < fields.length; i++)
{
FieldDescriptor fd = fields[i];
Object cv = fd.getPersistentField().get(obj);
/*
handle autoincrement attributes if
- is a autoincrement field
- field represents a 'null' value, is nullified
and generate a new value
*/
if(fd.isAutoIncrement() && representsNull(fd, cv))
{
/*
setAutoIncrementValue returns a value that is
properly typed for the java-world. This value
needs to be converted to it's corresponding
sql type so that the entire result array contains
objects that are properly typed for sql.
*/
cv = setAutoIncrementValue(fd, obj);
}
if(convertToSql)
{
// apply type and value conversion
cv = fd.getFieldConversion().javaToSql(cv);
}
// create ValueContainer
result[i] = new ValueContainer(cv, fd.getJdbcType());
}
return result;
}
/**
* returns an Array with an Objects NON-PK VALUES (READ/WRITE only)
* @throws MetadataException if there is an erros accessing o field values
*/
public ValueContainer[] getNonKeyRwValues(ClassDescriptor cld, Object obj) throws PersistenceBrokerException
{
ClassDescriptor realCld = getRealClassDescriptor(cld, obj);
return getValuesForObject(realCld.getNonPkRwFields(), obj, true);
}
/**
* returns an array containing values for all the Objects attribute (READ/WRITE only)
* @throws MetadataException if there is an erros accessing obj field values
*/
public ValueContainer[] getAllRwValues(ClassDescriptor cld, Object obj) throws PersistenceBrokerException
{
ClassDescriptor realCld = getRealClassDescriptor(cld, obj);
return getValuesForObject(realCld.getAllRwFields(), obj, true);
}
/**
* Extract an value array of the given {@link ValueContainer} array.
* @param containers
* @return
*/
public Object[] extractValueArray(ValueContainer[] containers)
{
Object[] result = new Object[containers.length];
for(int i = 0; i < containers.length; i++)
{
result[i] = containers[i].getValue();
}
return result;
}
/**
* returns true if the primary key fields are valid, else false.
* PK fields are valid if each of them is either an OJB managed
* attribute (autoincrement or locking) or if it contains
* a valid non-null value
* @param fieldDescriptors the array of PK fielddescriptors
* @param pkValues the array of PK values
* @return boolean
*/
public boolean assertValidPkFields(FieldDescriptor[] fieldDescriptors, Object[] pkValues)
{
int fieldDescriptorSize = fieldDescriptors.length;
for(int i = 0; i < fieldDescriptorSize; i++)
{
/**
* a pk field is valid if it is either managed by OJB
* (autoincrement or locking) or if it does contain a
* valid non-null value.
*/
if(!(fieldDescriptors[i].isAutoIncrement()
|| fieldDescriptors[i].isLocking()
|| assertValidPkValue(pkValues[i])))
{
return false;
}
}
return true;
}
/**
* returns true if a value is non-null, STring instances are also checked,
* if they are non-empty.
* @param pkValue the value to check
* @return boolean
*/
private boolean assertValidPkValue(Object pkValue)
{
// null as value of a primary key is not acceptable
if(pkValue == null)
{
return false;
}
if(pkValue instanceof String)
{
// the toString() method on a String-object is maybe faster
// than the downcast to String. Also use length() to test
// if a String empty or not, this is faster than the comparing
// a String-object with an empty string using the equals()-method.
if(pkValue.toString().trim().length() == 0)
{
return false;
}
}
return true;
}
/**
* Build a Count-Query based on aQuery
* @param aQuery
* @return
*/
public Query getCountQuery(Query aQuery)
{
if(aQuery instanceof QueryBySQL)
{
return getQueryBySqlCount((QueryBySQL) aQuery);
}
else if(aQuery instanceof ReportQueryByCriteria)
{
return getReportQueryByCriteriaCount((ReportQueryByCriteria) aQuery);
}
else
{
return getQueryByCriteriaCount((QueryByCriteria) aQuery);
}
}
/**
* Create a Count-Query for QueryBySQL
*
* @param aQuery
* @return
*/
private Query getQueryBySqlCount(QueryBySQL aQuery)
{
String countSql = aQuery.getSql();
int fromPos = countSql.toUpperCase().indexOf(" FROM ");
if(fromPos >= 0)
{
countSql = "select count(*)" + countSql.substring(fromPos);
}
int orderPos = countSql.toUpperCase().indexOf(" ORDER BY ");
if(orderPos >= 0)
{
countSql = countSql.substring(0, orderPos);
}
return new QueryBySQL(aQuery.getSearchClass(), countSql);
}
/**
* Create a Count-Query for QueryByCriteria
*
* @param aQuery
* @return
*/
private Query getQueryByCriteriaCount(QueryByCriteria aQuery)
{
Class searchClass = aQuery.getSearchClass();
ReportQueryByCriteria countQuery;
Criteria countCrit = null;
String[] columns = new String[1];
// BRJ: copied Criteria without groupby, orderby, and prefetched relationships
if (aQuery.getCriteria() != null)
{
countCrit = aQuery.getCriteria().copy(false,false,false);
}
if(aQuery.isDistinct())
{
//
// BRJ: Count distinct is dbms dependent
// hsql/sapdb: select count (distinct(person_id || project_id)) from person_project
// mysql: select count (distinct person_id,project_id) from person_project
//
// concatenation of pk-columns is a simple way to obtain a single column
// but concatenation is also dbms dependent:
//
// SELECT count(distinct concat(row1, row2, row3)) mysql
// SELECT count(distinct (row1 || row2 || row3)) ansi
// SELECT count(distinct (row1 + row2 + row3)) ms sql-server
//
FieldDescriptor[] pkFields = m_broker.getClassDescriptor(searchClass).getPkFields();
String[] keyColumns = new String[pkFields.length];
if(pkFields.length > 1)
{
// TODO: Use ColumnName. This is a temporary solution because
// we cannot yet resolve multiple columns in the same attribute.
for(int i = 0; i < pkFields.length; i++)
{
keyColumns[i] = pkFields[i].getColumnName();
}
}
else
{
for(int i = 0; i < pkFields.length; i++)
{
keyColumns[i] = pkFields[i].getAttributeName();
}
}
columns[0] = "count(distinct " + getPlatform().concatenate(keyColumns) + ")";
}
else
{
columns[0] = "count(*)";
}
// BRJ: we have to preserve indirection table !
if(aQuery instanceof MtoNQuery)
{
MtoNQuery mnQuery = (MtoNQuery) aQuery;
ReportQueryByMtoNCriteria mnReportQuery = new ReportQueryByMtoNCriteria(searchClass,
columns, countCrit);
mnReportQuery.setIndirectionTable(mnQuery.getIndirectionTable());
countQuery = mnReportQuery;
}
else
{
countQuery = new ReportQueryByCriteria(searchClass, columns, countCrit);
}
// BRJ: we have to preserve outer-join-settings (by Andr� Markwalder)
Iterator outerJoinPath = aQuery.getOuterJoinPaths().iterator();
while (outerJoinPath.hasNext())
{
String path = (String) outerJoinPath.next();
if (aQuery.isPathOuterJoin(path))
{
countQuery.setPathOuterJoin(path);
}
}
//BRJ: add orderBy Columns asJoinAttributes
List orderBy = aQuery.getOrderBy();
if (orderBy != null && !orderBy.isEmpty())
{
String[] joinAttributes = new String[orderBy.size()];
for (int i=0; i<orderBy.size(); i++)
{
joinAttributes[i] = ((FieldHelper)orderBy.get(i)).name;
}
countQuery.setJoinAttributes(joinAttributes);
}
return countQuery;
}
/**
* Create a Count-Query for ReportQueryByCriteria
*
* @param aQuery
* @return
*/
private Query getReportQueryByCriteriaCount(ReportQueryByCriteria aQuery)
{
ReportQueryByCriteria countQuery = (ReportQueryByCriteria) getQueryByCriteriaCount(aQuery);
// BRJ: keep the original columns to build the Join
countQuery.setJoinAttributes(aQuery.getAttributes());
// BRJ: we have to preserve groupby information
Iterator iter = aQuery.getGroupBy().iterator();
while(iter.hasNext())
{
countQuery.addGroupBy((FieldHelper) iter.next());
}
return countQuery;
}
/**
* answer the platform
*
* @return the platform
*/
private Platform getPlatform()
{
return m_broker.serviceSqlGenerator().getPlatform();
}
/*
NOTE: use WeakHashMap to allow reclaiming of no longer used ClassDescriptor
instances
*/
private Map sqlSelectMap = new WeakHashMap();
/**
* TODO: This method should be moved to {@link org.apache.ojb.broker.accesslayer.JdbcAccess}
* before 1.1 release. This method only checks if the requested object can be
* found in DB (without full object materialization).
*/
public boolean doesExist(ClassDescriptor cld, Identity oid, Object obj)
{
boolean result = false;
String sql = (String) sqlSelectMap.get(cld);
if(sql == null)
{
sql = new SqlExistStatement(cld, LoggerFactory.getDefaultLogger()).getStatement();
sqlSelectMap.put(cld, sql);
}
StatementManagerIF sm = m_broker.serviceStatementManager();
PreparedStatement stmt = null;
ResultSet rs = null;
try
{
stmt = sm.getPreparedStatement(cld, sql, false);
sm.bindSelect(stmt, oid, cld);
rs = stmt.executeQuery();
result = rs.next();
}
catch(SQLException e)
{
LoggerFactory.getDefaultLogger().error("[BrokerHelper#doesExist] Can't check for existance", e);
throw new PersistenceBrokerSQLException("Exist check failed for identity " + oid
+ ", sql was " + sql, e);
}
finally
{
sm.closeResources(stmt, rs);
}
return result;
}
/**
* This method concatenate the main object with all reference
* objects (1:1, 1:n and m:n) by hand. This method is needed when
* in the reference metadata definitions the auto-xxx setting was disabled.
* More info see OJB doc.
*/
public void link(Object obj, boolean insert)
{
linkOrUnlink(true, obj, insert);
}
/**
* Unlink all references from this object.
* More info see OJB doc.
* @param obj Object with reference
*/
public void unlink(Object obj)
{
linkOrUnlink(false, obj, false);
}
private void linkOrUnlink(boolean doLink, Object obj, boolean insert)
{
ClassDescriptor cld = m_broker.getDescriptorRepository().getDescriptorFor(obj.getClass());
if (cld.getObjectReferenceDescriptors().size() > 0)
{
// never returns null, thus we can direct call iterator
Iterator descriptors = cld.getObjectReferenceDescriptors().iterator();
while (descriptors.hasNext())
{
ObjectReferenceDescriptor ord = (ObjectReferenceDescriptor) descriptors.next();
linkOrUnlinkOneToOne(doLink, obj, ord, insert);
}
}
if (cld.getCollectionDescriptors().size() > 0)
{
// never returns null, thus we can direct call iterator
Iterator descriptors = cld.getCollectionDescriptors().iterator();
while (descriptors.hasNext())
{
CollectionDescriptor cod = (CollectionDescriptor) descriptors.next();
linkOrUnlinkXToMany(doLink, obj, cod, insert);
}
}
}
/**
* This method concatenate the main object and the specified reference
* object (1:1 reference a referenced object, 1:n and m:n reference a
* collection of referenced objects) by hand. This method is needed when
* in the reference metadata definitions the auto-xxx setting was disabled.
* More info see OJB doc.
*
* @param obj Object with reference
* @param ord the ObjectReferenceDescriptor of the reference
* @param insert flag signals insert operation
*/
public void link(Object obj, ObjectReferenceDescriptor ord, boolean insert)
{
linkOrUnlink(true, obj, ord, insert);
}
/**
* This method concatenate the main object and the specified reference
* object (1:1 reference a referenced object, 1:n and m:n reference a
* collection of referenced objects) by hand. This method is needed when
* in the reference metadata definitions the auto-xxx setting was disabled.
* More info see OJB doc.
*
* @param obj Object with reference
* @param attributeName field name of the reference
* @param insert flag signals insert operation
* @return true if the specified reference was found and linking was successful
*/
public boolean link(Object obj, String attributeName, boolean insert)
{
return linkOrUnlink(true, obj, attributeName, insert);
}
/**
* Unlink the specified reference from this object.
* More info see OJB doc.
* @param obj Object with reference
* @param attributeName field name of the reference
*/
public boolean unlink(Object obj, String attributeName)
{
return linkOrUnlink(false, obj, attributeName, false);
}
/**
* Unlink the specified reference from this object.
* More info see OJB doc.
*
* @param obj Object with reference
* @param ord the ObjectReferenceDescriptor of the reference
* @param insert flag signals insert operation
*/
public void unlink(Object obj, ObjectReferenceDescriptor ord, boolean insert)
{
linkOrUnlink(false, obj, ord, insert);
}
private boolean linkOrUnlink(boolean doLink, Object obj, String attributeName, boolean insert)
{
boolean match = false;
ClassDescriptor cld = m_broker.getDescriptorRepository().getDescriptorFor(ProxyHelper.getRealClass(obj));
ObjectReferenceDescriptor ord;
// first look for reference then for collection
ord = cld.getObjectReferenceDescriptorByName(attributeName);
if (ord != null)
{
linkOrUnlinkOneToOne(doLink, obj, ord, insert);
match = true;
}
else
{
CollectionDescriptor cod = cld.getCollectionDescriptorByName(attributeName);
if (cod != null)
{
linkOrUnlinkXToMany(doLink, obj, cod, insert);
match = true;
}
}
return match;
}
private void linkOrUnlink(boolean doLink, Object obj, ObjectReferenceDescriptor ord, boolean insert)
{
if (ord instanceof CollectionDescriptor)
{
linkOrUnlinkXToMany(doLink, obj, (CollectionDescriptor) ord, insert);
}
else
{
linkOrUnlinkOneToOne(doLink, obj, ord, insert);
}
}
private void linkOrUnlinkXToMany(boolean doLink, Object obj, CollectionDescriptor cod, boolean insert)
{
if (doLink)
{
if (cod.isMtoNRelation())
{
m_broker.linkMtoN(obj, cod, insert);
}
else
{
m_broker.linkOneToMany(obj, cod, insert);
}
}
else
{
m_broker.unlinkXtoN(obj, cod);
}
}
private void linkOrUnlinkOneToOne(boolean doLink, Object obj, ObjectReferenceDescriptor ord, boolean insert)
{
ClassDescriptor cld = m_broker.getDescriptorRepository().getDescriptorFor(ProxyHelper.getRealClass(obj));
if (doLink)
{
m_broker.linkOneToOne(obj, cld, ord, insert);
}
else
{
m_broker.unlinkFK(obj, cld, ord);
// in 1:1 relation we have to set relation to null
ord.getPersistentField().set(obj, null);
}
}
/**
* Unlink a bunch of 1:n or m:n objects.
*
* @param source The source object with reference.
* @param cds The {@link org.apache.ojb.broker.metadata.CollectionDescriptor} of the relation.
* @param referencesToUnlink List of referenced objects to unlink.
*/
public void unlink(Object source, CollectionDescriptor cds, List referencesToUnlink)
{
for(int i = 0; i < referencesToUnlink.size(); i++)
{
unlink(source, cds, referencesToUnlink.get(i));
}
}
/**
* Unlink a single 1:n or m:n object.
*
* @param source The source object with reference.
* @param cds The {@link org.apache.ojb.broker.metadata.CollectionDescriptor} of the relation.
* @param referenceToUnlink The referenced object to link.
*/
public void unlink(Object source, CollectionDescriptor cds, Object referenceToUnlink)
{
if(cds.isMtoNRelation())
{
m_broker.deleteMtoNImplementor(new MtoNImplementor(source, referenceToUnlink));
}
else
{
ClassDescriptor cld = m_broker.getClassDescriptor(referenceToUnlink.getClass());
m_broker.unlinkFK(referenceToUnlink, cld, cds);
}
}
/**
* Link a bunch of 1:n or m:n objects.
*
* @param source The source object with reference.
* @param cds The {@link org.apache.ojb.broker.metadata.CollectionDescriptor} of the relation.
* @param referencesToLink List of referenced objects to link.
*/
public void link(Object source, CollectionDescriptor cds, List referencesToLink)
{
for(int i = 0; i < referencesToLink.size(); i++)
{
link(source, cds, referencesToLink.get(i));
}
}
/**
* Link a single 1:n or m:n object.
*
* @param source The source object with reference.
* @param cds The {@link org.apache.ojb.broker.metadata.CollectionDescriptor} of the relation.
* @param referenceToLink The referenced object to link.
*/
public void link(Object source, CollectionDescriptor cds, Object referenceToLink)
{
if(cds.isMtoNRelation())
{
m_broker.addMtoNImplementor(new MtoNImplementor(source, referenceToLink));
}
else
{
ClassDescriptor cld = m_broker.getClassDescriptor(referenceToLink.getClass());
m_broker.link(referenceToLink, cld, cds, source, false);
}
}
/**
* Returns an Iterator instance for {@link java.util.Collection}, object Array or
* {@link org.apache.ojb.broker.ManageableCollection} instances.
*
* @param collectionOrArray a none <em>null</em> object of type {@link java.util.Collection},
* Array or {@link org.apache.ojb.broker.ManageableCollection}.
* @return Iterator able to handle given collection object
*/
public static Iterator getCollectionIterator(Object collectionOrArray)
{
Iterator colIterator;
if (collectionOrArray instanceof ManageableCollection)
{
colIterator = ((ManageableCollection) collectionOrArray).ojbIterator();
}
else if (collectionOrArray instanceof Collection)
{
colIterator = ((Collection) collectionOrArray).iterator();
}
else if (collectionOrArray.getClass().isArray())
{
colIterator = new ArrayIterator(collectionOrArray);
}
else
{
throw new OJBRuntimeException( "Given object collection of type '"
+ (collectionOrArray != null ? collectionOrArray.getClass().toString() : "null")
+ "' can not be managed by OJB. Use Array, Collection or ManageableCollection instead!");
}
return colIterator;
}
/**
* Returns an object array for {@link java.util.Collection}, array or
* {@link org.apache.ojb.broker.ManageableCollection} instances.
*
* @param collectionOrArray a none <em>null</em> object of type {@link java.util.Collection},
* Array or {@link org.apache.ojb.broker.ManageableCollection}.
* @return Object array able to handle given collection or array object
*/
public static Object[] getCollectionArray(Object collectionOrArray)
{
Object[] result = null;
if (collectionOrArray instanceof Collection)
{
result = ((Collection) collectionOrArray).toArray();
}
else if (collectionOrArray instanceof ManageableCollection)
{
Collection newCol = new ArrayList();
CollectionUtils.addAll(newCol, ((ManageableCollection) collectionOrArray).ojbIterator());
result = newCol.toArray();
}
else if (collectionOrArray.getClass().isArray())
{
result = (Object[]) collectionOrArray;
}
else
{
throw new OJBRuntimeException( "Given object collection of type '"
+ (collectionOrArray != null ? collectionOrArray.getClass().toString() : "null")
+ "' can not be managed by OJB. Use Array, Collection or ManageableCollection instead!");
}
return result;
}
/**
* Returns <em>true</em> if one or more anonymous FK fields are used.
* @param cld The {@link org.apache.ojb.broker.metadata.ClassDescriptor} of the main object.
* @param rds The {@link org.apache.ojb.broker.metadata.ObjectReferenceDescriptor} of the referenced object.
* @return <em>true</em> if one or more anonymous FK fields are used for specified reference.
*/
public static boolean hasAnonymousKeyReference(ClassDescriptor cld, ObjectReferenceDescriptor rds)
{
boolean result = false;
FieldDescriptor[] fkFields = rds.getForeignKeyFieldDescriptors(cld);
for(int i = 0; i < fkFields.length; i++)
{
FieldDescriptor fkField = fkFields[i];
if(fkField.isAnonymous())
{
result = true;
break;
}
}
return result;
}
// /**
// * Returns a {@link java.util.List} instance of the specified object in method argument,
// * in which the argument must be of type {@link java.util.Collection}, array or
// * {@link org.apache.ojb.broker.ManageableCollection}.
// *
// * @param collectionOrArray a none <em>null</em> object of type {@link java.util.Collection},
// * Array or {@link org.apache.ojb.broker.ManageableCollection}.
// * @return Object array able to handle given collection or array object
// */
// public static List getCollectionList(Object collectionOrArray)
// {
// List result = null;
// if (collectionOrArray instanceof Collection)
// {
// result = ((Collection) collectionOrArray).toArray();
// }
// else if (collectionOrArray instanceof ManageableCollection)
// {
// Collection newCol = new ArrayList();
// CollectionUtils.addAll(newCol, ((ManageableCollection) collectionOrArray).ojbIterator());
// result = newCol.toArray();
// }
// else if (collectionOrArray.getClass().isArray())
// {
// result = (Object[]) collectionOrArray;
// }
// else
// {
// throw new OJBRuntimeException( "Given object collection of type '"
// + (collectionOrArray != null ? collectionOrArray.getClass().toString() : "null")
// + "' can not be managed by OJB. Use Array, Collection or ManageableCollection instead!");
// }
// return result;
// }
}