Package com.crawljax.forms

Source Code of com.crawljax.forms.FormInputValueHelper

package com.crawljax.forms;

import java.io.IOException;
import java.util.ArrayList;
import java.util.HashSet;
import java.util.List;
import java.util.Map.Entry;
import java.util.Random;
import java.util.Set;
import java.util.regex.Matcher;
import java.util.regex.Pattern;

import javax.xml.xpath.XPathExpressionException;

import org.slf4j.Logger;
import org.slf4j.LoggerFactory;
import org.w3c.dom.Document;
import org.w3c.dom.Element;
import org.w3c.dom.NamedNodeMap;
import org.w3c.dom.Node;

import com.crawljax.browser.EmbeddedBrowser;
import com.crawljax.condition.eventablecondition.EventableCondition;
import com.crawljax.core.CandidateElement;
import com.crawljax.core.configuration.InputSpecification;
import com.crawljax.core.state.Identification;
import com.crawljax.util.DomUtils;
import com.crawljax.util.XPathHelper;
import com.google.common.collect.ImmutableListMultimap;
import com.google.common.collect.ImmutableMap;

/**
* Helper class for FormHandler.
*/
public final class FormInputValueHelper {

  private static final Logger LOGGER = LoggerFactory.getLogger(FormInputValueHelper.class
          .getName());

  private ImmutableMap<String, String> formFields;
  private ImmutableListMultimap<String, String> formFieldNames;
  private ImmutableListMultimap<String, String> fieldValues;

  private boolean randomInput;

  private static final int EMPTY = 0;

  /**
   * @param inputSpecification
   *            the input specification.
   * @param randomInput
   *            if random data should be used on the input fields.
   */
  public FormInputValueHelper(InputSpecification inputSpecification, boolean randomInput) {

    this.formFieldNames = inputSpecification.getFormFieldNames();
    this.fieldValues = inputSpecification.getFormFieldValues();

    ImmutableMap.Builder<String, String> builder = ImmutableMap.builder();
    for (Entry<String, String> entry : formFieldNames.entries()) {
      builder.put(entry.getValue(), entry.getKey());
    }
    formFields = builder.build();

    this.randomInput = randomInput;
  }

  private Element getBelongingElement(Document dom, String fieldName) {
    List<String> names = getNamesForInputFieldId(fieldName);
    if (names != null) {
      for (String name : names) {
        String xpath = "//*[@name='" + name + "' or @id='" + name + "']";
        try {
          Node node = DomUtils.getElementByXpath(dom, xpath);
          if (node != null) {
            return (Element) node;
          }
        } catch (XPathExpressionException e) {
          LOGGER.debug(e.getLocalizedMessage(), e);
          // just try next
        }
      }
    }
    return null;
  }

  private int getMaxNumberOfValues(List<String> fieldNames) {
    int maxValues = 0;
    // check the maximum number of form inputValues
    for (String fieldName : fieldNames) {
      List<String> values = getValuesForName(fieldName);
      if (values != null && values.size() > maxValues) {
        maxValues = values.size();
      }
    }
    return maxValues;
  }

  /**
   * @param browser
   *            the browser instance
   * @param sourceElement
   *            the form elements
   * @param eventableCondition
   *            the belonging eventable condition for sourceElement
   * @return a list with Candidate elements for the inputs
   */
  public List<CandidateElement> getCandidateElementsForInputs(EmbeddedBrowser browser,
          Element sourceElement, EventableCondition eventableCondition) {
    List<CandidateElement> candidateElements = new ArrayList<CandidateElement>();
    int maxValues = getMaxNumberOfValues(eventableCondition.getLinkedInputFields());

    if (maxValues == EMPTY) {
      LOGGER.warn("No input values found for element: "
              + DomUtils.getElementString(sourceElement));
      return candidateElements;
    }

    Document dom;
    try {
      dom = DomUtils.asDocument(browser.getDomWithoutIframeContent());
    } catch (IOException e) {
      LOGGER.error("Catched IOException while parsing dom", e);
      return candidateElements;
    }

    // add maxValues Candidate Elements for every input combination
    for (int curValueIndex = 0; curValueIndex < maxValues; curValueIndex++) {
      List<FormInput> formInputsForCurrentIndex = new ArrayList<FormInput>();
      for (String fieldName : eventableCondition.getLinkedInputFields()) {
        Element element = getBelongingElement(dom, fieldName);
        if (element != null) {
          FormInput formInput =
                  getFormInputWithIndexValue(browser, element, curValueIndex);
          formInputsForCurrentIndex.add(formInput);
        } else {
          LOGGER.warn("Could not find input element for: " + fieldName);
        }
      }

      String id = eventableCondition.getId() + "_" + curValueIndex;
      sourceElement.setAttribute("atusa", id);

      // clone node inclusive text content
      Element cloneElement = (Element) sourceElement.cloneNode(false);
      cloneElement.setTextContent(DomUtils.getTextValue(sourceElement));

      CandidateElement candidateElement =
              new CandidateElement(cloneElement,
                      XPathHelper.getXPathExpression(sourceElement),
                      formInputsForCurrentIndex);
      candidateElements.add(candidateElement);
    }
    return candidateElements;
  }

