/**
  * Recommends CC components and updates {@code recommendetComponents} and {@code processMeasures}.
  *
  * @param recommendedComponents the recommended measures
  * @param processedMeasures the process measures
  * @param measures measures to consider
  * @param measure measure to find
  * @param targetMimetype the target mimetype
  * @throws PlanningServiceException
  */
 private void recommendCcComponents(
     List<RecommendedComponent> recommendedComponents,
     Set<String> processedMeasures,
     final List<String> measures,
     final String measure,
     final String targetMimetype)
     throws PlanningServiceException {
   List<IServiceInfo> ccWfs = queryCcComponents(measure, targetMimetype);
   Iterator<IServiceInfo> ccIt = ccWfs.iterator();
   if (ccIt.hasNext()) {
     IServiceInfo wfi = ccIt.next();
     WorkflowDescription wfd = MyExperimentRESTClient.getWorkflow(wfi.getDescriptor());
     if (wfd != null) {
       wfd.readMetadata();
       List<Port> outputPorts = wfd.getOutputPorts();
       List<String> ccMeasures = new ArrayList<String>();
       for (Port port : outputPorts) {
         if (measures.contains(port.getValue())) {
           ccMeasures.add(port.getValue());
         }
       }
       processedMeasures.addAll(ccMeasures);
       recommendedComponents.add(new RecommendedComponent(wfd, ccMeasures, null, null, null));
     }
   }
 }
  /**
   * Finds QA components for the provided measures.
   *
   * @param measures measures to find components for
   * @param sourceMimetype source mimetype of the workflow
   * @param targetMimetype target mimetype of the workflow
   * @return a list of workflow description
   */
  public List<RecommendedComponent> recommendComponents(
      final List<String> measures, final String sourceMimetype, final String targetMimetype) {
    List<RecommendedComponent> recommendedComponents = new ArrayList<RecommendedComponent>();
    Set<String> processedMeasures = new HashSet<String>();

    try {
      for (String measure : measures) {
        if (!processedMeasures.contains(measure)) {
          List<IServiceInfo> qaWfs = queryQaComponents(measure, sourceMimetype, targetMimetype);
          Iterator<IServiceInfo> qaIt = qaWfs.iterator();
          if (qaIt.hasNext()) {
            IServiceInfo wfi = qaIt.next();
            WorkflowDescription wfd = MyExperimentRESTClient.getWorkflow(wfi.getDescriptor());
            if (wfd != null) {
              wfd.readMetadata();
              List<Port> outputPorts = wfd.getOutputPorts();

              List<String> leftMeasures = new ArrayList<String>();
              List<String> rightMeasures = new ArrayList<String>();

              for (Port port : outputPorts) {
                if (measures.contains(port.getValue())) {
                  if (port.getRelatedObject() == null) {
                    leftMeasures.add(port.getValue());
                    rightMeasures.add(port.getValue());
                  } else if (ComponentConstants.VALUE_LEFT_OBJECT.equals(port.getRelatedObject())) {
                    leftMeasures.add(port.getValue());
                  } else if (ComponentConstants.VALUE_RIGHT_OBJECT.equals(
                      port.getRelatedObject())) {
                    rightMeasures.add(port.getValue());
                  }
                }
              }
              boolean acceptsLeftMimetype = wfd.acceptsLeftMimetype(targetMimetype);
              boolean acceptsRightMimetype = wfd.acceptsRightMimetype(targetMimetype);
              if (acceptsLeftMimetype && acceptsRightMimetype) {
                if (leftMeasures.size() > rightMeasures.size()) {
                  processedMeasures.addAll(leftMeasures);
                  recommendedComponents.add(
                      new RecommendedComponent(
                          wfd,
                          leftMeasures,
                          InputSource.TARGET_OBJECT,
                          InputSource.SOURCE_OBJECT,
                          RelatedObject.LEFT_OBJECT));
                } else {
                  processedMeasures.addAll(rightMeasures);
                  recommendedComponents.add(
                      new RecommendedComponent(
                          wfd,
                          rightMeasures,
                          InputSource.SOURCE_OBJECT,
                          InputSource.TARGET_OBJECT,
                          RelatedObject.RIGHT_OBJECT));
                }
              } else if (acceptsLeftMimetype) {
                processedMeasures.addAll(leftMeasures);
                recommendedComponents.add(
                    new RecommendedComponent(
                        wfd,
                        leftMeasures,
                        InputSource.TARGET_OBJECT,
                        InputSource.SOURCE_OBJECT,
                        RelatedObject.LEFT_OBJECT));
              } else if (acceptsRightMimetype) {
                processedMeasures.addAll(rightMeasures);
                recommendedComponents.add(
                    new RecommendedComponent(
                        wfd,
                        rightMeasures,
                        InputSource.SOURCE_OBJECT,
                        InputSource.TARGET_OBJECT,
                        RelatedObject.RIGHT_OBJECT));
              }
            }
          } else {
            recommendCcComponents(
                recommendedComponents, processedMeasures, measures, measure, targetMimetype);
          }
        }
      }
    } catch (PlanningServiceException e) {
      // If querying myExperiment fails, it is probably not available, so skip all measures.
      e.printStackTrace();
    }

    return recommendedComponents;
  }