var AdfLocators = new Object;
AdfLocators.findComponentByAbsoluteLocator = function(page, absoluteLocator) {
  AdfAssert.assertNonEmptyString(absoluteLocator);
  var matchComponent = page.findComponent(absoluteLocator);
  if (matchComponent) {
    return matchComponent;
  }
  if (window.AdfDhtmlIteratorPeer) {
    absoluteLocator = AdfDhtmlIteratorPeer.fixupIteratorAbsoluteLocator(absoluteLocator);
  }
  var colonIndex = absoluteLocator.lastIndexOf(":");
  var componentId = colonIndex == -1 ? absoluteLocator : absoluteLocator.substring(colonIndex + 1);
  return AdfLocators._findComponentByAbsoluteLocator(page, componentId, absoluteLocator);
};
AdfLocators.findComponentOrAncestorByAbsoluteLocator = function(page, absoluteLocator) {
  if (window.AdfDhtmlIteratorPeer) {
    absoluteLocator = AdfDhtmlIteratorPeer.fixupIteratorAbsoluteLocator(absoluteLocator);
  }
  while (absoluteLocator.length) {
    var matchComponent = page.findComponent(absoluteLocator);
    if (matchComponent) {
      return matchComponent;
    }
    var colonIndex = absoluteLocator.lastIndexOf(":");
    var componentId = colonIndex == -1 ? absoluteLocator : absoluteLocator.substring(colonIndex + 1);
    if (componentId) {
      var componentIdCharCount = componentId.length;
      if (componentId.charAt(componentIdCharCount - 1) === "]") {
        var leftBracketIndex = componentId.lastIndexOf("[", componentIdCharCount - 2);
        if (leftBracketIndex >= 0) {
          componentId = componentId.substring(0, leftBracketIndex);
          absoluteLocator = absoluteLocator.substring(0, colonIndex + 1 + leftBracketIndex);
        }
      }
      if (componentId) {
        var component = AdfLocators._findComponentByAbsoluteLocator(page, componentId, absoluteLocator);
        if (component) {
          return component;
        }
      }
    }
    if (colonIndex < 1) {
      break;
    }
    if (absoluteLocator.charAt(colonIndex - 1) === ":") {
      colonIndex--;
    }
    absoluteLocator = absoluteLocator.substring(0, colonIndex);
  }
  return null;
};
AdfLocators.findComponentByComplexLocator = function(page, locator) {
  var component = page.findComponentByAbsoluteLocator(locator);
  if (!component) {
    var componentArray = AdfLocators._findComponentsByWildcardLocator(locator);
    if (componentArray.length == 1) {
      component = componentArray[0];
    }
  }
  return component;
};
AdfLocators.findComponentsByComplexLocator = function(page, locator) {
  var component = page.findComponentByAbsoluteLocator(locator);
  var componentArray;
  if (component) {
    componentArray = new Array;
    componentArray.push(component);
  } else {
    componentArray = AdfLocators._findComponentsByWildcardLocator(locator);
  }
  return componentArray;
};
AdfLocators._findComponentByAbsoluteLocator = function(page, componentId, absoluteLocator) {
  var idLength = componentId.length;
  var ClIdToCompMap = page.getClientIdToComponentMap();
  for (var currClientId in ClIdToCompMap) {
    if (idLength <= currClientId.length) {
      var currComponentId = currClientId.substring(currClientId.length - idLength);
      if (currComponentId === componentId) {
        var matchComponent = ClIdToCompMap[currClientId];
        if (matchComponent && matchComponent.getAbsoluteLocator() === absoluteLocator) {
          return matchComponent;
        }
      }
    }
  }
  return null;
};
AdfLocators._findComponentsByWildcardLocator = function(locator) {
  var hasWildcard = locator.indexOf("*") >= 0;
  if (!hasWildcard) {
    hasWildcard = locator.indexOf("(") >= 0;
    if (!hasWildcard) {
      hasWildcard = !/\[\d+\]/.test(locator);
    }
  }
  if (hasWildcard) {
    return AdfLocators._findComponentsByParsedWildcardLocator(locator);
  }
  return new Array;
};
AdfLocators._findComponentsByParsedWildcardLocator = function(locator) {
  var nameMatchers = [];
  var conditionMatchers = [];
  AdfLocators._buildMatchers(locator, nameMatchers, conditionMatchers);
  return AdfLocators._executeMatchers(nameMatchers, conditionMatchers);
};
AdfLocators._buildMatchers = function(locator, nameMatchers, conditionMatchers) {
  var charCount = locator.length;
  var charIndex = 0;
  do {
    charIndex = AdfLocators._parseLocator(locator, charIndex, nameMatchers, conditionMatchers);
  } while (charIndex < charCount);
};
AdfLocators._parseLocator = function(locator, startIndex, nameMatchers, conditionMatchers) {
  var charCount = locator.length;
  for (var charIndex = startIndex;charIndex < charCount;charIndex++) {
    var currChar = locator.charAt(charIndex);
    switch(currChar) {
      case "[":
      ;
      case "(":
        var isArrayExpression = currChar == "[";
        var lBracket = isArrayExpression ? "]" : ")";
        var arrayExpression = AdfLocators._getBracketedExpression(locator, charIndex + 1, currChar, lBracket);
        if (isArrayExpression && AdfLocators._isIndex(arrayExpression)) {
          charIndex += arrayExpression.length + 1;
        } else {
          if (isArrayExpression) {
            AdfLocators._addNameMatcher(locator, startIndex, charIndex, nameMatchers);
          }
          var isComplexExpression = !AdfLocators._isWildcardExpression(arrayExpression);
          charIndex += arrayExpression.length + 1;
          startIndex = charIndex + 1;
          if (isComplexExpression) {
            var lastNameMatcher = nameMatchers[nameMatchers.length - 1];
            var nameMatcherFilterId = lastNameMatcher.context.filterId;
            AdfLocators._parseExpression(arrayExpression, nameMatcherFilterId, conditionMatchers);
          }
        }
        break;
      case "*":
        return AdfLocators._addNameMatcher(locator, startIndex, charIndex, nameMatchers);
    }
  }
  return AdfLocators._addNameMatcher(locator, startIndex, charIndex, nameMatchers);
};
AdfLocators._getBracketedExpression = function(locator, startIndex, lBracket, rBracket) {
  var charCount = locator.length;
  var lBracketCount = 1;
  var inQuotes = false;
  var inSingleQuotes = false;
  var inDoubleQuotes = false;
  var escapeNextChar = false;
  for (var charIndex = startIndex;charIndex < charCount;charIndex++) {
    var currChar = locator.charAt(charIndex);
    if (rBracket === currChar) {
      if (!inQuotes) {
        lBracketCount--;
        if (!lBracketCount) {
          return locator.substring(startIndex, charIndex);
        }
      }
    } else {
      if (lBracket === currChar) {
        if (!inQuotes) {
          lBracketCount++;
        }
      } else {
        if ("'" === currChar) {
          if (escapeNextChar) {
            escapeNextChar = false;
          } else {
            if (!inDoubleQuotes) {
              inSingleQuotes = !inSingleQuotes;
              inQuotes = inSingleQuotes;
            }
          }
        } else {
          if ('"' === currChar) {
            if (escapeNextChar) {
              escapeNextChar = false;
            } else {
              if (!inSingleQuotes) {
                inDoubleQuotes = !inDoubleQuotes;
                inQuotes = inDoubleQuotes;
              }
            }
          } else {
            if (inQuotes && "\\" === currChar && !escapeNextChar) {
              escapeNextChar = true;
            } else {
              if (escapeNextChar) {
                escapeNextChar = false;
              }
            }
          }
        }
      }
    }
  }
  throw new Error("Missing '" + rBracket + "' in " + locator);
};
AdfLocators._matchComponentByName = function(component) {
  var locator = component.getAbsoluteLocator();
  var skipIndex = 0;
  var inLocatorToSkipIndex = this.input;
  if (inLocatorToSkipIndex) {
    skipIndex = inLocatorToSkipIndex[locator];
  }
  var matchName = this.name;
  var newSkipIndex = skipIndex + matchName.length;
  var matchingSection = locator.substring(skipIndex, newSkipIndex);
  if (matchName !== matchingSection) {
    return 0;
  }
  var locatorCharCount = locator.length;
  if (!this.wildcarded) {
    if (newSkipIndex != locatorCharCount) {
      return 0;
    }
  } else {
    var matchChars = this.arrayWildcarded ? "]" : ":[";
    for (;newSkipIndex < locatorCharCount;newSkipIndex++) {
      var currChar = locator.charAt(newSkipIndex);
      if (matchChars.indexOf(currChar) >= 0) {
        if (currChar == "]") {
          newSkipIndex++;
        }
        break;
      }
    }
  }
  var outLocatorToSkipIndex = this.output;
  if (outLocatorToSkipIndex == null) {
    outLocatorToSkipIndex = {};
    this.output = outLocatorToSkipIndex;
  }
  outLocatorToSkipIndex[locator] = newSkipIndex;
  return 1;
};
AdfLocators._addNameMatcher = function(locator, startIndex, endIndex, matchers) {
  var hasWildcard = false;
  var hasArrayWildcard = false;
  var charCount = locator.length;
  var nextSearchIndex = endIndex;
  var filterIdIndex = endIndex;
  if (endIndex < charCount) {
    var lastChar = locator.charAt(endIndex);
    if (lastChar === "*") {
      hasWildcard = true;
      nextSearchIndex = endIndex + 1;
      var nextChar = locator.charAt(nextSearchIndex);
      switch(nextChar) {
        case "":
        ;
        case ":":
        ;
        case "[":
        ;
        case "(":
          break;
        default:
          throw new Error("Invalid character after wildcard in " + locator);;
      }
    } else {
      if (lastChar === "[" || lastChar === "(") {
        hasWildcard = true;
        hasArrayWildcard = true;
        filterIdIndex++;
        if (lastChar == "[") {
          endIndex++;
        }
      }
    }
  }
  var id = locator.substring(0, filterIdIndex);
  var nameMatcherContext = {filterId:id, name:locator.substring(startIndex, endIndex), wildcarded:hasWildcard, arrayWildcarded:hasArrayWildcard};
  matchers.push({filter:AdfLocators._matchComponentByName, context:nameMatcherContext});
  return nextSearchIndex;
};
AdfLocators._parseExpression = function(arrayExpression, nameMatcherId, conditionMatchers) {
  conditionMatcherContext = {matcherId:nameMatcherId, expression:arrayExpression};
  conditionMatchers.push({filter:AdfLocators._matchByExpression, context:conditionMatcherContext});
};
AdfLocators._matchByExpression = function(component) {
  var func = function anonymous() {
    return this.expression;
  };
  try {
    return func.call(component);
  } catch (e) {
    AdfLogger.LOGGER.severe("Error: " + e.message + "\nOriginal expression was: " + this.expression + "\nCurrent component locator: " + component.getAbsoluteLocator());
  }
};
AdfLocators._isIndex = function(arrayExpression) {
  return !/\s*\D+\s*/.test(arrayExpression);
};
AdfLocators._isWildcardExpression = function(arrayExpression) {
  return !/\s*[^\*]\s*/.test(arrayExpression);
};
AdfLocators._executeConditionMatchers = function(conditionMatchers, filterOutput, matchedComponents) {
  var page = AdfPage.PAGE;
  var matcherCount = conditionMatchers.length;
  for (var matcherIndex = 0;matchedComponents.length && matcherIndex < matcherCount;matcherIndex++) {
    var currMatcher = conditionMatchers[matcherIndex];
    var currContext = currMatcher.context;
    currContext.filterOutput = filterOutput;
    matchedComponents = page.reduceMatchedComponents(matchedComponents, currMatcher.filter, currContext);
  }
  return matchedComponents;
};
AdfLocators._executeNameMatchers = function(nameMatchers, filterOutput) {
  var matchCount = nameMatchers.length;
  if (!matchCount) {
    return [];
  }
  var page = AdfPage.PAGE;
  var currMatcher = nameMatchers[0];
  var oldContext = currMatcher.context;
  var matchedComponents = page.matchComponents(currMatcher.filter, oldContext);
  var matcherIndex = 1;
  while (matchedComponents.length > 0 && matcherIndex < matchCount) {
    var filterId = oldContext.filterId;
    if (filterId) {
      filterOutput[filterId] = oldContext.output;
    }
    currMatcher = nameMatchers[matcherIndex];
    var currContext = currMatcher.context;
    currContext.input = oldContext.output;
    matchedComponents = page.reduceMatchedComponents(matchedComponents, currMatcher.filter, currContext);
    oldContext = currContext;
    matcherIndex++;
  }
  return matchedComponents;
};
AdfLocators._executeMatchers = function(nameMatchers, conditionMatchers) {
  var filterOutput = {};
  var matchedComponents = AdfLocators._executeNameMatchers(nameMatchers, filterOutput);
  if (matchedComponents.length == 0) {
    return new Array;
  }
  return AdfLocators._executeConditionMatchers(conditionMatchers, filterOutput, matchedComponents);
};
