/*******************************************************************************
* 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.SyntaxException;
import com.galenframework.parser.StringCharReader;
import com.galenframework.parser.StructNode;
import com.galenframework.specs.Place;
import org.apache.commons.lang3.math.NumberUtils;
import org.apache.commons.lang3.tuple.ImmutablePair;
import org.apache.commons.lang3.tuple.Pair;
import java.io.IOException;
import java.util.*;
import java.util.regex.Pattern;
public class ForLoop {
public static final String DEFAULT_VARIABLE_NAME = "index";
private String indexMapping;
private String previousMapping;
private String nextMapping;
private Object[] sequence;
private String variableName;
public ForLoop(Object[] sequence, String variableName, String previousMapping, String nextMapping, String indexMapping) {
this.sequence = sequence;
this.variableName = variableName;
this.previousMapping = previousMapping;
this.nextMapping = nextMapping;
this.indexMapping = indexMapping;
}
public static ForLoop read(boolean isSimpleLoop, PageSpecHandler pageSpecHandler, StringCharReader reader, StructNode originNode) {
try {
String emptyness = reader.readUntilSymbol('[').trim();
if (!emptyness.isEmpty()) {
throw new SyntaxException(originNode, "Unexpected token: " + emptyness);
}
String sequenceStatement = reader.readUntilSymbol(']');
Object[] sequence;
if (isSimpleLoop) {
sequence = readSequenceForSimpleLoop(sequenceStatement, originNode.getPlace());
} else {
sequence = readSequenceFromPageObjects(sequenceStatement, pageSpecHandler);
}
String variableName = DEFAULT_VARIABLE_NAME;
String previousMapping = null;
String nextMapping = null;
String indexMapping = null;
if (reader.hasMoreNormalSymbols()) {
String nextWord = reader.readWord();
if (!nextWord.equals("as")) {
throw new SyntaxException("Invalid token: " + nextWord);
}
variableName = reader.readWord();
if (variableName.isEmpty()) {
throw new SyntaxException("Missing index");
}
if (reader.hasMoreNormalSymbols()) {
reader.readUntilSymbol(',');
Pair<String, String> extraMappings = parseExtraMapping(reader);
if ("prev".equals(extraMappings.getKey())) {
previousMapping = extraMappings.getValue();
} else if ("next".equals(extraMappings.getKey())) {
nextMapping = extraMappings.getValue();
} else if ("index".equals(extraMappings.getKey())) {
indexMapping = extraMappings.getValue();
} else {
throw new SyntaxException("Unknown loop mapping: " + extraMappings.getKey());
}
}
}
return new ForLoop(sequence, variableName, previousMapping, nextMapping, indexMapping);
} catch (SyntaxException ex) {
ex.setPlace(originNode.getPlace());
throw ex;
}
}
private static Pair<String, String> parseExtraMapping(StringCharReader reader) {
String type = reader.readWord();
String as = reader.readWord();
String varName = reader.readWord();
if (type.isEmpty()) {
throw new SyntaxException("Missing type. Expected 'prev' or 'next'");
}
if (!"as".equals(as)) {
throw new SyntaxException("Incorrect statement. Use 'as'");
}
if (varName.isEmpty()) {
throw new SyntaxException("Missing mapping name for '" + type + "'");
}
String theRest = reader.getTheRest().trim();
if (!theRest.isEmpty()) {
throw new SyntaxException("Cannot process: " + theRest);
}
return new ImmutablePair<>(type, varName);
}
private static String[] readSequenceFromPageObjects(String sequenceStatement, PageSpecHandler pageSpecHandler) {
List<String> matchingObjects = pageSpecHandler.findAllObjectsMatchingStrictStatements(sequenceStatement);
return matchingObjects.toArray(new String[matchingObjects.size()]);
}
private static Object[] readSequenceForSimpleLoop(String sequenceStatement, Place place) {
sequenceStatement = sequenceStatement.replace(" ", "");
sequenceStatement = sequenceStatement.replace("\t", "");
Pattern sequencePattern = Pattern.compile(".*\\-.*");
try {
String[] values = sequenceStatement.split(",");
ArrayList<Object> sequence = new ArrayList<>();
for (String stringValue : values) {
if (sequencePattern.matcher(stringValue).matches()) {
sequence.addAll(createSequence(stringValue));
}
else {
sequence.add(convertValueToIndex(stringValue));
}
}
return sequence.toArray(new Object[sequence.size()]);
}
catch (Exception ex) {
throw new SyntaxException(place, "Incorrect sequence syntax: " + sequenceStatement, ex);
}
}
private static Object convertValueToIndex(String stringValue) {
if (NumberUtils.isNumber(stringValue)) {
return NumberUtils.toLong(stringValue);
} else {
return stringValue;
}
}
private static List<Object> createSequence(String value) {
int dashIndex = value.indexOf('-');
int rangeA = Integer.parseInt(value.substring(0, dashIndex));
int rangeB = Integer.parseInt(value.substring(dashIndex + 1));
return createSequence(rangeA, rangeB);
}
private static List<Object> createSequence(int min, int max) {
if (max >= min) {
List<Object> parameters = new LinkedList<>();
for (int i = min; i <= max; i++) {
parameters.add(i);
}
return parameters;
}
else {
return Collections.emptyList();
}
}
public List<StructNode> apply(LoopVisitor loopVisitor) throws IOException {
List<StructNode> resultingNodes = new LinkedList<>();
int begin = 0;
int end = sequence.length;
if (previousMapping != null) {
begin = 1;
}
if (nextMapping != null) {
end = end - 1;
}
int index = 0;
for (int i = begin; i < end; i++) {
index += 1;
Map<String, Object> vars = new HashMap<>();
vars.put(variableName, sequence[i]);
if (previousMapping != null) {
vars.put(previousMapping, sequence[i-1]);
}
if (nextMapping != null) {
vars.put(nextMapping, sequence[i+1]);
}
if (indexMapping != null) {
vars.put(indexMapping, index);
}
resultingNodes.addAll(loopVisitor.visitLoop(vars));
}
return resultingNodes;
}
}