/**
   * Change a single reference and check the generated operation.
   *
   * @throws UnsupportedOperationException on test fail
   * @throws UnsupportedNotificationException on test fail
   */
  @Test
  public void changeSingleReference()
      throws UnsupportedOperationException, UnsupportedNotificationException {
    final UseCase useCase = RequirementFactory.eINSTANCE.createUseCase();
    final Actor actor = RequirementFactory.eINSTANCE.createActor();

    new EMFStoreCommand() {

      @Override
      protected void doRun() {
        getProject().addModelElement(useCase);
        getProject().addModelElement(actor);

        clearOperations();

        useCase.setInitiatingActor(actor);

        assertEquals(actor, useCase.getInitiatingActor());
        EList<UseCase> initiatedUseCases = actor.getInitiatedUseCases();
        assertEquals(1, initiatedUseCases.size());
        assertEquals(useCase, initiatedUseCases.get(0));
      }
    }.run(false);

    List<AbstractOperation> operations = getProjectSpace().getOperations();

    assertEquals(1, operations.size());
    AbstractOperation operation = operations.get(0);
    assertEquals(true, operation instanceof CompositeOperation);

    EList<AbstractOperation> subOperations = ((CompositeOperation) operation).getSubOperations();

    assertEquals(2, subOperations.size());

    operation = subOperations.get(0);
    assertEquals(true, operation instanceof MultiReferenceOperationImpl);
    MultiReferenceOperation multiReferenceOperation = (MultiReferenceOperation) operation;
    assertEquals("initiatedUseCases", multiReferenceOperation.getFeatureName());
    assertEquals("initiatingActor", multiReferenceOperation.getOppositeFeatureName());

    ModelElementId useCaseId = ModelUtil.getProject(useCase).getModelElementId(useCase);
    ModelElementId actorId = ModelUtil.getProject(actor).getModelElementId(actor);

    assertEquals(useCaseId, multiReferenceOperation.getReferencedModelElements().get(0));
    assertEquals(actorId, multiReferenceOperation.getModelElementId());
    assertTrue(multiReferenceOperation.isBidirectional());
    assertTrue(multiReferenceOperation.isAdd());
    assertEquals(1, multiReferenceOperation.getOtherInvolvedModelElements().size());

    operation = subOperations.get(1);
    assertEquals(true, operation instanceof SingleReferenceOperation);
    SingleReferenceOperation singleReferenceOperation = (SingleReferenceOperation) operation;

    assertEquals(null, singleReferenceOperation.getOldValue());
    assertEquals(actorId, singleReferenceOperation.getNewValue());
    assertEquals("initiatingActor", singleReferenceOperation.getFeatureName());
    assertEquals(useCaseId, singleReferenceOperation.getModelElementId());
    assertEquals("initiatedUseCases", singleReferenceOperation.getOppositeFeatureName());
    assertEquals(true, singleReferenceOperation.isBidirectional());
    Set<ModelElementId> otherInvolvedModelElements =
        singleReferenceOperation.getOtherInvolvedModelElements();
    assertEquals(1, otherInvolvedModelElements.size());
    assertEquals(actorId, otherInvolvedModelElements.iterator().next());
  }
  /**
   * Move a containee to another container.
   *
   * @throws UnsupportedOperationException on test fail
   * @throws UnsupportedNotificationException on test fail
   */
  @Test
  public void moveContainmentReference()
      throws UnsupportedOperationException, UnsupportedNotificationException {
    final Issue oldIssue = RationaleFactory.eINSTANCE.createIssue();
    final Issue newIssue = RationaleFactory.eINSTANCE.createIssue();
    final Proposal proposal = RationaleFactory.eINSTANCE.createProposal();

    new EMFStoreCommand() {

      @Override
      protected void doRun() {
        getProject().addModelElement(oldIssue);
        getProject().addModelElement(newIssue);
        getProject().addModelElement(proposal);
        proposal.setIssue(oldIssue);
        clearOperations();

        assertEquals(0, newIssue.getProposals().size());
        assertEquals(1, oldIssue.getProposals().size());
        assertEquals(proposal, oldIssue.getProposals().get(0));
        assertEquals(oldIssue, proposal.getIssue());

        proposal.setIssue(newIssue);

        assertEquals(0, oldIssue.getProposals().size());
        assertEquals(1, newIssue.getProposals().size());
        assertEquals(proposal, newIssue.getProposals().get(0));
        assertEquals(newIssue, proposal.getIssue());
      }
    }.run(false);

    List<AbstractOperation> operations = getProjectSpace().getOperations();

    assertEquals(1, operations.size());

    if (operations.get(0) instanceof CompositeOperation) {
      operations = ((CompositeOperation) operations.get(0)).getSubOperations();
    } else {
      fail("composite operation expected");
    }

    assertEquals(3, operations.size());

    AbstractOperation op0 = operations.get(0);
    assertTrue(op0 instanceof MultiReferenceOperation);
    MultiReferenceOperation multiReferenceOperation = (MultiReferenceOperation) op0;

    ModelElementId oldIssueId = ModelUtil.getProject(oldIssue).getModelElementId(oldIssue);
    ModelElementId proposalId = ModelUtil.getProject(proposal).getModelElementId(proposal);
    ModelElementId newIssueId = ModelUtil.getProject(newIssue).getModelElementId(newIssue);

    assertEquals(multiReferenceOperation.getModelElementId(), oldIssueId);
    assertFalse(multiReferenceOperation.isAdd());
    assertEquals(multiReferenceOperation.getReferencedModelElements().get(0), proposalId);
    assertEquals(multiReferenceOperation.getReferencedModelElements().size(), 1);
    assertEquals(multiReferenceOperation.getIndex(), 0);

    AbstractOperation op1 = operations.get(1);
    assertTrue(op1 instanceof MultiReferenceOperation);
    multiReferenceOperation = (MultiReferenceOperation) op1;
    assertEquals(multiReferenceOperation.getModelElementId(), newIssueId);
    assertTrue(multiReferenceOperation.isAdd());
    assertEquals(multiReferenceOperation.getReferencedModelElements().get(0), proposalId);
    assertEquals(multiReferenceOperation.getReferencedModelElements().size(), 1);
    assertEquals(multiReferenceOperation.getIndex(), 0);

    AbstractOperation operation = operations.get(2);
    assertEquals(true, operation instanceof SingleReferenceOperation);
    SingleReferenceOperation singleReferenceOperation = (SingleReferenceOperation) operation;

    assertEquals(oldIssueId, singleReferenceOperation.getOldValue());
    assertEquals(newIssueId, singleReferenceOperation.getNewValue());
    assertEquals(
        RationalePackage.eINSTANCE.getProposal_Issue().getName(),
        singleReferenceOperation.getFeatureName());
    assertEquals(proposalId, singleReferenceOperation.getModelElementId());
    assertEquals(
        RationalePackage.eINSTANCE.getIssue_Proposals().getName(),
        singleReferenceOperation.getOppositeFeatureName());
    assertEquals(true, singleReferenceOperation.isBidirectional());
    Set<ModelElementId> otherInvolvedModelElements =
        singleReferenceOperation.getOtherInvolvedModelElements();
    assertEquals(2, otherInvolvedModelElements.size());
    assertEquals(true, otherInvolvedModelElements.contains(newIssueId));
    assertEquals(true, otherInvolvedModelElements.contains(oldIssueId));
  }
  /**
   * Test containment removing.
   *
   * @throws UnsupportedOperationException on test fail
   */
  @Test
  public void removeContainment() throws UnsupportedOperationException {
    final Issue issue = RationaleFactory.eINSTANCE.createIssue();
    final Proposal proposal = RationaleFactory.eINSTANCE.createProposal();

    new EMFStoreCommand() {

      @Override
      protected void doRun() {
        proposal.setName("proposal");
        getProject().addModelElement(issue);
        getProject().addModelElement(proposal);
        proposal.setIssue(issue);

        clearOperations();

        assertEquals(1, issue.getProposals().size());
        assertEquals(proposal, issue.getProposals().get(0));
        assertEquals(issue, proposal.getIssue());
        assertEquals(true, getProject().containsInstance(issue));
        assertEquals(true, getProject().containsInstance(proposal));
        assertEquals(getProject(), ModelUtil.getProject(issue));
        assertEquals(getProject(), ModelUtil.getProject(proposal));
        assertEquals(issue, proposal.eContainer());
      }
    }.run(false);

    ModelElementId proposalId = ModelUtil.getProject(proposal).getModelElementId(proposal);

    new EMFStoreCommand() {

      @Override
      protected void doRun() {
        proposal.setIssue(null);
      }
    }.run(false);

    assertEquals(true, getProject().containsInstance(issue));
    assertEquals(false, getProject().containsInstance(proposal));
    assertEquals(getProject(), ModelUtil.getProject(issue));
    // assertEquals(null, ModelUtil.getProject(proposal));
    assertEquals(0, issue.getProposals().size());
    assertEquals(null, proposal.getIssue());
    // assertEquals(null, proposal.eContainer());

    List<AbstractOperation> operations = getProjectSpace().getOperations();

    assertEquals(1, operations.size());

    ModelElementId issueId = ModelUtil.getProject(issue).getModelElementId(issue);

    assertEquals(true, operations.get(0) instanceof CreateDeleteOperation);
    CreateDeleteOperation deleteOperation = (CreateDeleteOperation) operations.get(0);
    assertEquals(true, deleteOperation.isDelete());
    assertEquals(true, deleteOperation.getModelElement() instanceof Proposal);
    assertEquals("proposal", ((Proposal) deleteOperation.getModelElement()).getName());
    assertEquals(proposalId, deleteOperation.getModelElementId());

    List<ReferenceOperation> subOperations =
        ((CreateDeleteOperation) operations.get(0)).getSubOperations();
    assertEquals(2, subOperations.size());

    AbstractOperation op0 = subOperations.get(0);
    assertTrue(op0 instanceof MultiReferenceOperation);
    MultiReferenceOperation multiReferenceOperation = (MultiReferenceOperation) op0;
    assertEquals(multiReferenceOperation.getModelElementId(), issueId);
    assertFalse(multiReferenceOperation.isAdd());
    assertEquals(multiReferenceOperation.getReferencedModelElements().get(0), proposalId);
    assertEquals(multiReferenceOperation.getReferencedModelElements().size(), 1);
    assertEquals(multiReferenceOperation.getIndex(), 0);

    AbstractOperation operation = subOperations.get(1);
    assertEquals(true, operation instanceof SingleReferenceOperation);
    SingleReferenceOperation singleReferenceOperation = (SingleReferenceOperation) operation;

    assertEquals(issueId, singleReferenceOperation.getOldValue());
    assertEquals(null, singleReferenceOperation.getNewValue());
    assertEquals(
        RationalePackage.eINSTANCE.getProposal_Issue().getName(),
        singleReferenceOperation.getFeatureName());
    assertEquals(proposalId, singleReferenceOperation.getModelElementId());
    assertEquals(
        RationalePackage.eINSTANCE.getIssue_Proposals().getName(),
        singleReferenceOperation.getOppositeFeatureName());
    assertEquals(true, singleReferenceOperation.isBidirectional());
    Set<ModelElementId> otherInvolvedModelElements =
        singleReferenceOperation.getOtherInvolvedModelElements();
    assertEquals(1, otherInvolvedModelElements.size());
    assertEquals(true, otherInvolvedModelElements.contains(issueId));
  }
  /**
   * Change an single reference and reverse it.
   *
   * @throws UnsupportedOperationException on test fail
   * @throws UnsupportedNotificationException on test fail
   */
  @Test
  public void reverseSingleReference()
      throws UnsupportedOperationException, UnsupportedNotificationException {

    final UseCase useCase = RequirementFactory.eINSTANCE.createUseCase();
    final Actor oldActor = RequirementFactory.eINSTANCE.createActor();
    final Actor newActor = RequirementFactory.eINSTANCE.createActor();

    new EMFStoreCommand() {

      @Override
      protected void doRun() {
        getProject().addModelElement(useCase);
        getProject().addModelElement(oldActor);
        getProject().addModelElement(newActor);
        useCase.setInitiatingActor(oldActor);
        assertEquals(oldActor, useCase.getInitiatingActor());
        EList<UseCase> initiatedUseCases = oldActor.getInitiatedUseCases();
        assertEquals(1, initiatedUseCases.size());
        assertEquals(useCase, initiatedUseCases.get(0));

        clearOperations();

        useCase.setInitiatingActor(newActor);
        assertEquals(newActor, useCase.getInitiatingActor());
        initiatedUseCases = newActor.getInitiatedUseCases();
        assertEquals(1, initiatedUseCases.size());
        assertEquals(useCase, initiatedUseCases.get(0));
      }
    }.run(false);

    List<AbstractOperation> operations = getProjectSpace().getOperations();

    assertEquals(1, operations.size());

    assertEquals(1, operations.size());
    if (operations.get(0) instanceof CompositeOperation) {
      operations = ((CompositeOperation) operations.get(0)).getSubOperations();
    } else {
      fail("composite operation expected");
    }

    assertEquals(3, operations.size());

    // note: skipping multireferenceop at index 0 in test, as it is not interesting in this context
    AbstractOperation operation = operations.get(2);
    assertEquals(true, operation instanceof SingleReferenceOperation);
    SingleReferenceOperation singleReferenceOperation = (SingleReferenceOperation) operation;

    ModelElementId useCaseId = ModelUtil.getProject(useCase).getModelElementId(useCase);
    ModelElementId oldActorId = ModelUtil.getProject(oldActor).getModelElementId(oldActor);
    ModelElementId newActorId = ModelUtil.getProject(newActor).getModelElementId(newActor);

    assertEquals(oldActorId, singleReferenceOperation.getOldValue());
    assertEquals(newActorId, singleReferenceOperation.getNewValue());
    assertEquals("initiatingActor", singleReferenceOperation.getFeatureName());
    assertEquals(useCaseId, singleReferenceOperation.getModelElementId());
    assertEquals("initiatedUseCases", singleReferenceOperation.getOppositeFeatureName());
    assertEquals(true, singleReferenceOperation.isBidirectional());
    Set<ModelElementId> otherInvolvedModelElements =
        singleReferenceOperation.getOtherInvolvedModelElements();
    assertEquals(2, otherInvolvedModelElements.size());
    assertEquals(true, otherInvolvedModelElements.contains(oldActorId));
    assertEquals(true, otherInvolvedModelElements.contains(newActorId));

    AbstractOperation reverse = singleReferenceOperation.reverse();
    assertEquals(true, reverse instanceof SingleReferenceOperation);
    final SingleReferenceOperation reversedSingleReferenceOperation =
        (SingleReferenceOperation) reverse;

    new EMFStoreCommand() {

      @Override
      protected void doRun() {
        reversedSingleReferenceOperation.apply(getProject());
      }
    }.run(false);

    assertEquals(oldActor, useCase.getInitiatingActor());

    assertEquals(newActorId, reversedSingleReferenceOperation.getOldValue());
    assertEquals(oldActorId, reversedSingleReferenceOperation.getNewValue());
    assertEquals("initiatingActor", reversedSingleReferenceOperation.getFeatureName());
    assertEquals(useCaseId, reversedSingleReferenceOperation.getModelElementId());
    assertEquals("initiatedUseCases", reversedSingleReferenceOperation.getOppositeFeatureName());
    assertEquals(true, reversedSingleReferenceOperation.isBidirectional());
    otherInvolvedModelElements = reversedSingleReferenceOperation.getOtherInvolvedModelElements();
    assertEquals(2, otherInvolvedModelElements.size());
    assertEquals(true, otherInvolvedModelElements.contains(oldActorId));
    assertEquals(true, otherInvolvedModelElements.contains(newActorId));
  }