use of org.eclipse.persistence.indirection.IndirectList in project eclipselink by eclipse-ee4j.
the class AggregateCollectionMapping method compareListsAndWrite_UpdatableListOrderField.
/**
* INTERNAL:
* Old and new lists are compared and only the changes are written to the database.
* Called only if listOrderField != null
*/
protected void compareListsAndWrite_UpdatableListOrderField(List previousList, List currentList, WriteObjectQuery query) throws DatabaseException, OptimisticLockException {
boolean shouldRepairOrder = false;
if (currentList instanceof IndirectList) {
shouldRepairOrder = ((IndirectList) currentList).isListOrderBrokenInDb();
}
// Object[] = {previousObject, currentObject, previousIndex, currentIndex}
HashMap<Object, Object[]> previousAndCurrentByKey = new HashMap<>();
// a SortedMap, current index mapped by previous index, both indexes must exist and be not equal.
TreeMap<Integer, Integer> currentIndexByPreviousIndex = new TreeMap<>();
// First index the current objects by their primary key.
for (int i = 0; i < currentList.size(); i++) {
Object currentObject = currentList.get(i);
try {
Object primaryKey = getReferenceDescriptor().getObjectBuilder().extractPrimaryKeyFromObject(currentObject, query.getSession());
Object[] previousAndCurrent = new Object[] { null, currentObject, null, i };
previousAndCurrentByKey.put(primaryKey, previousAndCurrent);
} catch (NullPointerException e) {
// ideally the customer should check for these themselves.
if (currentObject != null) {
throw e;
}
}
}
// Next index the previous objects (read from db or from backup in uow), also remove the objects to be removed.
for (int i = 0; i < previousList.size(); i++) {
Object previousObject = previousList.get(i);
Object primaryKey = getReferenceDescriptor().getObjectBuilder().extractPrimaryKeyFromObject(previousObject, query.getSession());
Object[] previousAndCurrent = previousAndCurrentByKey.get(primaryKey);
if (previousAndCurrent == null) {
// there's no current object - that means that previous object should be deleted
objectRemovedDuringUpdate(query, previousObject, null);
} else {
previousAndCurrent[0] = previousObject;
previousAndCurrent[2] = i;
int iCurrent = (Integer) previousAndCurrent[3];
if (i != iCurrent || shouldRepairOrder) {
currentIndexByPreviousIndex.put(i, iCurrent);
}
}
}
// some order indexes should be changed
if (!currentIndexByPreviousIndex.isEmpty()) {
boolean shouldUpdateOrderUsingPk = shouldRepairOrder;
if (!shouldUpdateOrderUsingPk) {
// search for cycles in order changes, such as, for instance:
// previous index 1, 2
// current index 2, 1
// or
// previous index 1, 3, 5
// current index 3, 5, 1
// those objects order index can't be updated using their previous order index value - should use pk in where clause instead.
// For now, if a cycle is found let's update all order indexes using pk.
// Ideally that should be refined in the future so that only indexes participating in cycles updated using pks - others still through bulk update.
boolean isCycleFound = false;
int iCurrentMax = -1;
Iterator<Integer> itCurrentIndexes = currentIndexByPreviousIndex.values().iterator();
while (itCurrentIndexes.hasNext() && !isCycleFound) {
int iCurrent = itCurrentIndexes.next();
if (iCurrent > iCurrentMax) {
iCurrentMax = iCurrent;
} else {
isCycleFound = true;
}
}
shouldUpdateOrderUsingPk = isCycleFound;
}
if (shouldUpdateOrderUsingPk) {
Iterator<Map.Entry<Object, Object[]>> it = previousAndCurrentByKey.entrySet().iterator();
while (it.hasNext()) {
Map.Entry<Object, Object[]> entry = it.next();
Object key = entry.getKey();
Object[] previousAndCurrent = entry.getValue();
// previousObject may be null, meaning currentObject has been added to the list
Object previousObject = previousAndCurrent[0];
if (previousObject != null) {
Object currentObject = previousAndCurrent[1];
if (!this.isEntireObjectPK) {
objectUnchangedDuringUpdate(query, currentObject, previousObject);
}
int iPrevious = (Integer) previousAndCurrent[2];
int iCurrent = (Integer) previousAndCurrent[3];
if (iPrevious != iCurrent || shouldRepairOrder) {
objectChangedListOrderDuringUpdate(query, key, iCurrent);
}
}
}
} else {
// update the objects - but not their order values
if (!this.isEntireObjectPK) {
Iterator<Map.Entry<Object, Object[]>> iterator = previousAndCurrentByKey.entrySet().iterator();
while (iterator.hasNext()) {
Map.Entry<Object, Object[]> entry = iterator.next();
Object[] previousAndCurrent = entry.getValue();
// previousObject may be null, meaning currentObject has been added to the list
Object previousObject = previousAndCurrent[0];
if (previousObject != null) {
Object currentObject = previousAndCurrent[1];
objectUnchangedDuringUpdate(query, currentObject, previousObject);
}
}
}
// a bulk update query will be executed for each bunch of adjacent previous indexes from which current indexes could be obtained with a shift, for instance:
// previous index 1, 2, 3
// current index 5, 6, 7
// the sql will look like:
// UPDATE ... SET ListOrderField = ListOrderField + 4 WHERE 1 <= ListOrderField AND ListOrderField <= 3 AND FK = ...
int iMin = -1;
int iMax = -1;
int iShift = 0;
// each index corresponds to a bunch of objects to be shifted
ArrayList<Integer> iMinList = new ArrayList();
ArrayList<Integer> iMaxList = new ArrayList();
ArrayList<Integer> iShiftList = new ArrayList();
Iterator<Map.Entry<Integer, Integer>> itEntries = currentIndexByPreviousIndex.entrySet().iterator();
while (itEntries.hasNext()) {
Map.Entry<Integer, Integer> entry = itEntries.next();
int iPrevious = entry.getKey();
int iCurrent = entry.getValue();
if (iMin >= 0) {
// the shift should be the same for all indexes participating in bulk update
int iPreviousExpected = iMax + 1;
if (iPrevious == iPreviousExpected && iCurrent == iPreviousExpected + iShift) {
iMax++;
} else {
iMinList.add(iMin);
iMaxList.add(iMax);
iShiftList.add(iShift);
iMin = -1;
}
}
if (iMin == -1) {
// start defining a new bulk update - define iShift, iFirst, iLast
iMin = iPrevious;
iMax = iPrevious;
iShift = iCurrent - iPrevious;
}
}
if (iMin >= 0) {
iMinList.add(iMin);
iMaxList.add(iMax);
iShiftList.add(iShift);
}
// Look for the left-most and right-most bunches and update them first.
while (!iMinList.isEmpty()) {
int iMinLeft = previousList.size() + 1;
int iMinRight = -1;
int indexShiftLeft = -1;
int indexShiftRight = -1;
for (int i = 0; i < iMinList.size(); i++) {
iMin = iMinList.get(i);
iShift = iShiftList.get(i);
if (iShift < 0) {
if (iMin < iMinLeft) {
iMinLeft = iMin;
indexShiftLeft = i;
}
} else {
// iShift > 0
if (iMin > iMinRight) {
iMinRight = iMin;
indexShiftRight = i;
}
}
}
if (indexShiftLeft >= 0) {
objectChangedListOrderDuringUpdate(query, iMinList.get(indexShiftLeft), iMaxList.get(indexShiftLeft), iShiftList.get(indexShiftLeft));
}
if (indexShiftRight >= 0) {
objectChangedListOrderDuringUpdate(query, iMinList.get(indexShiftRight), iMaxList.get(indexShiftRight), iShiftList.get(indexShiftRight));
}
if (indexShiftLeft >= 0) {
iMinList.remove(indexShiftLeft);
iMaxList.remove(indexShiftLeft);
iShiftList.remove(indexShiftLeft);
}
if (indexShiftRight >= 0) {
iMinList.remove(indexShiftRight);
iMaxList.remove(indexShiftRight);
iShiftList.remove(indexShiftRight);
}
}
}
}
// Add the new objects
Iterator<Map.Entry<Object, Object[]>> iterator = previousAndCurrentByKey.entrySet().iterator();
while (iterator.hasNext()) {
Map.Entry<Object, Object[]> entry = iterator.next();
Object[] previousAndCurrent = entry.getValue();
// previousObject may be null, meaning currentObject has been added to the list
Object previousObject = previousAndCurrent[0];
if (previousObject == null) {
// there's no previous object - that means that current object should be added.
// currentObject is not null
Object currentObject = previousAndCurrent[1];
// index of currentObject in currentList
int iCurrent = (Integer) previousAndCurrent[3];
DatabaseRecord extraData = new DatabaseRecord(1);
extraData.put(this.listOrderField, iCurrent);
objectAddedDuringUpdate(query, currentObject, null, extraData);
}
}
if (shouldRepairOrder) {
((IndirectList) currentList).setIsListOrderBrokenInDb(false);
}
}
use of org.eclipse.persistence.indirection.IndirectList in project eclipselink by eclipse-ee4j.
the class EntityManagerJUnitTestSuite method testForUOWInSharedCacheWithBatchQueryHint.
/**
* This test ensures that the eclipselink.batch query hint works. It tests
* two things.
*
* 1. That the batch read attribute is properly added to the queyr 2. That
* the query will execute
*
* It does not do any verification that the batch reading feature actually
* works. That is left for the batch reading testing to do.
*/
public void testForUOWInSharedCacheWithBatchQueryHint() {
if (isOnServer()) {
// Can not unwrap on WLS.
return;
}
int id1 = 0;
EntityManager em = createEntityManager();
beginTransaction(em);
Employee manager = new Employee();
manager.setFirstName("Marvin");
manager.setLastName("Malone");
PhoneNumber number = new PhoneNumber("cell", "613", "888-8888");
manager.addPhoneNumber(number);
number = new PhoneNumber("home", "613", "888-8880");
manager.addPhoneNumber(number);
em.persist(manager);
id1 = manager.getId();
Employee emp = new Employee();
emp.setFirstName("Melvin");
emp.setLastName("Malone");
emp.setManager(manager);
manager.addManagedEmployee(emp);
number = new PhoneNumber("cell", "613", "888-9888");
emp.addPhoneNumber(number);
number = new PhoneNumber("home", "613", "888-0880");
emp.addPhoneNumber(number);
em.persist(emp);
emp = new Employee();
emp.setFirstName("David");
emp.setLastName("Malone");
emp.setManager(manager);
manager.addManagedEmployee(emp);
number = new PhoneNumber("cell", "613", "888-9988");
emp.addPhoneNumber(number);
number = new PhoneNumber("home", "613", "888-0980");
emp.addPhoneNumber(number);
em.persist(emp);
commitTransaction(em);
closeEntityManager(em);
clearCache();
// org.eclipse.persistence.jpa.JpaQuery query =
// (org.eclipse.persistence.jpa.JpaQuery)em.createQuery("SELECT e FROM Employee e WHERE e.lastName = 'Malone' order by e.firstName");
em = createEntityManager();
beginTransaction(em);
org.eclipse.persistence.jpa.JpaQuery query = (org.eclipse.persistence.jpa.JpaQuery) em.createQuery("SELECT e FROM Employee e WHERE e.lastName = 'Malone' order by e.firstName");
query.setHint(QueryHints.BATCH, "e.phoneNumbers");
List resultList = query.getResultList();
emp = (Employee) resultList.get(0);
emp.setFirstName("somethingelse" + System.currentTimeMillis());
// execute random other query
em.createQuery("SELECT e FROM Employee e WHERE e.lastName = 'Malone'").getResultList();
((Employee) resultList.get(1)).getPhoneNumbers().hashCode();
commitTransaction(em);
try {
emp = (Employee) JpaHelper.getEntityManager(em).getServerSession().getIdentityMapAccessor().getFromIdentityMap(emp);
assertNotNull("Error, phone numbers is empty. Not Batch Read", emp.getPhoneNumbers());
assertFalse("PhoneNumbers was empty. This should not be the case as the test created phone numbers", emp.getPhoneNumbers().isEmpty());
assertTrue("Phonee numbers was not an indirectList", emp.getPhoneNumbers() instanceof IndirectList);
assertNotNull("valueholder was null in triggered batch attribute", ((IndirectList) emp.getPhoneNumbers()).getValueHolder());
QueryBasedValueHolder bvh = (QueryBasedValueHolder) ((IndirectList) emp.getPhoneNumbers()).getValueHolder();
if (!usesSOP()) {
if (!(bvh instanceof BatchValueHolder)) {
fail("BatchValueHolder was expected, instead got " + bvh);
}
}
if (bvh.getQuery() != null && bvh.getQuery().getSession() != null && bvh.getQuery().getSession().isUnitOfWork()) {
fail("In Shared Cache a UOW was set within a BatchValueHolder's query object");
}
closeEntityManager(em);
} finally {
em = createEntityManager();
beginTransaction(em);
emp = em.find(Employee.class, id1);
Iterator<Employee> it = emp.getManagedEmployees().iterator();
while (it.hasNext()) {
Employee managedEmp = it.next();
it.remove();
managedEmp.setManager(null);
em.remove(managedEmp);
}
em.remove(emp);
commitTransaction(em);
}
}
use of org.eclipse.persistence.indirection.IndirectList in project eclipselink by eclipse-ee4j.
the class DirectCollectionMapping method postUpdateWithChangeSetListOrder.
/**
* INTERNAL:
* Update private owned part.
*/
protected void postUpdateWithChangeSetListOrder(WriteObjectQuery writeQuery) throws DatabaseException {
ObjectChangeSet changeSet = writeQuery.getObjectChangeSet();
DirectCollectionChangeRecord changeRecord = (DirectCollectionChangeRecord) changeSet.getChangesForAttributeNamed(this.getAttributeName());
if (changeRecord == null) {
return;
}
for (int index = 0; index < getReferenceKeyFields().size(); index++) {
DatabaseField referenceKey = getReferenceKeyFields().get(index);
DatabaseField sourceKey = getSourceKeyFields().get(index);
Object sourceKeyValue = writeQuery.getTranslationRow().get(sourceKey);
writeQuery.getTranslationRow().put(referenceKey, sourceKeyValue);
}
boolean shouldRepairOrder = false;
if (changeRecord.getLatestCollection() instanceof IndirectList) {
shouldRepairOrder = ((IndirectList) changeRecord.getLatestCollection()).isListOrderBrokenInDb();
}
if (shouldRepairOrder) {
// delete all members of collection
DeleteObjectQuery deleteQuery = new DeleteObjectQuery();
deleteQuery.setObject(writeQuery.getObject());
deleteQuery.setSession(writeQuery.getSession());
deleteQuery.setTranslationRow(writeQuery.getTranslationRow());
// Hey I might actually want to use an inner class here... ok array for now.
Object[] eventDeleteAll = new Object[2];
eventDeleteAll[0] = DeleteAll;
eventDeleteAll[1] = deleteQuery;
writeQuery.getSession().getCommitManager().addDataModificationEvent(this, eventDeleteAll);
// re-insert them back
for (int i = 0; i < ((List) changeRecord.getLatestCollection()).size(); i++) {
Object value = ((List) changeRecord.getLatestCollection()).get(i);
value = getFieldValue(value, writeQuery.getSession());
AbstractRecord insertRow = writeQuery.getTranslationRow().clone();
insertRow.add(getDirectField(), value);
insertRow.add(this.listOrderField, i);
// Hey I might actually want to use an inner class here... ok array for now.
Object[] event = new Object[3];
event[0] = Insert;
event[1] = getInsertQuery();
event[2] = insertRow;
writeQuery.getSession().getCommitManager().addDataModificationEvent(this, event);
}
((IndirectList) changeRecord.getLatestCollection()).setIsListOrderBrokenInDb(false);
changeRecord.setOrderHasBeenRepaired(true);
return;
}
if (changeRecord.getChangedIndexes() == null) {
compareListsForChange((List) changeRecord.getOriginalCollection(), (List) changeRecord.getLatestCollection(), changeRecord, writeQuery.getSession());
}
Iterator<Map.Entry<Object, Set[]>> it = changeRecord.getChangedIndexes().entrySet().iterator();
while (it.hasNext()) {
Map.Entry<Object, Set[]> entry = it.next();
Object value = entry.getKey();
if (getValueConverter() != null) {
value = getValueConverter().convertObjectValueToDataValue(value, writeQuery.getSession());
}
Set[] indexes = entry.getValue();
Set indexesBefore = indexes[0];
Set indexesAfter = indexes[1];
if (indexesAfter == null) {
// All copies of the target object deleted - don't need to verify order field contents.
AbstractRecord deleteRow = writeQuery.getTranslationRow().clone();
// Hey I might actually want to use an inner class here... ok array for now.
Object[] event = new Object[3];
event[0] = Delete;
if (value == null) {
// Bug 306075 - for deleting a null value from a collection
event[1] = getDeleteNullQuery();
} else {
deleteRow.add(getDirectField(), value);
event[1] = getDeleteQuery();
}
event[2] = deleteRow;
writeQuery.getSession().getCommitManager().addDataModificationEvent(this, event);
} else if (indexesAfter.isEmpty()) {
// Some copies of the target objects should be deleted, some left in the db
Iterator<Integer> itBefore = indexesBefore.iterator();
while (itBefore.hasNext()) {
AbstractRecord deleteAtIndexRow = writeQuery.getTranslationRow().clone();
deleteAtIndexRow.add(getDirectField(), value);
deleteAtIndexRow.add(this.listOrderField, itBefore.next());
// Hey I might actually want to use an inner class here... ok array for now.
Object[] event = new Object[3];
event[0] = DeleteAtIndex;
event[1] = deleteAtIndexQuery;
event[2] = deleteAtIndexRow;
writeQuery.getSession().getCommitManager().addDataModificationEvent(this, event);
}
} else {
if (indexesBefore == null || indexesBefore.isEmpty()) {
// insert the object for each index in indexesAfter
Iterator<Integer> itAfter = indexesAfter.iterator();
while (itAfter.hasNext()) {
AbstractRecord insertRow = writeQuery.getTranslationRow().clone();
insertRow.add(getDirectField(), value);
insertRow.add(this.listOrderField, itAfter.next());
// Hey I might actually want to use an inner class here... ok array for now.
Object[] event = new Object[3];
event[0] = Insert;
event[1] = getInsertQuery();
event[2] = insertRow;
writeQuery.getSession().getCommitManager().addDataModificationEvent(this, event);
}
} else {
Iterator<Integer> itBefore = indexesBefore.iterator();
Iterator<Integer> itAfter = indexesAfter.iterator();
while (itBefore.hasNext() || itAfter.hasNext()) {
if (itBefore.hasNext()) {
if (itAfter.hasNext()) {
// update the object changing index from indexBefore to indexAfter
AbstractRecord updateAtIndexRow = writeQuery.getTranslationRow().clone();
updateAtIndexRow.add(getDirectField(), value);
updateAtIndexRow.add(this.listOrderField, itBefore.next());
// Hey I might actually want to use an inner class here... ok array for now.
Object[] event = new Object[4];
event[0] = UpdateAtIndex;
event[1] = updateAtIndexQuery;
event[2] = updateAtIndexRow;
DatabaseRecord modifyRow = new DatabaseRecord(1);
modifyRow.add(this.listOrderField, itAfter.next());
event[3] = modifyRow;
writeQuery.getSession().getCommitManager().addDataModificationEvent(this, event);
} else {
// delete the object at indexBefore
AbstractRecord deleteAtIndexRow = writeQuery.getTranslationRow().clone();
deleteAtIndexRow.add(getDirectField(), value);
deleteAtIndexRow.add(this.listOrderField, itBefore.next());
// Hey I might actually want to use an inner class here... ok array for now.
Object[] event = new Object[3];
event[0] = DeleteAtIndex;
event[1] = deleteAtIndexQuery;
event[2] = deleteAtIndexRow;
writeQuery.getSession().getCommitManager().addDataModificationEvent(this, event);
}
} else {
// itAfter.hasNext() must be true
// insert the object at indexAfter
AbstractRecord insertRow = writeQuery.getTranslationRow().clone();
insertRow.add(getDirectField(), value);
insertRow.add(this.listOrderField, itAfter.next());
// Hey I might actually want to use an inner class here... ok array for now.
Object[] event = new Object[3];
event[0] = Insert;
event[1] = getInsertQuery();
event[2] = insertRow;
writeQuery.getSession().getCommitManager().addDataModificationEvent(this, event);
}
}
}
}
}
}
use of org.eclipse.persistence.indirection.IndirectList in project eclipselink by eclipse-ee4j.
the class DirectCollectionMapping method simpleRemoveFromCollectionChangeRecord.
protected void simpleRemoveFromCollectionChangeRecord(Object objectToRemove, Integer index, boolean isSet, ObjectChangeSet changeSet, AbstractSession session, boolean isChangeApplied) {
DirectCollectionChangeRecord collectionChangeRecord = (DirectCollectionChangeRecord) changeSet.getChangesForAttributeNamed(getAttributeName());
if (collectionChangeRecord == null) {
collectionChangeRecord = new DirectCollectionChangeRecord(changeSet);
collectionChangeRecord.setAttribute(getAttributeName());
collectionChangeRecord.setMapping(this);
changeSet.addChange(collectionChangeRecord);
Object collection = getRealAttributeValueFromObject(changeSet.getUnitOfWorkClone(), session);
if (this.listOrderField != null) {
List originalListCopy = new ArrayList((List) collection);
// index is not null because IndirectList does remove through indexOf.
if (isSet) {
originalListCopy.set(index, objectToRemove);
} else {
originalListCopy.add(index, objectToRemove);
}
collectionChangeRecord.setOriginalCollection(originalListCopy);
collectionChangeRecord.setLatestCollection(collection);
} else {
collectionChangeRecord.storeDatabaseCounts(collection, getContainerPolicy(), session);
collectionChangeRecord.setFirstToRemoveAlreadyOutCollection(isChangeApplied);
if (isSet) {
collectionChangeRecord.setFirstToAddAlreadyInCollection(isChangeApplied);
}
}
}
if (!collectionChangeRecord.isDeferred() && this.listOrderField == null) {
collectionChangeRecord.addRemoveChange(objectToRemove, 1);
}
}
use of org.eclipse.persistence.indirection.IndirectList in project eclipselink by eclipse-ee4j.
the class DirectCollectionMapping method mergeChangesIntoObject.
/**
* INTERNAL:
* Merge changes from the source to the target object.
* Because this is a collection mapping, values are added to or removed from the
* collection based on the changeset
*/
@Override
public void mergeChangesIntoObject(Object target, ChangeRecord changeRecord, Object source, MergeManager mergeManager, AbstractSession targetSession) {
if (this.descriptor.getCachePolicy().isProtectedIsolation() && !this.isCacheable && !targetSession.isProtectedSession()) {
setAttributeValueInObject(target, this.indirectionPolicy.buildIndirectObject(new ValueHolder<>(null)));
return;
}
ContainerPolicy containerPolicy = getContainerPolicy();
Object valueOfTarget = null;
AbstractSession session = mergeManager.getSession();
DirectCollectionChangeRecord directCollectionChangeRecord = (DirectCollectionChangeRecord) changeRecord;
// Check to see if the target has an instantiated collection
if ((isAttributeValueInstantiated(target)) && (!changeRecord.getOwner().isNew())) {
if (isSynchronizeOnMerge) {
valueOfTarget = getRealCollectionAttributeValueFromObject(target, session);
} else {
valueOfTarget = containerPolicy.cloneFor(getRealCollectionAttributeValueFromObject(target, session));
}
} else {
// if not create an instance of the collection
valueOfTarget = containerPolicy.containerInstance(directCollectionChangeRecord.getAddObjectMap().size());
}
if (!isAttributeValueInstantiated(target)) {
if (mergeManager.shouldMergeChangesIntoDistributedCache()) {
return;
}
for (Object iterator = containerPolicy.iteratorFor(getRealCollectionAttributeValueFromObject(source, session)); containerPolicy.hasNext(iterator); ) {
containerPolicy.addInto(containerPolicy.next(iterator, session), valueOfTarget, session);
}
} else {
Object synchronizationTarget = valueOfTarget;
// not the wrapper as the clone synchs on the delegate, see bug#5685287.
if (valueOfTarget instanceof IndirectCollection) {
synchronizationTarget = ((IndirectCollection) valueOfTarget).getDelegateObject();
if (((DirectCollectionChangeRecord) changeRecord).orderHasBeenRepaired() && (valueOfTarget instanceof IndirectList)) {
((IndirectList) valueOfTarget).setIsListOrderBrokenInDb(false);
}
}
if (isSynchronizeOnMerge) {
synchronized (synchronizationTarget) {
mergeAddRemoveChanges(valueOfTarget, synchronizationTarget, directCollectionChangeRecord, mergeManager, session);
}
} else {
mergeAddRemoveChanges(valueOfTarget, synchronizationTarget, directCollectionChangeRecord, mergeManager, session);
}
}
setRealAttributeValueInObject(target, valueOfTarget);
}
Aggregations