/*******************************************************************************
* Copyright 2017 Ivan Shubin http://galenframework.com
*
* Licensed under the Apache License, Version 2.0 (the "License");
* you may not use this file except in compliance with the License.
* You may obtain a copy of the License at
*
* http://www.apache.org/licenses/LICENSE-2.0
*
* Unless required by applicable law or agreed to in writing, software
* distributed under the License is distributed on an "AS IS" BASIS,
* WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
* See the License for the specific language governing permissions and
* limitations under the License.
******************************************************************************/
package com.galenframework.speclang2.pagespec;
import com.galenframework.parser.ExpectWord;
import com.galenframework.parser.Expectations;
import com.galenframework.parser.StructNode;
import com.galenframework.parser.SyntaxException;
import com.galenframework.specs.page.CorrectionsRect;
import com.galenframework.page.Page;
import com.galenframework.specs.page.Locator;
import com.galenframework.parser.StringCharReader;
import com.galenframework.utils.GalenUtils;
import java.util.*;
import static java.lang.String.format;
public class ObjectDefinitionProcessor {
public static final String GROUPED = "@grouped";
private final PageSpecHandler pageSpecHandler;
private static final String CORRECTIONS_SYMBOL = "@";
public ObjectDefinitionProcessor(PageSpecHandler pageSpecHandler) {
this.pageSpecHandler = pageSpecHandler;
}
private Stack<List<String>> groupStack = new Stack<>();
public List<StructNode> process(StringCharReader reader, StructNode structNode) {
if (!reader.getTheRest().isEmpty()) {
throw new SyntaxException(structNode.getPlace(), "Objects definition does not take any arguments");
}
if (structNode.getChildNodes() != null) {
groupStack = new Stack<>();
for (StructNode childNode : structNode.getChildNodes()) {
parseItem(childNode);
}
}
return Collections.emptyList();
}
private void parseItem(StructNode objectNode) {
parseItem(objectNode, null, null);
}
private void parseItem(StructNode objectNode, String parentName, Locator parentLocator) {
processObject(objectNode, parentName, parentLocator);
}
private void processObject(StructNode objectNode, String parentName, Locator parentLocator) {
StringCharReader reader = new StringCharReader(pageSpecHandler.processExpressionsIn(objectNode).getName());
String objectName = reader.readWord();
if (parentName != null) {
objectName = parentName + "." + objectName;
}
String locatorText = null;
List<String> groups = null;
CorrectionsRect corrections = null;
while(reader.hasMore()) {
String word = expectCorrectionsOrId(objectNode, reader, objectName);
if (word.equals(CORRECTIONS_SYMBOL)) {
corrections = Expectations.corrections().read(reader);
} else if (word.equals(GROUPED)) {
groups = parseInlineGroupsInBrackets(reader);
}
else {
locatorText = word + reader.getTheRest();
reader.moveToTheEnd();
}
}
if (locatorText == null) {
throw new SyntaxException("Missing locator");
}
Locator locator = readLocatorFromString(objectNode, objectName, locatorText.trim());
locator.setCorrections(corrections);
if (parentLocator != null) {
locator.setParent(parentLocator);
}
if (objectName.contains("*")) {
addMultiObjectsToSpec(objectNode, objectName, locator, groups);
} else {
addObjectToSpec(objectNode, objectName, locator, groups);
}
}
private List<String> parseInlineGroupsInBrackets(StringCharReader reader) {
if (reader.firstNonWhiteSpaceSymbol() == '(') {
reader.readUntilSymbol('(');
return GalenUtils.fromCommaSeparated(reader.readUntilSymbol(')'));
} else {
throw new SyntaxException("Missing '(' for group definitions");
}
}
private void addObjectToSpec(StructNode objectNode, String objectName, Locator locator, List<String> groupsForThisObject) {
if (!objectName.matches("[0-9a-zA-Z_\\.\\-]*")) {
throw new SyntaxException("Invalid object name: " + objectName);
}
pageSpecHandler.addObjectToSpec(objectName, locator);
List<String> allCurrentGroups = getAllCurrentGroups();
if (allCurrentGroups != null && !allCurrentGroups.isEmpty()) {
pageSpecHandler.applyGroupsToObject(objectName, allCurrentGroups);
}
if (groupsForThisObject != null && !groupsForThisObject.isEmpty()) {
pageSpecHandler.applyGroupsToObject(objectName, groupsForThisObject);
}
if (objectNode.getChildNodes() != null && objectNode.getChildNodes().size() > 0) {
for (StructNode subObjectNode : objectNode.getChildNodes()) {
parseItem(pageSpecHandler.processExpressionsIn(subObjectNode), objectName, locator);
}
}
}
private void addMultiObjectsToSpec(StructNode objectNode, String objectName, Locator locator, List<String> groupsForThisObject) {
Page page = pageSpecHandler.getPage();
int count = page.getObjectCount(locator);
for (int index = 1; index <= count; index++) {
addObjectToSpec(objectNode, objectName.replace("*", Integer.toString(index)),
new Locator(locator.getLocatorType(), locator.getLocatorValue(), index).withParent(locator.getParent()),
groupsForThisObject);
}
}
private Locator readLocatorFromString(StructNode structNode, String objectName, String locatorText) {
if (locatorText.isEmpty()) {
throw new SyntaxException(structNode.getPlace(),
"Missing locator for object \"" + objectName + "\"");
}
StringCharReader reader = new StringCharReader(locatorText);
String firstWord = reader.readWord();
String locatorValue = reader.getTheRest().trim();
if ("id".equals(firstWord) ||
"css".equals(firstWord) ||
"xpath".equals(firstWord)) {
return createLocator(objectName, firstWord, locatorValue);
}
else {
return identifyLocator(locatorText);
}
}
private Locator identifyLocator(String locatorText) {
if (locatorText.startsWith("/")) {
return new Locator("xpath", locatorText);
}
else {
return new Locator("css", locatorText);
}
}
private Locator createLocator(String objectName, String type, String value) {
if (value == null || value.isEmpty()) {
throw new SyntaxException("Locator for object \"" + objectName + "\" is not defined correctly");
}
return new Locator(type, value);
}
private String expectCorrectionsOrId(StructNode structNode, StringCharReader reader, String objectName) {
String word = new ExpectWord().stopOnTheseSymbols('(').read(reader).trim();
if (word.isEmpty()) {
throw new SyntaxException(structNode.getPlace(),
format("Missing locator for object \"%s\"", objectName));
}
return word;
}
private List<String> getAllCurrentGroups() {
List<String> allCurrentGroups = new LinkedList<>();
Iterator<List<String>> it = groupStack.iterator();
while(it.hasNext()) {
for (String groupName : it.next()) {
if (!allCurrentGroups.contains(groupName)) {
allCurrentGroups.add(groupName);
}
}
}
return allCurrentGroups;
}
}