  /**
   * @param input
   *            the form input
   * @param dom
   *            the document
   * @return returns the belonging node to input in dom
   * @throws XPathExpressionException
   *             if a failure is occurred.
   */
  public Node getBelongingNode(FormInput input, Document dom) throws XPathExpressionException {

    Node result = null;

    switch (input.getIdentification().getHow()) {
      case xpath:
        result = DomUtils.getElementByXpath(dom, input.getIdentification().getValue());
        break;

      case id:
      case name:
        String xpath = "";
        String element = "";

        if (input.getType().equalsIgnoreCase("select")
                || input.getType().equalsIgnoreCase("textarea")) {
          element = input.getType().toUpperCase();
        } else {
          element = "INPUT";
        }
        xpath =
                "//" + element + "[@name='" + input.getIdentification().getValue()
                        + "' or @id='" + input.getIdentification().getValue() + "']";
        result = DomUtils.getElementByXpath(dom, xpath);
        break;

      default:
        LOGGER.info("Identification " + input.getIdentification()
                + " not supported yet for form inputs.");
        break;

    }

    return result;
  }

  /**
   * @param element
   * @return returns the id of the element if set, else the name. If none found, returns null
   * @throws Exception
   */
  private Identification getIdentification(Node element) throws Exception {
    NamedNodeMap attributes = element.getAttributes();
    if (attributes.getNamedItem("id") != null) {
      return new Identification(Identification.How.id, attributes.getNamedItem("id")
              .getNodeValue());
    } else if (attributes.getNamedItem("name") != null) {
      return new Identification(Identification.How.name, attributes.getNamedItem("name")
              .getNodeValue());
    }

    // try to find the xpath
    String xpathExpr = XPathHelper.getXPathExpression(element);
    if (xpathExpr != null && !xpathExpr.equals("")) {
      return new Identification(Identification.How.xpath, xpathExpr);
    }

    return null;
  }

  /**
   * @param browser
   *            the current browser instance
   * @param element
   *            the element in the dom
   * @return the first related formInput belonging to element in the browser
   */
  public FormInput getFormInputWithDefaultValue(EmbeddedBrowser browser, Node element) {
    return getFormInput(browser, element, 0);
  }

  /**
   * @param browser
   *            the current browser instance
   * @param element
   *            the element in the dom
   * @param indexValue
   *            the i-th specified value. if i>#values, first value is used
   * @return the specified value with index indexValue for the belonging elements
   */
  public FormInput getFormInputWithIndexValue(EmbeddedBrowser browser, Node element,
          int indexValue) {
    return getFormInput(browser, element, indexValue);
  }

  private FormInput getFormInput(EmbeddedBrowser browser, Node element, int indexValue) {
    Identification identification;
    try {
      identification = getIdentification(element);
      if (identification == null) {
        return null;
      }
    } catch (Exception e) {
      return null;
    }
    // CHECK
    String id = fieldMatches(identification.getValue());
    FormInput input = new FormInput();
    input.setType(getElementType(element));
    input.setIdentification(identification);
    Set<InputValue> values = new HashSet<InputValue>();

    if (id != null && fieldValues.containsKey(id)) {
      // TODO: make multiple selection for list available
      // add defined value to element
      String value;
      if (indexValue == 0 || fieldValues.get(id).size() == 1
              || indexValue + 1 > fieldValues.get(id).size()) {
        // default value
        value = fieldValues.get(id).get(0);
      } else if (indexValue > 0) {
        // index value
        value = fieldValues.get(id).get(indexValue);
      } else {
        // random value
        value =
                fieldValues.get(id).get(
                        new Random().nextInt(fieldValues.get(id).size() - 1));
      }

      if (input.getType().equals("checkbox") || input.getType().equals("radio")) {
        // check element
        values.add(new InputValue(value, value.equals("1")));
      } else {
        // set value of text input field
        values.add(new InputValue(value, true));
      }

      input.setInputValues(values);
    } else {

      if (this.randomInput) {
        return browser.getInputWithRandomValue(input);
      }
      // field is not specified, lets try a random value

    }
    return input;
  }

  private String fieldMatches(String fieldName) {
    for (String field : formFields.keySet()) {
      Pattern p = Pattern.compile(field, Pattern.CASE_INSENSITIVE);
      Matcher m = p.matcher(fieldName);
      if (m.matches()) {
        return formFields.get(field);
      }
    }
    return null;
  }

  private List<String> getValuesForName(String inputFieldId) {
    if (!fieldValues.containsKey(inputFieldId)) {
      return null;
    }
    return fieldValues.get(inputFieldId);
  }

  private List<String> getNamesForInputFieldId(String inputFieldId) {
    if (!formFieldNames.containsKey(inputFieldId)) {
      return null;
    }
    return formFieldNames.get(inputFieldId);
  }

  private String getElementType(Node node) {
    if (node.getAttributes().getNamedItem("type") != null) {
      return node.getAttributes().getNamedItem("type").getNodeValue().toLowerCase();
    } else if (node.getNodeName().equalsIgnoreCase("input")) {
      return "text";
    } else {
      return node.getNodeName().toLowerCase();
    }
  }

}
TOP

Related Classes of com.crawljax.forms.FormInputValueHelper

TOP
Copyright © 2018 www.massapi.com. All rights reserved.
All source code are property of their respective owners. Java is a trademark of Sun Microsystems, Inc and owned by ORACLE Inc. Contact coftware#gmail.com.