public DataTransferSettings(
      @Nullable IDataTransferProducer[] producers, @Nullable IDataTransferConsumer[] consumers) {
    dataPipes = new ArrayList<>();
    if (!ArrayUtils.isEmpty(producers) && !ArrayUtils.isEmpty(consumers)) {
      if (producers.length != consumers.length) {
        throw new IllegalArgumentException("Producers number must match consumers number");
      }
      // Make pipes
      for (int i = 0; i < producers.length; i++) {
        dataPipes.add(new DataTransferPipe(producers[i], consumers[i]));
      }
      consumerOptional = false;
    } else if (!ArrayUtils.isEmpty(producers)) {
      // Make pipes
      for (IDataTransferProducer source : producers) {
        dataPipes.add(new DataTransferPipe(source, null));
      }
      // Set default producer
      Class<? extends IDataTransferProducer> producerType =
          dataPipes.get(0).getProducer().getClass();
      DataTransferNodeDescriptor producerDesc =
          DataTransferRegistry.getInstance().getNodeByType(producerType);
      if (producerDesc != null) {
        selectProducer(producerDesc);
        consumerOptional = true;
      } else {
        UIUtils.showErrorDialog(
            null, "Can't find producer", "Can't find data propducer descriptor in registry");
      }
    } else if (!ArrayUtils.isEmpty(consumers)) {
      // Make pipes
      for (IDataTransferConsumer target : consumers) {
        dataPipes.add(new DataTransferPipe(null, target));
      }
      // Set default consumer
      Class<? extends IDataTransferConsumer> consumerType =
          dataPipes.get(0).getConsumer().getClass();
      DataTransferNodeDescriptor consumerDesc =
          DataTransferRegistry.getInstance().getNodeByType(consumerType);
      if (consumerDesc != null) {
        selectConsumer(consumerDesc, null);
        consumerOptional = false;
      } else {
        UIUtils.showErrorDialog(
            null, "Can't find producer", "Can't find data propducer descriptor in registry");
      }
    } else {
      throw new IllegalArgumentException("Producers or consumers must be specified");
    }

    Collection<Class<?>> objectTypes = getObjectTypes();
    List<DataTransferNodeDescriptor> nodes = new ArrayList<>();
    DataTransferRegistry registry = DataTransferRegistry.getInstance();
    if (ArrayUtils.isEmpty(producers)) {
      nodes.addAll(registry.getAvailableProducers(objectTypes));
    } else {
      for (IDataTransferProducer source : producers) {
        DataTransferNodeDescriptor node = registry.getNodeByType(source.getClass());
        if (node != null && !nodes.contains(node)) {
          nodes.add(node);
        }
      }
    }
    if (ArrayUtils.isEmpty(consumers)) {
      nodes.addAll(registry.getAvailableConsumers(objectTypes));
    } else {
      for (IDataTransferConsumer target : consumers) {
        DataTransferNodeDescriptor node = registry.getNodeByType(target.getClass());
        if (node != null && !nodes.contains(node)) {
          nodes.add(node);
          this.consumer = node;
        }
      }
    }
    for (DataTransferNodeDescriptor node : nodes) {
      addNodeSettings(node);
    }
  }
  void loadFrom(IRunnableContext runnableContext, IDialogSettings dialogSettings) {
    try {
      maxJobCount = dialogSettings.getInt("maxJobCount");
    } catch (NumberFormatException e) {
      maxJobCount = DEFAULT_THREADS_NUM;
    }
    String producerId = dialogSettings.get("producer");
    if (!CommonUtils.isEmpty(producerId)) {
      DataTransferNodeDescriptor producerNode =
          DataTransferRegistry.getInstance().getNodeById(producerId);
      if (producerNode != null) {
        this.producer = producerNode;
      }
    }

    if (consumerOptional) {
      DataTransferNodeDescriptor savedConsumer = null;
      String consumerId = dialogSettings.get("consumer");
      if (!CommonUtils.isEmpty(consumerId)) {
        DataTransferNodeDescriptor consumerNode =
            DataTransferRegistry.getInstance().getNodeById(consumerId);
        if (consumerNode != null) {
          savedConsumer = consumerNode;
        }
      }

      DataTransferProcessorDescriptor savedProcessor = null;
      if (savedConsumer != null) {
        String processorId = dialogSettings.get("processor");
        if (!CommonUtils.isEmpty(processorId)) {
          savedProcessor = savedConsumer.getProcessor(processorId);
        }
      }
      if (savedConsumer != null) {
        selectConsumer(savedConsumer, savedProcessor);
      }
    }

    // Load nodes' settings
    for (Map.Entry<Class, NodeSettings> entry : nodeSettings.entrySet()) {
      IDialogSettings nodeSection =
          DialogSettings.getOrCreateSection(dialogSettings, entry.getKey().getSimpleName());
      entry.getValue().settings.loadSettings(runnableContext, this, nodeSection);
    }
    IDialogSettings processorsSection = dialogSettings.getSection("processors");
    if (processorsSection != null) {
      for (IDialogSettings procSection : ArrayUtils.safeArray(processorsSection.getSections())) {
        String processorId = procSection.getName();
        String nodeId = procSection.get("@node");
        String propNamesId = procSection.get("@propNames");
        DataTransferNodeDescriptor node = DataTransferRegistry.getInstance().getNodeById(nodeId);
        if (node != null) {
          Map<Object, Object> props = new HashMap<>();
          DataTransferProcessorDescriptor nodeProcessor = node.getProcessor(processorId);
          if (nodeProcessor != null) {
            for (String prop : CommonUtils.splitString(propNamesId, ',')) {
              props.put(prop, procSection.get(prop));
            }
            processorPropsHistory.put(nodeProcessor, props);
            NodeSettings nodeSettings = this.nodeSettings.get(node.getNodeClass());
            if (nodeSettings != null) {}
          }
        }
      }
    }
  }