/*
* JBoss, Home of Professional Open Source.
* Copyright 2014 Red Hat, Inc., and individual contributors
* as indicated by the @author tags.
*
* 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 io.undertow.attribute;
import java.util.ArrayList;
import java.util.Collections;
import java.util.Comparator;
import java.util.List;
import java.util.ServiceLoader;
import io.undertow.UndertowLogger;
import io.undertow.UndertowMessages;
Attribute parser for exchange attributes. This builds an attribute from a string definition.
This uses a service loader mechanism to allow additional token types to be loaded. Token definitions are loaded
from the provided class loader.
Author: Stuart Douglas See Also:
/**
* Attribute parser for exchange attributes. This builds an attribute from a string definition.
* <p>
* This uses a service loader mechanism to allow additional token types to be loaded. Token definitions are loaded
* from the provided class loader.
*
* @author Stuart Douglas
* @see ExchangeAttributes#parser(ClassLoader)
*/
public class ExchangeAttributeParser {
private final List<ExchangeAttributeBuilder> builders;
private final List<ExchangeAttributeWrapper> wrappers;
ExchangeAttributeParser(final ClassLoader classLoader, List<ExchangeAttributeWrapper> wrappers) {
this.wrappers = wrappers;
ServiceLoader<ExchangeAttributeBuilder> loader = ServiceLoader.load(ExchangeAttributeBuilder.class, classLoader);
final List<ExchangeAttributeBuilder> builders = new ArrayList<>();
for (ExchangeAttributeBuilder instance : loader) {
builders.add(instance);
}
//sort with highest priority first
Collections.sort(builders, new Comparator<ExchangeAttributeBuilder>() {
@Override
public int compare(ExchangeAttributeBuilder o1, ExchangeAttributeBuilder o2) {
return Integer.compare(o2.priority(), o1.priority());
}
});
this.builders = Collections.unmodifiableList(builders);
}
Parses the provided value string, and turns it into a list of exchange attributes.
Tokens are created according to the following rules:
%a - % followed by single character. %% is an escape for a literal %
%{.*}a? - % plus curly braces with any amount of content inside, followed by an optional character
${.*} - $ followed by a curly braces to reference an item from the predicate context
Params: - valueString –
Returns:
/**
* Parses the provided value string, and turns it into a list of exchange attributes.
* <p>
* Tokens are created according to the following rules:
* <p>
* %a - % followed by single character. %% is an escape for a literal %
* %{.*}a? - % plus curly braces with any amount of content inside, followed by an optional character
* ${.*} - $ followed by a curly braces to reference an item from the predicate context
*
* @param valueString
* @return
*/
public ExchangeAttribute parse(final String valueString) {
final List<ExchangeAttribute> attributes = new ArrayList<>();
int pos = 0;
int state = 0; //0 = literal, 1 = %, 2 = %{, 3 = $, 4 = ${
for (int i = 0; i < valueString.length(); ++i) {
char c = valueString.charAt(i);
switch (state) {
case 0: {
if (c == '%' || c == '$') {
if (pos != i) {
attributes.add(wrap(parseSingleToken(valueString.substring(pos, i))));
pos = i;
}
if (c == '%') {
state = 1;
} else {
state = 3;
}
}
break;
}
case 1: {
if (c == '{') {
state = 2;
} else if (c == '%') {
//literal percent
attributes.add(wrap(new ConstantExchangeAttribute("%")));
pos = i + 1;
state = 0;
} else {
attributes.add(wrap(parseSingleToken(valueString.substring(pos, i + 1))));
pos = i + 1;
state = 0;
}
break;
}
case 2: {
if (c == '}') {
attributes.add(wrap(parseSingleToken(valueString.substring(pos, i + 1))));
pos = i + 1;
state = 0;
}
break;
}
case 3: {
if (c == '{') {
state = 4;
} else if (c == '$') {
//literal dollars
attributes.add(wrap(new ConstantExchangeAttribute("$")));
pos = i + 1;
state = 0;
} else {
attributes.add(wrap(parseSingleToken(valueString.substring(pos, i + 1))));
pos = i + 1;
state = 0;
}
break;
}
case 4: {
if (c == '}') {
attributes.add(wrap(parseSingleToken(valueString.substring(pos, i + 1))));
pos = i + 1;
state = 0;
}
break;
}
}
}
switch (state) {
case 0:
case 1:
case 3:{
if(pos != valueString.length()) {
attributes.add(wrap(parseSingleToken(valueString.substring(pos))));
}
break;
}
case 2:
case 4: {
throw UndertowMessages.MESSAGES.mismatchedBraces(valueString);
}
}
if(attributes.size() == 1) {
return attributes.get(0);
}
return new CompositeExchangeAttribute(attributes.toArray(new ExchangeAttribute[attributes.size()]));
}
public ExchangeAttribute parseSingleToken(final String token) {
for (final ExchangeAttributeBuilder builder : builders) {
ExchangeAttribute res = builder.build(token);
if (res != null) {
return res;
}
}
if (token.startsWith("%")) {
UndertowLogger.ROOT_LOGGER.unknownVariable(token);
}
return new ConstantExchangeAttribute(token);
}
private ExchangeAttribute wrap(ExchangeAttribute attribute) {
ExchangeAttribute res = attribute;
for(ExchangeAttributeWrapper w : wrappers) {
res = w.wrap(res);
}
return res;
}
}