Some Eclipse Foundation services are deprecated, or will be soon. Please ensure you've read this important communication.
Bug 338752 - IndexOutOfBoundsException from Copier.copyReferences(...)
Summary: IndexOutOfBoundsException from Copier.copyReferences(...)
Status: RESOLVED INVALID
Alias: None
Product: EMF
Classification: Modeling
Component: Core (show other bugs)
Version: unspecified   Edit
Hardware: PC Windows XP
: P3 major (vote)
Target Milestone: ---   Edit
Assignee: Ed Merks CLA
QA Contact:
URL:
Whiteboard:
Keywords:
Depends on:
Blocks:
 
Reported: 2011-03-02 17:11 EST by Jared CLA
Modified: 2011-03-10 13:31 EST (History)
0 users

See Also:


Attachments
This is the program that reproduces the issue. (1.27 KB, application/octet-stream)
2011-03-02 17:16 EST, Jared CLA
no flags Details
This is the serialized object that gets copied to reproduce the issue. it is input for the java program provided. (1.72 MB, application/octet-stream)
2011-03-02 17:16 EST, Jared CLA
no flags Details

Note You need to log in before you can comment on or make changes to this bug.
Description Jared CLA 2011-03-02 17:11:50 EST
Build Identifier: EMF Version 2.2.2

The issue is consistently repeatable. Here's the stack trace:


java.lang.IndexOutOfBoundsException: targetIndex=1, size=1
        at org.eclipse.emf.common.util.BasicEList.move(BasicEList.java:997)
        at org.eclipse.emf.common.notify.impl.NotifyingListImpl.doMove(NotifyingListImpl.java:1308)
        at org.eclipse.emf.common.notify.impl.NotifyingListImpl.move(NotifyingListImpl.java:1293)
        at org.eclipse.emf.common.util.BasicEList.move(BasicEList.java:981)
        at org.eclipse.emf.ecore.util.EcoreUtil$Copier.copyReference(EcoreUtil.java:710)
        at org.eclipse.emf.ecore.util.EcoreUtil$Copier.copyReferences(EcoreUtil.java:613)
        at com.ibm.connectors.migration.Job.cloneObjects(Job.java:1560)
        at com.ibm.connectors.migration.Job.migrate(Job.java:701)
        at com.ibm.connectors.migration.CCJobMigrator.migrateJobs(CCJobMigrator.java:1922)
        at com.ibm.connectors.migration.CCJobMigrator.processGUIMode(CCJobMigrator.java:1388)
        at com.ibm.connectors.migration.CCJobMigrator.process(CCJobMigrator.java:1355)
        at com.ibm.connectors.migration.CCJobMigrator.main(CCJobMigrator.java:2443)



Reproducible: Always

Steps to Reproduce:
I will send you the java program that consistently reproduces the issue.
Comment 1 Jared CLA 2011-03-02 17:16:08 EST
Created attachment 190211 [details]
This is the program that reproduces the issue.

This java program requires several libraries that I will attempt to send. I am not yet sure if IBM company policy allows me to send them...
Comment 2 Jared CLA 2011-03-02 17:16:58 EST
Created attachment 190213 [details]
This is the serialized object that gets copied to reproduce the issue. it is input for the java program provided.
Comment 3 Jared CLA 2011-03-02 17:19:28 EST
I need to confirm that I am allowed to send private jar files for the resolution of this issue.
Comment 4 Jared CLA 2011-03-02 17:31:28 EST
I have sent all files required to internal IBM EMF team.
Comment 5 Ed Merks CLA 2011-03-02 18:07:57 EST
We don't fix bugs in streams older than the most recent release, i.e, older than 2.6.   It's highly likely this bug was fixed years ago, but I can't confirm that when your test case has dependencies that I can't resolve:

import DataStageX.DataStageXPackage;

import com.ascential.xmeta.emf.util.EObjectMemento;
import com.ascential.xmeta.emf.util.EObjectMementoConverter;

