org.ocpsoft.rewrite.param.RegexParameterizedPatternParser Maven / Gradle / Ivy
/*
* Copyright 2011 Lincoln Baxter, III
*
* 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 org.ocpsoft.rewrite.param;
import java.util.ArrayList;
import java.util.Arrays;
import java.util.Iterator;
import java.util.LinkedHashMap;
import java.util.LinkedHashSet;
import java.util.List;
import java.util.Map;
import java.util.Set;
import java.util.regex.Matcher;
import java.util.regex.Pattern;
import org.ocpsoft.common.util.Assert;
import org.ocpsoft.rewrite.context.EvaluationContext;
import org.ocpsoft.rewrite.event.Rewrite;
import org.ocpsoft.rewrite.util.ParseTools;
import org.ocpsoft.rewrite.util.ParseTools.CaptureType;
import org.ocpsoft.rewrite.util.ParseTools.CapturingGroup;
/**
* An {@link org.ocpsoft.rewrite.param.Parameterized} regular expression {@link Pattern}.
*
* @author Lincoln Baxter, III
*/
public class RegexParameterizedPatternParser implements ParameterizedPatternParser
{
private static final char[] REGEX_ESCAPE_END = new char[] { '\\', 'E' };
private static final char[] REGEX_ESCAPE_BEGIN = new char[] { '\\', 'Q' };
private static final String DEFAULT_PARAMETER_PATTERN = ".*";
private Pattern compiledPattern;
private final String pattern;
private final char[] chars;
private final List groups = new ArrayList();
private RegexParameterizedPatternBuilder builder;
private String defaultParameterPattern;
private ParameterStore store;
private CaptureType type;
RegexParameterizedPatternParser(RegexParameterizedPatternBuilder builder,
String defaultParameterPattern, String pattern)
{
this(defaultParameterPattern, pattern);
this.builder = builder;
}
/**
* Create a new {@link RegexParameterizedPatternParser} instance with the default
* {@link org.ocpsoft.rewrite.bind.parse.CaptureType#BRACE} and parameter pattern of ".*".
*/
public RegexParameterizedPatternParser(final String pattern)
{
this(CaptureType.BRACE, DEFAULT_PARAMETER_PATTERN, pattern);
}
/**
* Create a new {@link RegexParameterizedPatternParser} instance with the default {@link CaptureType#BRACE}.
*/
public RegexParameterizedPatternParser(final String parameterPattern, final String pattern)
{
this(CaptureType.BRACE, parameterPattern, pattern);
}
/**
* Create a new {@link RegexParameterizedPatternParser} instance with the default parameter regex of ".*".
*/
public RegexParameterizedPatternParser(final CaptureType type, final String pattern)
{
this(type, DEFAULT_PARAMETER_PATTERN, pattern);
}
/**
* Create a new {@link RegexParameterizedPatternParser} instance.
*/
public RegexParameterizedPatternParser(final CaptureType type, final String defaultParameterPattern,
final String pattern)
{
Assert.notNull(pattern, "Pattern must not be null");
this.defaultParameterPattern = defaultParameterPattern;
this.pattern = pattern;
this.chars = pattern.toCharArray();
this.type = type;
this.groups.addAll(getGroups(type, chars));
}
protected static List getGroups(final CaptureType type, final char[] chars)
{
List groups = new ArrayList();
int cursor = 0;
try {
if (chars.length > 0)
{
int parameterIndex = 0;
while (cursor < chars.length)
{
char c = chars[cursor];
if (!ParseTools.isEscaped(chars, cursor))
{
if (c == '{')
{
int startPos = cursor;
CapturingGroup group = ParseTools.balancedCapture(chars, startPos, chars.length - 1, type);
cursor = group.getEnd();
groups.add(new RegexGroup(group, parameterIndex++));
}
else if (c == '\\')
{
if (chars.length - 1 > cursor)
{
if (chars[cursor + 1] != type.getBegin()
&& chars[cursor + 1] != type.getEnd()
&& chars[cursor + 1] != '\\')
{
throw new ParameterizedPatternSyntaxException("Illegal escape sequence at index " + cursor,
new String(chars), cursor);
}
}
else if (chars.length - 1 == cursor)
{
throw new ParameterizedPatternSyntaxException(
"Illegal partial escape sequence at index [" + cursor + "] of [" + new String(chars)
+ "]", new String(chars), cursor);
}
}
}
cursor++;
}
}
}
catch (ParameterizedPatternSyntaxException e)
{
throw e;
}
catch (Exception e)
{
throw new ParameterizedPatternSyntaxException("Error parsing parameterized pattern due to: " + e.getMessage(),
new String(chars), cursor);
}
return groups;
}
@Override
public ParameterizedPatternResult parse(final String value)
{
return new RegexParameterizedPatternMatchResult(groups, getCompiledPattern(store).matcher(value));
}
public Pattern getCompiledPattern(ParameterStore store)
{
if (compiledPattern == null)
{
StringBuilder patternBuilder = new StringBuilder();
CapturingGroup last = null;
for (RegexGroup group : groups)
{
CapturingGroup capture = group.getCapture();
if ((last != null) && (last.getEnd() < capture.getStart() - 1))
{
patternBuilder.append(REGEX_ESCAPE_BEGIN);
String literal = String.valueOf(Arrays.copyOfRange(chars, last.getEnd() + 1, capture.getStart()));
patternBuilder.append(unescape(literal));
patternBuilder.append(REGEX_ESCAPE_END);
}
else if ((last == null) && (capture.getStart() > 0))
{
patternBuilder.append(REGEX_ESCAPE_BEGIN);
String literal = String.valueOf(Arrays.copyOfRange(chars, 0, capture.getStart()));
patternBuilder.append(unescape(literal));
patternBuilder.append(REGEX_ESCAPE_END);
}
patternBuilder.append('(');
StringBuilder parameterPatternBuilder = new StringBuilder();
if (store != null)
{
if (store.contains(group.getName()))
{
Iterator> iterator = store.get(group.getName()).getConstraints().iterator();
while (iterator.hasNext())
{
Constraint constraint = iterator.next();
if (constraint instanceof RegexConstraint)
{
if (iterator.hasNext())
parameterPatternBuilder.append("(?=");
else
parameterPatternBuilder.append("(?:");
parameterPatternBuilder.append(sanitizePattern(constraint));
parameterPatternBuilder.append(")");
}
}
}
}
if (parameterPatternBuilder.length() > 0)
patternBuilder.append(parameterPatternBuilder);
else
patternBuilder.append(defaultParameterPattern);
patternBuilder.append(')');
last = capture;
}
if ((last != null) && (last.getEnd() < chars.length))
{
patternBuilder.append(REGEX_ESCAPE_BEGIN);
String literal = String.valueOf(Arrays.copyOfRange(chars, last.getEnd() + 1, chars.length));
patternBuilder.append(unescape(literal));
patternBuilder.append(REGEX_ESCAPE_END);
}
else if (last == null)
{
patternBuilder.append(REGEX_ESCAPE_BEGIN);
patternBuilder.append(unescape(String.valueOf(chars)));
patternBuilder.append(REGEX_ESCAPE_END);
}
compiledPattern = Pattern.compile(patternBuilder.toString());
}
return compiledPattern;
}
private String sanitizePattern(Constraint constraint)
{
StringBuilder result = new StringBuilder();
String regex = ((RegexConstraint) constraint).getPattern();
char[] pattern = regex.toCharArray();
for (int i = 0; i < pattern.length; i++)
{
result.append(pattern[i]);
if (pattern[i] == '(' && !ParseTools.isEscaped(pattern, i))
{
if (pattern[i + 1] != '?')
{
result.append("?:");
}
}
}
return result.toString();
}
private String unescape(String literal)
{
String result = literal.replace("\\\\", "\\");
result = result.replace("\\" + type.getBegin(), String.valueOf(type.getBegin()));
return result;
}
@Override
public String toString()
{
return new String(chars);
}
@Override
public String getPattern()
{
return pattern;
}
@Override
public ParameterizedPatternBuilder getBuilder()
{
if (builder == null)
{
builder = new RegexParameterizedPatternBuilder(pattern, this);
builder.setParameterStore(store);
}
return builder;
}
static class RegexGroup
{
private final CapturingGroup capture;
private final int index;
public RegexGroup(final CapturingGroup capture, int index)
{
this.capture = capture;
this.index = index;
}
public int getIndex()
{
return index;
}
public String getName()
{
return new String(capture.getCaptured());
}
public CapturingGroup getCapture()
{
return capture;
}
@Override
public String toString()
{
return "RegexParameter [name=" + getName() + ", capture=" + capture + "]";
}
}
@Override
public Set getRequiredParameterNames()
{
Set result = new LinkedHashSet();
for (RegexGroup group : groups)
{
result.add(group.getName());
}
return result;
}
@Override
public void setParameterStore(ParameterStore store)
{
this.store = store;
}
private static class RegexParameterizedPatternMatchResult implements ParameterizedPatternResult
{
private Matcher matcher;
private List groups;
private Boolean matched;
public RegexParameterizedPatternMatchResult(List groups, Matcher matcher)
{
this.groups = groups;
this.matcher = matcher;
}
/**
* Returns true
if the {@link ParameterizedPattern} matched the input value, false
if
* not.
*/
@Override
public boolean matches()
{
if (matched == null)
{
matched = matcher.matches();
}
return matched;
}
@Override
public Map, String> getParameters(EvaluationContext context)
{
Map, String> values = new LinkedHashMap, String>();
ParameterStore store = DefaultParameterStore.getInstance(context);
if (matcher.matches())
{
for (RegexGroup group : groups)
{
values.put(store.get(group.getName()), matcher.group(group.getIndex() + 1));
}
}
return values;
}
@Override
public boolean isValid(Rewrite event, EvaluationContext context)
{
if (matches())
{
ParameterStore store = DefaultParameterStore.getInstance(context);
int index = 1;
for (RegexGroup group : groups)
{
String value = matcher.group(index++);
Parameter> param = store.get(group.getName());
ParameterValueStore valueStore = DefaultParameterValueStore.getInstance(context);
if (!valueStore.isValid(event, context, param, value))
{
return false;
}
}
return true;
}
return false;
}
@Override
public boolean submit(Rewrite event, EvaluationContext context)
{
if (matches())
{
ParameterStore store = DefaultParameterStore.getInstance(context);
int index = 1;
for (RegexGroup group : groups)
{
String value = matcher.group(index++);
Parameter> param = store.get(group.getName());
ParameterValueStore valueStore = DefaultParameterValueStore.getInstance(context);
if (!valueStore.submit(event, context, param, value))
{
return false;
}
}
return true;
}
return false;
}
}
}
© 2015 - 2025 Weber Informatics LLC | Privacy Policy