/**
   * First correlation algorithm : tries to find correlation - if in content, value and path
   * correlations - else (otherwise lowered) if in query, value and (path or name) correlations -
   * else (otherwise lowered) for all path elements, value correlations
   *
   * @param jsonExchange
   * @param inPathFields
   * @param inQueryFields
   * @param inContentFields
   * @param foundOutFields
   */
  private void correlate(
      ExchangeRecord jsonExchange,
      HashMap<String, CandidateField> inPathFields,
      HashMap<String, CandidateField> inQueryFields,
      HashMap<String, CandidateField> inContentFields,
      HashMap<String, CandidateField> foundOutFields) {
    // looking for correlations :
    int level = 16;
    // if in content, value and path correlations
    // ArrayList<ReqResFieldCorrelation> correlations = new ArrayList<ReqResFieldCorrelation>();
    HashMap<Integer, CorrelationLevel> correlationLevels = new HashMap<Integer, CorrelationLevel>();

    if (jsonExchange.getInMessage().getPostData() != null) {
      addCorrelationsFromOutByPathOrNameAndValue(
          correlationLevels, level, foundOutFields, inContentFields);
      level = level / 4;
    }

    // else (otherwise lowered) if in query, value and path correlations
    if (jsonExchange.getInMessage().getQueryString() != null) {
      addCorrelationsFromOutByPathOrNameAndValue(
          correlationLevels, level, foundOutFields, inQueryFields);
      level = level / 4;
    }

    // else (otherwise lowered) for all path elements, value correlations
    for (CandidateField inPathField : inPathFields.values()) {
      List<CandidateField> foundByValueFields = getFromOutByValue(foundOutFields, inPathField);
      for (CandidateField field : foundByValueFields) {
        // exact only
        // correlations.add(new ReqResFieldCorrelation(level - field.getPath().split("/").length*2,
        // inPathField, field, "byValue"));
        // correlationLevels.putCorrelation(new ReqResFieldCorrelation(level -
        // field.getPath().split("/").length*2, inPathField, field, "byValue"));
        putCorrelation(
            correlationLevels,
            new ReqResFieldCorrelation(
                level - field.getPath().split("/").length * 2, inPathField, field, "byValue"));
      }
    }

    // printCorrelations(correlations);
    int inFieldNb = inPathFields.size() + inQueryFields.size() + inContentFields.size();
    int outFieldNb = foundOutFields.size();
    printCorrelations(correlationLevels, inFieldNb, outFieldNb);
  }
  /**
   * Second correlation algorithm : tries to find correlation - if in content, value and (sub)path
   * correlations - else (otherwise lowered) if in query, value and (sub)path correlations - else
   * (otherwise lowered) for all path elements, value correlations
   *
   * @param jsonExchange
   * @param inPathFields
   * @param inQueryFields
   * @param inContentFields
   * @param foundOutFields
   */
  private void correlateWithSubpath(
      ExchangeRecord jsonExchange,
      HashMap<String, CandidateField> inPathFields,
      HashMap<String, CandidateField> inQueryFields,
      HashMap<String, CandidateField> inContentFields,
      HashMap<String, CandidateField> foundOutFields) {
    // looking for correlations :
    int level = 16;
    // if in content, value and path correlations
    ArrayList<Object[]> correlations = new ArrayList<Object[]>();

    if (jsonExchange.getInMessage().getPostData() != null) {
      addCorrelationsFromOutBySubpathAndValue(correlations, level, foundOutFields, inContentFields);
      level = level / 4;
    }

    // else (otherwise lowered) if in query, value and path correlations
    if (jsonExchange.getInMessage().getQueryString() != null) {
      addCorrelationsFromOutBySubpathAndValue(correlations, level, foundOutFields, inQueryFields);
      level = level / 4;
    }

    // else (otherwise lowered) for all path elements, value correlations
    for (CandidateField inPathField : inPathFields.values()) {
      List<CandidateField> foundByValueFields = getFromOutByValue(foundOutFields, inPathField);
      for (CandidateField field : foundByValueFields) {
        // exact only
        correlations.add(
            new Object[] {
              level - field.getPath().split("/").length * 2, inPathField, field, "byValue"
            });
      }
    }

    System.out.println("Found correlations with subpath :");
    for (Object[] correlation : correlations) {
      System.out.println(
          correlation[0] + "\t" + correlation[1] + "\t" + correlation[2] + "\t" + correlation[3]);
    }
    System.out.println();
  }
 @Before
 public void setUp() {
   jsonPostExchange =
       new ExchangeRecord(
           "id1",
           new InMessage("POST", "/test"),
           new OutMessage(200, "{ id: 1, name:'test1', description:'a fine test' }"));
   jsonPostExchange
       .getInMessage()
       .setPostData(
           new PostData(
               "application/javascript", "{ name:'test1', description:'a fine test' }", null));
   jsonGetExchange =
       new ExchangeRecord(
           "id2",
           new InMessage("GET", "/test/1"),
           new OutMessage(200, "{ id: 1, name:'test1', description:'a fine test' }"));
   jsonGetQueryExchange =
       new ExchangeRecord(
           "id3",
           new InMessage("GET", "/test?id=1"),
           new OutMessage(200, "{ id: 1, name:'test1', description:'a fine test' }"));
   jsonGetQueryExchange.getInMessage().setQueryString(new QueryString());
   jsonGetQueryExchange.getInMessage().getQueryString().addQueryParam(new QueryParam("id", "1"));
   jsonGetAllExchange =
       new ExchangeRecord(
           "id4",
           new InMessage("GET", "/test"),
           new OutMessage(200, "[ { id: 1, name:'test1', description:'a fine test' } ]"));
   jsonPostQueryExchange =
       new ExchangeRecord(
           "id1",
           new InMessage("POST", "/test?http_method=GET"),
           new OutMessage(200, "{ id: 1, name:'test1', description:'a fine test' }"));
   jsonPostQueryExchange
       .getInMessage()
       .setPostData(new PostData("application/javascript", "{ id: 1 }", null));
 }
  public void testJsonContent() {
    String trimmedOutContent = jsonGetExchange.getOutMessage().getMessageContent().getRawContent();
    if (trimmedOutContent.startsWith("[") || trimmedOutContent.startsWith("{")) {
      JSONObject jsonOutObject = (JSONObject) JSONSerializer.toJSON(trimmedOutContent);
      HashMap<String, CandidateField> foundOutFields = new HashMap<String, CandidateField>();
      Stack<Object> pathStack = new Stack<Object>();
      visit(jsonOutObject, foundOutFields, pathStack);

      HashMap<String, CandidateField> inPathFields = new HashMap<String, CandidateField>();
      putField(inPathFields, new CandidateField("path.0", "1"));
      putField(inPathFields, new CandidateField("path.0", "test"));
      // ...
    }
  }
  /**
   * Generate templates for request and response part of an exchange record store in the template
   * store : a customized record AND a velocity macro template
   *
   * @param fieldSuggestions Fields to replace by template expressions (Velocity expression in this
   *     case)
   * @param record The exchange record to templatize
   * @param runName The store name to save the template
   * @return a templatized record
   * @throws Exception If a problem occurs
   */
  public ExchangeRecord templatizeRecord(
      TemplateFieldSuggestions fieldSuggestions, ExchangeRecord record /*, String runName*/)
      throws Exception {
    // What to do if the suggested fields are not found in the record InMessage ?? throws an
    // exception, do nothing ?
    /**
     * The TemplateBuilder applies a selection of TemplateFieldSuggestions on a record (request) and
     * uses their fieldSetterInfos to replace those fields' values by template variables (ex. in
     * velocity : $myFieldName), and saves the result in a different record. Note that the
     * TemplateBuilder can therefore be used (or tested) with hand-defined TemplateFieldSuggestions.
     */
    if (fieldSuggestions == null || record == null) {
      throw new IllegalArgumentException(
          "Parameters fieldSuggestions and originalRecord must not be null !");
    }
    for (AbstractTemplateField field : fieldSuggestions.getTemplateFields()) {
      logger.debug(
          "Field to replace in a new custom record : "
              + field.getFieldName()
              + " = "
              + field.getDefaultValue());
      logger.debug(
          "Field type : "
              + field.getParamType()
              + ", position in path : "
              + field.getPathParamPosition());

      // Make a copy of exchange record, save it or pass it directly to templateRenderer ?
      // In case of saving the custom record on disk, where to save it ?
      // Replace in the cloned record the suggested files by a template variable => eg : for
      // velocity $bean.variableName
      // Syntax to use for template variable : $renderer.getFieldValue("fieldName")
      // See TemplateRenderer.getFieldValue method

      // Generate a velocity template and save it in the disk
      // In this template we must have :
      // - A reference on the TemplateRendererBean (provide suggested field values)
      // - No presentation or display informations, must be generic

      // How to retrieve where the suggested field is stored in the record exchange ? Path, query or
      // form param ?
      // No information about this is available in the correlationField ... Need to use
      // templateField like bean with data concerning the param type (and position)

      // Replace the parameter values with template expressions
      if (TemplateFieldType.IN_QUERY_PARAM.equals(field.getParamType())) {
        // TODO : Can be a problem in case of the same value appears several time in the query, for
        // instance check boxes ...
        List<QueryParam> paramList = record.getInMessage().getQueryString().getQueryParams();
        for (QueryParam param : paramList) {
          if (param.getName().equals(field.getFieldName())) {
            param.setValue(VARIABLE_BEAN_PREFIX + field.getFieldName() + VARIABLE_BEAN_SUFFIX);
            break;
          }
        }
      } else if (TemplateFieldType.IN_PATH_PARAM.equals(field.getParamType())) {
        String path = record.getInMessage().getPath();
        // Fastest solution is to replace the value corresponding to the field
        // TODO : Can be a problem in case of the same value appears several time in the path ...
        record
            .getInMessage()
            .setPath(
                path.replace(
                    field.getDefaultValue(),
                    VARIABLE_BEAN_PREFIX
                        + field.getFieldName()
                        + VARIABLE_BEAN_SUFFIX)); // TODO argMap.get(...)
        // Other solution with a StringTokenizer, need to add a StringBuffer to appends tokens to be
        // complete
        /*StringTokenizer tokenizer = new StringTokenizer(path, "/");
        int tokenPosition = 0;
        while(tokenizer.hasMoreTokens()){
        	String token = tokenizer.nextToken();
        	// Compare here the position OR the value ??
        	if(tokenPosition == field.getPathParamPosition()){
        		token = VARIABLE_BEAN_PREFIX + field.getFieldName() + VARIABLE_BEAN_SUFFIX;
        	}
        	tokenPosition++;
        }*/
      } else if (TemplateFieldType.IN_CONTENT_PARAM.equals(field.getParamType())) {
        // TODO : To implements
      } else if (TemplateFieldType.IN_WSDL_PARAM.equals(field.getParamType())) {
        // TODO : To implements
      } else {
        logger.debug(
            "Unable to replace value for unknow field type '" + field.getParamType() + "'");
      }
    }

    /*if(fieldSuggestions != null && fieldSuggestions.getTemplateFields().size() > 0){
    	// Store the custom exchange record
    	//ProxyExchangeRecordFileStore fileStore= new ProxyExchangeRecordFileStore();
    	//fileStore.setStorePath(PropertyManager.getProperty("path.template.store"));
    	try {
    		// TODO : regroup save operations in a same method in StoreManager
    		// Make a copy of the original record
    		//fileStore.createStore(runName);
    		//fileStore.setStorePath(PropertyManager.getProperty("path.template.store") + runName + "/");
    	    // Save customized record
    		//fileStore.save(record);
    		// templateFileMap = fileStore.saveTemplate(new TemplateRecord(record), runName);
    		// Add code to save a fld : containing field suggestions with default values
    		//fileStore.saveFieldSuggestions(fieldSuggestions, runName, record.getExchange().getExchangeID());
    	}
    	catch(Exception ex){
    		logger.error("Unable to save the templates", ex);
    		throw ex;
    	}
    	return templateFileMap;
    }*/
    // Returns null if there is not suggested fields
    return record;
  }