package jp.vmi.selenium.selenese.locator; import java.util.ArrayDeque; import java.util.Deque; import java.util.regex.Matcher; import java.util.regex.Pattern; import jp.vmi.selenium.selenese.SeleneseRunnerRuntimeException; import static org.apache.commons.lang3.StringUtils.*; /** * Parsed locator. */ @SuppressWarnings("javadoc") public class Locator { // locators. public static final String IDENTIFIER = "identifier"; public static final String ID = "id"; public static final String NAME = "name"; public static final String DOM = "dom"; public static final String XPATH = "xpath"; public static final String LINK = "link"; public static final String CSS = "css"; // locators for frame. public static final String RELATIVE = "relative"; public static final String INDEX = "index"; public static final String RELATIVE_TOP = RELATIVE + "=top"; public static final String RELATIVE_PARENT = RELATIVE + "=parent"; /** Separator between locator and option locator. */ @Deprecated public static final String OPTION_LOCATOR_SEPARATOR = "\0"; /** the array of empty locators. */ public static final Locator[] EMPTY_ARRAY = new Locator[0]; private static final Pattern LOCATORS_RE = Pattern.compile("(\\w+)=(.*)|(document\\..*)|(//.*)"); private static final int LOCATOR_TYPE = 1; private static final int LOCATOR_ARG = 2; private static final int DOM_LOCATOR = 3; private static final int XPATH_LOCATOR = 4; /** locator. */ public final String locator; /** locator type. */ public final String type; /** argument for locator type. */ public final String arg; /** option locator. */ public final OptionLocator poptloc; /** frame list. */ public final Deque<Integer> frameIndexList = new ArrayDeque<>(); private static String formatLocator(String locator, OptionLocator poptloc) { return (poptloc == null) ? locator : locator + " (" + poptloc.locator + ")"; } /** * Constructor. * * @param locator locator with option. (separated by OPTION_LOCATOR_SEPARATOR) */ public Locator(String locator) { String[] pair = locator.split(OPTION_LOCATOR_SEPARATOR, 2); this.locator = pair[0]; this.poptloc = pair.length == 2 ? new OptionLocator(pair[1]) : null; Matcher matcher = LOCATORS_RE.matcher(this.locator); if (matcher.matches()) { String type = matcher.group(LOCATOR_TYPE); String arg = matcher.group(LOCATOR_ARG); if (isNotEmpty(type)) { try { this.type = type.toLowerCase(); } catch (IllegalArgumentException e) { throw new UnsupportedOperationException("Unknown locator type: " + formatLocator(this.locator, this.poptloc), e); } this.arg = arg; } else if (isNotEmpty(matcher.group(DOM_LOCATOR))) { // start with "document." this.type = DOM; this.arg = this.locator; } else if (isNotEmpty(matcher.group(XPATH_LOCATOR))) { // start with "//" this.type = XPATH; this.arg = this.locator; } else { // not reached? throw new UnsupportedOperationException("Unknown locator type: " + formatLocator(this.locator, this.poptloc)); } } else { this.type = IDENTIFIER; this.arg = this.locator; } } private Locator(Locator parent, String optionLocator) { this.locator = parent.locator; this.type = parent.type; this.arg = parent.arg; this.poptloc = new OptionLocator(optionLocator); } /** * Add option locator. * * @param option option locator. * @return locator with option. */ public Locator withOption(String option) { return new Locator(this, option); } public boolean isTypeRelative() { return RELATIVE.equals(type); } public boolean isTypeIndex() { return INDEX.equals(type); } public boolean isRelativeTop() { return RELATIVE_TOP.equals(locator); } public boolean isRelativeParent() { return RELATIVE_PARENT.equals(locator); } public int getIndex() { try { return Integer.parseInt(arg); } catch (NumberFormatException e) { throw new SeleneseRunnerRuntimeException("Invalid \"" + type + "\" locator argument: " + arg, e); } } @Deprecated public String toLocatorString() { return poptloc == null ? locator : locator + OPTION_LOCATOR_SEPARATOR + poptloc.locator; } @Override public String toString() { return formatLocator(locator, poptloc); } }