If you want me to look at a problem, please provide a self contained test case that I can run locally with no additional dependencies.
Comment 6 Jared CLA 2011-03-03 18:12:32 EST
Thank you Ed. I will attempt to reproduce with a later version...
Comment 7 Jared CLA 2011-03-09 16:08:49 EST
I have reproduced with the latest version of EMF (version 2.7)

This is the stack trace using EMF 2.7
Error: targetIndex=1, size=1
java.lang.IndexOutOfBoundsException: targetIndex=1, size=1
          at org.eclipse.emf.common.util.BasicEList.move(BasicEList.java:664)
          at org.eclipse.emf.common.notify.impl.NotifyingListImpl.doMove(NotifyingListImpl.java:1335)
          at org.eclipse.emf.common.notify.impl.NotifyingListImpl.move(NotifyingListImpl.java:1320)
          at org.eclipse.emf.common.util.AbstractEList.move(AbstractEList.java:539)
          at org.eclipse.emf.ecore.util.EcoreUtil$Copier.copyReference(EcoreUtil.java:743)
          at org.eclipse.emf.ecore.util.EcoreUtil$Copier.copyReferences(EcoreUtil.java:646)
          at EmfBug_OutOfBounds.main(EmfBug_OutOfBounds.java:33)

In EMF EcoreUtil.java we have this code:
for (Iterator<EObject> k = resolveProxies ? source.iterator() : source.basicIterator(); k.hasNext();)
{
...
   if (isBidirectional)
   {
      int position = target.indexOf(copyReferencedEObject);
      if (position == -1)
      {
         target.addUnique(index, copyReferencedEObject);
      }
      else if (index != position)
      {
         target.move(index, copyReferencedEObject);
      }
   }
...

The exception is coming from target.move(…) above because the index value is equal to the size of the target List. So EMF is trying to move an element from index 0, to index 1, but there is only one element in the target. So this move operation does not make sense. This EMF issue is revealed because the source List contains two identical elements.

The EMF Copier code does not account for two identical elements in a List.

If the reference is “bidirectional” (not sure what that means) then the element will not be duplicated in the List, else it’s added without checking if it already exists. For this case, we are dealing with identical bidirectional references, so they will not get duplicated in the source.

The for loop will hit the first element in the list, add it, move to the second element, find it already exists in the target, and then attempt to move the existing element from position 0 to position 1. This is bad because the List only contains 1 element and throws an exception because the target position can only be 0, since the target List only contains one element so far.

I modified the EMF code to account for the possibility of two identical objects in a List without duplicating…
EcoreUtil.java (line 741)
else if (index != position)
{
   // If target has only one element, then we don't need to move it.
   if (target.size() > 1) {
                     
      // if target position (index) is a valid destination, then move it.
      if (index < target.size()) {
         target.move(index, copyReferencedEObject);
      }
                 
      // If the target position (index) is too large for the List
      // move it to the end.
      else {
         target.move(target.size() -1, copyReferencedEObject);
      }
   }
}

Tested the fix and it works.
I am having a hard time reproducing in a stand-alone application because the object that is being copied has many references and dependencies on internal libraries. I hope my above analysis is obvious enough to get a fix for this in the code.
Comment 8 Ed Merks CLA 2011-03-09 17:11:16 EST
There are not allowed to be two identical elements in a bidirectional list of EObjects.  The implementation classes for this type of list all have implemented checks to prevent duplicates.  It's possible to bypass those checks (via casting to InternalEList) to add more efficiently (addUnique) when you can ensure otherwise it's not a duplicate.  If there are duplicates, someone has corrupted the integrity of the list, and the rest of the framework, which assumes there are no duplicates, will behave poorly but will not changed to accommodate such violation.  After all, to produce an actual copy, we'd need to create something with duplicates, which isn't allowed, so copying should fail.
Comment 9 Jared CLA 2011-03-10 13:31:49 EST
Thank you very much Ed! I will pass your excellent answer on to my team. 

Appreciate your time.
Jared.