/******************************************************************************* * 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.rules; import com.galenframework.parser.SyntaxException; import java.util.LinkedList; import java.util.List; import java.util.regex.Pattern; /** * Created by ishubin on 2015/02/22. */ public class RuleBuilder { private List<Chunk> chunks = new LinkedList<>(); public NormalTextChunk newNormalTextChunk() { return addChunk(new NormalTextChunk()); } public ParameterChunk newParameterChunk() { return addChunk(new ParameterChunk()); } public WhiteSpaceChunk newWhiteSpaceChunk() { return addChunk(new WhiteSpaceChunk()); } public static abstract class Chunk { public abstract String build(Rule rule); public abstract void appendSymbol(char ch); } public class WhiteSpaceChunk extends Chunk { @Override public String build(Rule rule) { return "\\s+"; } @Override public void appendSymbol(char ch) { } } public static class NormalTextChunk extends Chunk { private StringBuilder stringBuilder = new StringBuilder(); @Override public String build(Rule rule) { String text = stringBuilder.toString(); if (text.isEmpty()) { return text; } else { return Pattern.quote(text); } } @Override public void appendSymbol(char ch) { stringBuilder.append(ch); } } public static class ParameterChunk extends Chunk { private static final int PARSE_NAME = 0; private static final int PARSE_PARAMETER = 1; private int state = 0; private StringBuilder nameBuilder = new StringBuilder(); private StringBuilder regexBuilder = new StringBuilder(); @Override public String build(Rule rule) { String parameterName = nameBuilder.toString().toString().trim(); if (parameterName.isEmpty()) { throw new SyntaxException("Parameter name should not be empty"); } else if (containsInvalidSymbolsForName(parameterName)) { throw new SyntaxException("Incorrect parameter name: " + parameterName); } rule.addParameter(parameterName); String customRegex = regexBuilder.toString().trim(); if (customRegex.isEmpty()) { if (isStillParsingName()) { customRegex = ".*"; } else { throw new SyntaxException("Missing custom regular expression after ':'"); } } return "(" + customRegex + ")"; } private final Pattern objectNamePattern = Pattern.compile("[a-zA-Z0-9_]+"); private boolean containsInvalidSymbolsForName(String parameterName) { return !objectNamePattern.matcher(parameterName).matches(); } @Override public void appendSymbol(char symbol) { if (symbol == ':' && isStillParsingName()) { startParsingCustomRegex(); } else { if (isStillParsingName()) { nameBuilder.append(symbol); } else { regexBuilder.append(symbol); } } } private boolean isStillParsingName() { return state == PARSE_NAME; } private void startParsingCustomRegex() { this.state = PARSE_PARAMETER; } } public <T extends Chunk> T addChunk(T chunk) { this.chunks.add(chunk); return chunk; } public Rule build() { Rule rule = new Rule(); StringBuilder patternBuilder = new StringBuilder(); for (Chunk chunk : chunks) { patternBuilder.append(chunk.build(rule)); } rule.setPattern(Pattern.compile(patternBuilder.toString())); return rule; } }