Project Tiles has retired. For details please refer to its Attic page.
PatternUtil xref
View Javadoc

1   /*
2    * $Id: PatternUtil.java 1594481 2014-05-14 06:52:35Z mck $
3    *
4    * Licensed to the Apache Software Foundation (ASF) under one
5    * or more contributor license agreements.  See the NOTICE file
6    * distributed with this work for additional information
7    * regarding copyright ownership.  The ASF licenses this file
8    * to you under the Apache License, Version 2.0 (the
9    * "License"); you may not use this file except in compliance
10   * with the License.  You may obtain a copy of the License at
11   *
12   * http://www.apache.org/licenses/LICENSE-2.0
13   *
14   * Unless required by applicable law or agreed to in writing,
15   * software distributed under the License is distributed on an
16   * "AS IS" BASIS, WITHOUT WARRANTIES OR CONDITIONS OF ANY
17   * KIND, either express or implied.  See the License for the
18   * specific language governing permissions and limitations
19   * under the License.
20   */
21  
22  package org.apache.tiles.definition.pattern;
23  
24  import java.text.MessageFormat;
25  import java.util.ArrayList;
26  import java.util.LinkedHashMap;
27  import java.util.List;
28  import java.util.Locale;
29  import java.util.Map;
30  import java.util.Set;
31  import java.util.regex.Matcher;
32  import java.util.regex.Pattern;
33  
34  import org.apache.tiles.Attribute;
35  import org.apache.tiles.Definition;
36  import org.apache.tiles.Expression;
37  import org.apache.tiles.ListAttribute;
38  
39  /**
40   * Utilities for pattern matching and substitution.
41   *
42   * @version $Rev: 1594481 $ $Date: 2014-05-14 16:52:35 +1000 (Wed, 14 May 2014) $
43   * @since 2.2.0
44   */
45  public final class PatternUtil {
46  
47      /**
48       * The root locale. Notice that this is a replacement for Locale.ROOT for
49       * Java 1.6.
50       */
51      private static final Locale ROOT_LOCALE = new Locale("", "");
52  
53      /** Pattern to find {.*} occurrences that do not match {[0-9]+} so to prevent MessageFormat from crashing.
54       */
55      private static final Pattern INVALID_FORMAT_ELEMENT = Pattern.compile("\\{[^}0-9]+\\}");
56  
57      /**
58       * Private constructor to avoid instantiation.
59       */
60      private PatternUtil() {
61      }
62  
63      /**
64       * Creates a definition given its representation with wildcards and
65       * attribute values with placeholders, replacing real values into
66       * placeholders.
67       *
68       * @param d The definition to replace.
69       * @param name The name of the definition to be created.
70       * @param varsOrig The variables to be substituted.
71       * @return The definition that can be rendered.
72       * @since 2.2.0
73       */
74      public static Definition replacePlaceholders(Definition d, String name,
75              Object... varsOrig) {
76  
77          Object[] vars = replaceNullsWithBlank(varsOrig);
78  
79          Definition nudef = new Definition();
80  
81          nudef.setExtends(replace(d.getExtends(), vars));
82          nudef.setName(name);
83          nudef.setPreparer(replace(d.getPreparer(), vars));
84          Attribute templateAttribute = d.getTemplateAttribute();
85          if (templateAttribute != null) {
86              nudef.setTemplateAttribute(replaceVarsInAttribute(
87                      templateAttribute, vars));
88          }
89  
90          Set<String> attributeNames = d.getLocalAttributeNames();
91          if (attributeNames != null && !attributeNames.isEmpty()) {
92              for (String attributeName : attributeNames) {
93                  Attribute attr = d.getLocalAttribute(attributeName);
94                  Attribute nuattr = replaceVarsInAttribute(attr, vars);
95  
96                  nudef.putAttribute(replace(attributeName, vars), nuattr);
97              }
98          }
99  
100         attributeNames = d.getCascadedAttributeNames();
101         if (attributeNames != null && !attributeNames.isEmpty()) {
102             for (String attributeName : attributeNames) {
103                 Attribute attr = d.getCascadedAttribute(attributeName);
104                 Attribute nuattr = replaceVarsInAttribute(attr, vars);
105 
106                 nudef.putAttribute(replace(attributeName, vars), nuattr, true);
107             }
108         }
109 
110         return nudef;
111     }
112 
113     /**
114      * Creates a new map that contains all the entries of the
115      * <code>defsMap</code> whose keys are contained in <code>keys</code>.
116      *
117      * @param map The map to read.
118      * @param keys The keys to extract.
119      * @param <K> The key of the map.
120      * @param <V> The value of the map.
121      * @return The extracted map.
122      * @since 2.2.1
123      */
124     public static <K, V> Map<K, V> createExtractedMap(Map<K, V> map, Set<K> keys) {
125         Map<K, V> retValue = new LinkedHashMap<K, V>();
126         for (K key : keys) {
127             retValue.put(key, map.get(key));
128         }
129         return retValue;
130     }
131 
132     /**
133      * Replaces variables into an attribute.
134      *
135      * @param attr The attribute to be used as a basis, containing placeholders
136      * for variables.
137      * @param vars The variables to replace.
138      * @return A new instance of an attribute, whose properties have been
139      * replaced with variables' values.
140      */
141     private static Attribute replaceVarsInAttribute(Attribute attr,
142             Object... vars) {
143         Attribute nuattr;
144         if (attr instanceof ListAttribute) {
145             nuattr = replaceVarsInListAttribute((ListAttribute) attr, vars);
146         } else {
147             nuattr = replaceVarsInSimpleAttribute(attr, vars);
148         }
149         return nuattr;
150     }
151 
152     /**
153      * Replaces variables into a simple (not list) attribute.
154      *
155      * @param attr The attribute to be used as a basis, containing placeholders
156      * for variables.
157      * @param vars The variables to replace.
158      * @return A new instance of an attribute, whose properties have been
159      * replaced with variables' values.
160      */
161     private static Attribute replaceVarsInSimpleAttribute(Attribute attr,
162             Object... vars) {
163         Attribute nuattr;
164         nuattr = new Attribute();
165 
166         nuattr.setRole(replace(attr.getRole(), vars));
167         nuattr.setRenderer(attr.getRenderer());
168         Expression expressionObject = attr.getExpressionObject();
169         if (expressionObject != null) {
170             Expression newExpressionObject = Expression
171                     .createExpression(replace(expressionObject.getExpression(), vars), expressionObject.getLanguage());
172             nuattr.setExpressionObject(newExpressionObject);
173         }
174 
175         Object value = attr.getValue();
176         if (value instanceof String) {
177             value = replace((String) value, vars);
178         }
179         nuattr.setValue(value);
180         return nuattr;
181     }
182 
183     /**
184      * Replaces variables into a list attribute.
185      *
186      * @param listAttr The attribute to be used as a basis, containing attributes
187      * that may contain placeholders for variables.
188      * @param vars The variables to replace.
189      * @return A new instance of an attribute, whose properties have been
190      * replaced with variables' values.
191      */
192     private static Attribute replaceVarsInListAttribute(ListAttribute listAttr,
193             Object... vars) {
194         Attribute nuattr;
195         ListAttribute nuListAttr = new ListAttribute();
196         nuListAttr.setInherit(listAttr.isInherit());
197         List<Attribute> nuItems = nuListAttr.getValue();
198         for (Object item : listAttr.getValue()) {
199             Attribute child = (Attribute) item;
200             child = replaceVarsInAttribute(child, vars);
201             nuItems.add(child);
202         }
203         nuattr = nuListAttr;
204         return nuattr;
205     }
206 
207     /**
208      * Replaces a string with placeholders using values of a variable map.
209      *
210      * @param st The string to replace.
211      * @param vars The variables.
212      * @return The replaced string.
213      */
214     private static String replace(String st, Object... vars) {
215         if (st != null && st.indexOf('{') >= 0) {
216 
217             // replace them with markers
218             List<String> originals = new ArrayList<String>();
219             for(Matcher m = INVALID_FORMAT_ELEMENT.matcher(st); m.find() ; m = INVALID_FORMAT_ELEMENT.matcher(st)) {
220                 originals.add(m.group());
221                 st = m.replaceFirst("INVALID_FORMAT_ELEMENT");
222             }
223 
224             // do the MessageFormat replacement (escaping quote characters)
225             st = new MessageFormat(st.replaceAll("'", "'''"), ROOT_LOCALE)
226                     .format(vars, new StringBuffer(), null).toString();
227 
228             // return the markers to their original invalid occurrences
229             for (String original : originals) {
230                 st = st.replaceFirst("INVALID_FORMAT_ELEMENT", original);
231             }
232         }
233         return st;
234     }
235 
236     private static Object[] replaceNullsWithBlank(Object[] varsOrig) {
237         Object[] vars = new Object[varsOrig.length];
238         for(int i = 0; i < varsOrig.length; ++i) {
239             vars[i] = null != varsOrig[i] ? varsOrig[i] : "";
240         }
241         return vars;
242     }
243 }