/**
   * Moves the operators from this process to another process, keeping all connections intact. TODO:
   * Test more rigorously. Do we register/unregister everything correctly?
   *
   * @return the number of ports the connections of which could not be restored
   */
  public int stealOperatorsFrom(ExecutionUnit otherUnit) {
    int failedReconnects = 0;

    // remember source and sink connections so we can reconnect them later.
    Map<String, InputPort> sourceMap = new HashMap<String, InputPort>();
    Map<String, OutputPort> sinkMap = new HashMap<String, OutputPort>();
    for (OutputPort source : otherUnit.getInnerSources().getAllPorts()) {
      if (source.isConnected()) {
        sourceMap.put(source.getName(), source.getDestination());
      }
    }
    otherUnit.getInnerSources().disconnectAll();
    for (InputPort sink : otherUnit.getInnerSinks().getAllPorts()) {
      if (sink.isConnected()) {
        sinkMap.put(sink.getName(), sink.getSource());
      }
    }
    otherUnit.getInnerSinks().disconnectAll();

    // Move operators
    Iterator<Operator> i = otherUnit.operators.iterator();
    while (i.hasNext()) {
      Operator operator = i.next();
      i.remove();
      otherUnit.unregister(operator);
      Process otherProcess = operator.getProcess();
      if (otherProcess != null) {
        operator.unregisterOperator(otherProcess);
      }
      this.operators.add(operator);
      operator.setEnclosingProcess(null);
      // operator.unregisterOperator(operator.getProcess());
      registerOperator(operator, true);
      // operator.registerOperator(this.getEnclosingOperator().getProcess());
    }

    // Rewire sources and sinks
    for (Map.Entry<String, InputPort> entry : sourceMap.entrySet()) {
      OutputPort mySource = getInnerSources().getPortByName(entry.getKey());
      if (mySource != null) {
        mySource.connectTo(entry.getValue());
      } else {
        failedReconnects++;
      }
    }
    getInnerSources().unlockPortExtenders();

    for (Map.Entry<String, OutputPort> entry : sinkMap.entrySet()) {
      InputPort mySink = getInnerSinks().getPortByName(entry.getKey());
      if (mySink != null) {
        entry.getValue().connectTo(mySink);
      } else {
        failedReconnects++;
      }
    }
    getInnerSinks().unlockPortExtenders();

    fireUpdate(this);
    return failedReconnects;
  }
  private void cloneConnections(
      OutputPorts originalPorts,
      ExecutionUnit originalExecutionUnit,
      Map<String, Operator> clonedOperatorsByName) {
    for (OutputPort originalSource : originalPorts.getAllPorts()) {
      if (originalSource.isConnected()) {

        OutputPort mySource;
        if (originalPorts.getOwner().getOperator()
            == originalExecutionUnit.getEnclosingOperator()) {
          // this is an inner source
          mySource = getInnerSources().getPortByName(originalSource.getName());
          if (mySource == null) {
            throw new RuntimeException(
                "Error during clone: Corresponding source for "
                    + originalSource
                    + " not found (no such inner source).");
          }
        } else {
          // this is an output port
          Operator myOperator =
              clonedOperatorsByName.get(
                  originalSource.getPorts().getOwner().getOperator().getName());
          if (myOperator == null) {
            throw new RuntimeException(
                "Error during clone: Corresponding source for "
                    + originalSource
                    + " not found (no such operator).");
          }
          mySource = myOperator.getOutputPorts().getPortByName(originalSource.getName());
          if (mySource == null) {
            throw new RuntimeException(
                "Error during clone: Corresponding source for "
                    + originalSource
                    + " not found (no such output port).");
          }
        }

        InputPort originalDestination = originalSource.getDestination();
        InputPort myDestination;
        if (originalDestination.getPorts().getOwner().getOperator()
            == originalExecutionUnit.getEnclosingOperator()) {
          // this is an inner sink
          myDestination = getInnerSinks().getPortByName(originalDestination.getName());
          if (myDestination == null) {
            throw new RuntimeException(
                "Error during clone: Corresponding destination for "
                    + originalDestination
                    + " not found (no such inner sink).");
          }
        } else {
          // this is an input port
          Operator myOperator =
              clonedOperatorsByName.get(
                  originalDestination.getPorts().getOwner().getOperator().getName());
          if (myOperator == null) {
            throw new RuntimeException(
                "Error during clone: Corresponding destination for "
                    + originalDestination
                    + " not found (no such operator).");
          }
          myDestination = myOperator.getInputPorts().getPortByName(originalDestination.getName());
          if (myDestination == null) {
            throw new RuntimeException(
                "Error during clone: Corresponding destination for "
                    + originalDestination
                    + " not found (no such input port).");
          }
        }
        mySource.connectTo(myDestination);
      }
    }
  }
  @Override
  public ExampleSet apply(ExampleSet exampleSet) throws OperatorException {

    if (exampleSet == null) {
      throw new UserError(this, 149, getInputPorts().getPortByIndex(0).getName());
    }

    // get unmachted attributes
    Attributes attributes = exampleSet.getAttributes();
    List<Attribute> unmachtedAttributes = getAttributeList(attributes);

    if (getParameterAsString(PARAMETER_ORDER_MODE).equals(ALPHABETICALLY_MODE)) {

      if (getParameterAsString(PARAMETER_SORT_DIRECTION).equals(DIRECTION_NONE)) {
        return exampleSet;
      }

      // sort attributes
      sortAttributeListAlphabetically(unmachtedAttributes);

      // apply sorted attributes
      applySortedAttributes(unmachtedAttributes, null, attributes);

    } else if (getParameterAsString(PARAMETER_ORDER_MODE).equals(USER_SPECIFIED_RULES_MODE)
        || getParameterAsString(PARAMETER_ORDER_MODE).equals(REFERENCE_DATA)) {
      List<Attribute> sortedAttributes = new LinkedList<>();

      if (getParameterAsString(PARAMETER_ORDER_MODE).equals(REFERENCE_DATA)) {
        InputPort referencePort = getInputPorts().getPortByName(REFERENCE_DATA_PORT_NAME);
        ExampleSet referenceSet = referencePort.getData(ExampleSet.class);

        if (referenceSet == null) {
          throw new UserError(this, 149, referencePort.getName());
        }

        // iterate over reference attributes and order unmachted attributes accordingly
        for (Attribute refAttr : referenceSet.getAttributes()) {
          // System.out.println("Check attribute " + refAttr.getName());
          Iterator<Attribute> iterator = unmachtedAttributes.iterator();
          while (iterator.hasNext()) {
            Attribute unmachtedAttr = iterator.next();
            if (refAttr.getName().equals(unmachtedAttr.getName())) {

              // only pairwise matching is possible -> directly add attribute to
              // sorted list
              sortedAttributes.add(unmachtedAttr);
              // System.out.println("Added unmachted attribute to list: " +
              // unmachtedAttr.getName());

              // remove attribute from unmachted attributes
              iterator.remove();
            }
          }
        }
      } else {
        String combinedMaskedRules = getParameterAsString(PARAMETER_ORDER_RULES);
        if (combinedMaskedRules == null || combinedMaskedRules.length() == 0) {
          throw new UndefinedParameterError(PARAMETER_ORDER_RULES, this);
        }

        // iterate over all rules
        for (String maskedRule : combinedMaskedRules.split("\\|")) {
          String rule = Tools.unmask('|', maskedRule); // unmask them to allow regexp
          List<Attribute> matchedAttributes = new LinkedList<>();

          // iterate over all attributes and check if rules apply
          Iterator<Attribute> iterator = unmachtedAttributes.iterator();
          while (iterator.hasNext()) {
            Attribute attr = iterator.next();
            boolean match = false;
            if (getParameterAsBoolean(PARAMETER_USE_REGEXP)) {
              try {
                if (attr.getName().matches(rule)) {
                  match = true;
                }
              } catch (PatternSyntaxException e) {
                throw new UserError(this, 206, rule, e.getMessage());
              }
            } else {
              if (attr.getName().equals(rule)) {
                match = true;
              }
            }

            // if rule applies remove attribute from unmachted list and add it to rules
            // matched list
            if (match) {
              iterator.remove();
              matchedAttributes.add(attr);
            }
          }

          // sort matched attributes according to sort direction if more then one match
          // has been found
          if (matchedAttributes.size() > 1) {
            sortAttributeListAlphabetically(matchedAttributes);
          }

          // add matched attributes to sorted attribute list
          sortedAttributes.addAll(matchedAttributes);
        }
      }

      /*
       * UNMACHTED Handling
       */

      if (!getParameterAsString(PARAMETER_HANDLE_UNMATCHED_ATTRIBUTES)
          .equals(REMOVE_UNMATCHED_MODE)) {
        // sort unmachted attributes according to sort direction
        sortAttributeListAlphabetically(unmachtedAttributes);

        if (getParameterAsString(PARAMETER_HANDLE_UNMATCHED_ATTRIBUTES)
            .equals(PREPEND_UNMATCHED_MODE)) {
          // prepend attributes to ordered attributes list
          sortedAttributes.addAll(0, unmachtedAttributes);
        } else {
          // append attributes to ordered attributes list
          sortedAttributes.addAll(unmachtedAttributes);
        }

        applySortedAttributes(sortedAttributes, null, attributes);

      } else {
        applySortedAttributes(sortedAttributes, unmachtedAttributes, attributes);
      }

    } else {
      throw new IllegalArgumentException(
          "Order mode " + getParameterAsString(PARAMETER_ORDER_MODE) + " is not implemented!");
    }
    return exampleSet;
  }