/*
 * 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; } }