blob: 569416e7270776f2b6010b85ea8ff6b55b9a2c7e [file] [log] [blame]
/*
* $Id$
*
* Licensed to the Apache Software Foundation (ASF) under one
* or more contributor license agreements. See the NOTICE file
* distributed with this work for additional information
* regarding copyright ownership. The ASF licenses this file
* to you 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.apache.tiles.definition.pattern;
import java.text.MessageFormat;
import java.util.LinkedHashMap;
import java.util.List;
import java.util.Locale;
import java.util.Map;
import java.util.Set;
import java.util.regex.Matcher;
import java.util.regex.Pattern;
import org.apache.tiles.Attribute;
import org.apache.tiles.Definition;
import org.apache.tiles.Expression;
import org.apache.tiles.ListAttribute;
/**
* Utilities for pattern matching and substitution.
*
* @version $Rev$ $Date$
* @since 2.2.0
*/
public final class PatternUtil {
/**
* The root locale. Notice that this is a replacement for Locale.ROOT for
* Java 1.6.
*/
private static final Locale ROOT_LOCALE = new Locale("", "");
/** Pattern to find {.*} occurrences that do not match {[0-9]+} so to prevent MessageFormat from crashing.
*/
private static final Pattern INVALID_FORMAT_ELEMENT = Pattern.compile("\\Q{\\E[\\D^}]+\\Q}\\E");
/**
* Private constructor to avoid instantiation.
*/
private PatternUtil() {
}
/**
* Creates a definition given its representation with wildcards and
* attribute values with placeholders, replacing real values into
* placeholders.
*
* @param d The definition to replace.
* @param name The name of the definition to be created.
* @param vars The variables to be substituted.
* @return The definition that can be rendered.
* @since 2.2.0
*/
public static Definition replacePlaceholders(Definition d, String name,
Object... vars) {
Definition nudef = new Definition();
nudef.setExtends(replace(d.getExtends(), vars));
nudef.setName(name);
nudef.setPreparer(replace(d.getPreparer(), vars));
Attribute templateAttribute = d.getTemplateAttribute();
if (templateAttribute != null) {
nudef.setTemplateAttribute(replaceVarsInAttribute(
templateAttribute, vars));
}
Set<String> attributeNames = d.getLocalAttributeNames();
if (attributeNames != null && !attributeNames.isEmpty()) {
for (String attributeName : attributeNames) {
Attribute attr = d.getLocalAttribute(attributeName);
Attribute nuattr = replaceVarsInAttribute(attr, vars);
nudef.putAttribute(replace(attributeName, vars), nuattr);
}
}
attributeNames = d.getCascadedAttributeNames();
if (attributeNames != null && !attributeNames.isEmpty()) {
for (String attributeName : attributeNames) {
Attribute attr = d.getCascadedAttribute(attributeName);
Attribute nuattr = replaceVarsInAttribute(attr, vars);
nudef.putAttribute(replace(attributeName, vars), nuattr, true);
}
}
return nudef;
}
/**
* Creates a new map that contains all the entries of the
* <code>defsMap</code> whose keys are contained in <code>keys</code>.
*
* @param map The map to read.
* @param keys The keys to extract.
* @param <K> The key of the map.
* @param <V> The value of the map.
* @return The extracted map.
* @since 2.2.1
*/
public static <K, V> Map<K, V> createExtractedMap(Map<K, V> map, Set<K> keys) {
Map<K, V> retValue = new LinkedHashMap<K, V>();
for (K key : keys) {
retValue.put(key, map.get(key));
}
return retValue;
}
/**
* Replaces variables into an attribute.
*
* @param attr The attribute to be used as a basis, containing placeholders
* for variables.
* @param vars The variables to replace.
* @return A new instance of an attribute, whose properties have been
* replaced with variables' values.
*/
private static Attribute replaceVarsInAttribute(Attribute attr,
Object... vars) {
Attribute nuattr;
if (attr instanceof ListAttribute) {
nuattr = replaceVarsInListAttribute((ListAttribute) attr, vars);
} else {
nuattr = replaceVarsInSimpleAttribute(attr, vars);
}
return nuattr;
}
/**
* Replaces variables into a simple (not list) attribute.
*
* @param attr The attribute to be used as a basis, containing placeholders
* for variables.
* @param vars The variables to replace.
* @return A new instance of an attribute, whose properties have been
* replaced with variables' values.
*/
private static Attribute replaceVarsInSimpleAttribute(Attribute attr,
Object... vars) {
Attribute nuattr;
nuattr = new Attribute();
nuattr.setRole(replace(attr.getRole(), vars));
nuattr.setRenderer(attr.getRenderer());
Expression expressionObject = attr.getExpressionObject();
if (expressionObject != null) {
Expression newExpressionObject = Expression
.createExpression(replace(expressionObject.getExpression(), vars), expressionObject.getLanguage());
nuattr.setExpressionObject(newExpressionObject);
}
Object value = attr.getValue();
if (value instanceof String) {
value = replace((String) value, vars);
}
nuattr.setValue(value);
return nuattr;
}
/**
* Replaces variables into a list attribute.
*
* @param listAttr The attribute to be used as a basis, containing attributes
* that may contain placeholders for variables.
* @param vars The variables to replace.
* @return A new instance of an attribute, whose properties have been
* replaced with variables' values.
*/
private static Attribute replaceVarsInListAttribute(ListAttribute listAttr,
Object... vars) {
Attribute nuattr;
ListAttribute nuListAttr = new ListAttribute();
nuListAttr.setInherit(listAttr.isInherit());
List<Attribute> nuItems = nuListAttr.getValue();
for (Object item : listAttr.getValue()) {
Attribute child = (Attribute) item;
child = replaceVarsInAttribute(child, vars);
nuItems.add(child);
}
nuattr = nuListAttr;
return nuattr;
}
/**
* Replaces a string with placeholders using values of a variable map.
*
* @param st The string to replace.
* @param vars The variables.
* @return The replaced string.
*/
private static String replace(String st, Object... vars) {
if (st != null && st.indexOf('{') >= 0) {
// remember the invalid "{...}" occurrences
Matcher m = INVALID_FORMAT_ELEMENT.matcher(st);
// replace them with markers
st = INVALID_FORMAT_ELEMENT.matcher(st).replaceAll("INVALID_FORMAT_ELEMENT");
// do the MessageFormat replacement (escaping quote characters)
st = new MessageFormat(st.replaceAll("'", "'''"), ROOT_LOCALE)
.format(vars, new StringBuffer(), null).toString();
// return the markers to their original invalid occurrences
while (m.find()) {
st = st.replace("INVALID_FORMAT_ELEMENT", m.group());
}
}
return st;
}
}