View Javadoc

1   /*
2    * Copyright 2007-2011 smartics, Kronseder & Reiner GmbH
3    *
4    * Licensed under the Apache License, Version 2.0 (the "License");
5    * you may not use this file except in compliance with the License.
6    * You may obtain a copy of the License at
7    *
8    *     http://www.apache.org/licenses/LICENSE-2.0
9    *
10   * Unless required by applicable law or agreed to in writing, software
11   * distributed under the License is distributed on an "AS IS" BASIS,
12   * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
13   * See the License for the specific language governing permissions and
14   * limitations under the License.
15   */
16  package de.smartics.exceptions.i18n.message;
17  
18  import java.util.ArrayList;
19  import java.util.Collections;
20  import java.util.HashMap;
21  import java.util.List;
22  import java.util.Map;
23  
24  import de.smartics.exceptions.i18n.app.ParseExceptionCode;
25  
26  /**
27   * This class provides methods to parse message parameter.
28   *
29   * @author <a href="mailto:robert.reiner@smartics.de">Robert Reiner</a>
30   * @version $Revision:591 $
31   */
32  public final class MessageParamParser
33  {
34    // ********************************* Fields *********************************
35  
36    // --- constants ------------------------------------------------------------
37  
38    // --- members --------------------------------------------------------------
39  
40    // ****************************** Initializer *******************************
41  
42    // ****************************** Constructors ******************************
43  
44    /**
45     * Utility class.
46     */
47    private MessageParamParser()
48    {
49    }
50  
51    // ****************************** Inner Classes *****************************
52  
53    /**
54     * Contains the index information for one message parameter.
55     */
56    public static final class MessageParamInfo
57    {
58      // ******************************** Fields ********************************
59  
60      // --- constants ----------------------------------------------------------
61  
62      // --- members ------------------------------------------------------------
63  
64      /**
65       * The index of the placeholder the information should be written to.
66       */
67      private final int index;
68  
69      /**
70       * The OGNL path to the value inside the value to use.
71       */
72      private final String ognlPath;
73  
74      // ***************************** Initializer ******************************
75  
76      // ***************************** Constructors *****************************
77  
78      /**
79       * Default constructor.
80       *
81       * @param index the index of the placeholder the information should be
82       *          written to.
83       * @param ognlPath the OGNL path to the value inside the value to use.
84       */
85      public MessageParamInfo(final int index, final String ognlPath)
86      {
87        this.index = index;
88        this.ognlPath = ognlPath;
89      }
90  
91      // ***************************** Inner Classes ****************************
92  
93      // ******************************** Methods *******************************
94  
95      // --- init ---------------------------------------------------------------
96  
97      // --- get&set ------------------------------------------------------------
98  
99      /**
100      * Returns the index of the placeholder the information should be written
101      * to.
102      *
103      * @return the index of the placeholder the information should be written
104      *         to.
105      */
106     public int getIndex()
107     {
108       return index;
109     }
110 
111     /**
112      * Returns the OGNL path to the value inside the value to use.
113      *
114      * @return the OGNL path to the value inside the value to use.
115      */
116     public String getOgnlPath()
117     {
118       return ognlPath;
119     }
120 
121     // --- business -----------------------------------------------------------
122 
123     // --- object basics ------------------------------------------------------
124   }
125 
126   // ********************************* Methods ********************************
127 
128   // --- init -----------------------------------------------------------------
129 
130   // --- get&set --------------------------------------------------------------
131 
132   // --- business -------------------------------------------------------------
133 
134   /**
135    * Splits the input string into message param informations.
136    * <p>
137    * The expected format is a comma separated list of
138    * <code>index:ognlPath</code> strings. The OGNL path may be missing, in this
139    * case the index alone.
140    * <p>
141    * Example: <code>0:user.name,4,3:user.id</code>.
142    * <p>
143    * Spaces between tokens are removed.
144    * <p>
145    * If the input string is <code>null</code> or empty, the empty list is
146    * returned.
147    *
148    * @param input the input string to parse.
149    * @return the list of message parameter infos.
150    * @throws ParseException if the input is violating the expected syntax.
151    */
152   public static List<MessageParamInfo> parse(final String input)
153     throws ParseException
154   {
155     if (input == null || "".equals(input.trim()))
156     {
157       return Collections.<MessageParamInfo> emptyList();
158     }
159     final List<String> tokens = Helper.split(input);
160     final List<MessageParamInfo> infos = new ArrayList<MessageParamInfo>(tokens // NOPMD
161         .size());
162     for (String token : tokens)
163     {
164       final byte index;
165       final String ognlPath;
166 
167       final int i = token.indexOf(':');
168       if (i >= 0)
169       {
170         index = Byte.parseByte(token.substring(0, i).trim()); // NOPMD
171         ognlPath = token.substring(i + 1).trim();
172         if ("".equals(ognlPath))
173         {
174           throw new ParseException(ParseExceptionCode.OGNL, token, i + 1);
175         }
176       }
177       else
178       {
179         index = Byte.parseByte(token);
180         ognlPath = null;
181       }
182 
183       final MessageParamInfo info = new MessageParamInfo(index, ognlPath); // NOPMD
184       infos.add(info);
185     }
186 
187     return infos;
188   }
189 
190   /**
191    * Parses the content of the parent message parameter. The input must follow
192    * the following syntax:
193    * <p>
194    * <code>property=index:ognl-path</code> as in <code>cause=1:message</code>
195    * <p>
196    * If several properties of a referenced entity are to be displayed at
197    * different indices, separate them by commas and the information for
198    * different attributes separate by colons (<code>;</code>).
199    * <p>
200    * <code>cause=1:cause.message,3:message,4:localizedMessage; message=5</code>
201    *
202    * @param input the input string to parse.
203    * @return the parsed information. The key of the map is the name of the
204    *         attribute, the value is the index information for that attribute.
205    * @throws ParseException if the input is violating the expected syntax.
206    */
207   public static Map<String, List<MessageParamInfo>> parseParentMessageParam(
208       final String input) throws ParseException
209   {
210     if (input == null || "".equals(input.trim()))
211     {
212       return Collections.emptyMap();
213     }
214 
215     final Map<String, List<MessageParamInfo>> map =
216         new HashMap<String, List<MessageParamInfo>>(// NOPMD
217             3);
218     final List<String> tokens = Helper.split(input, ";");
219     int currentParsingPosition = 0; // NOPMD
220     for (String token : tokens)
221     {
222       parseToken(input, map, token, currentParsingPosition);
223       currentParsingPosition = currentParsingPosition + token.length();
224     }
225 
226     return map;
227   }
228 
229   /**
230    * Parses the given token and throws an exception if it cannot be parsed.
231    *
232    * @param input the input string is only used as exception information in case
233    *          an exception is thrown.
234    * @param map if the token is successfully parsed, the information is set to
235    *          this map.
236    * @param token the token to be analyzed. There should be a key and a value
237    *          separated by the equal character ("<code>=</code>").
238    * @param currentParsingPosition the current parsing position in the input
239    *          string. This information is only passed to the exception in case
240    *          one is thrown.
241    * @throws ParseException if the token cannot be parsed.
242    */
243   private static void parseToken(final String input,
244       final Map<String, List<MessageParamInfo>> map, final String token,
245       final int currentParsingPosition) throws ParseException
246   {
247     final int index = token.indexOf('=');
248     if (index != -1)
249     {
250       final String attributeName = token.substring(0, index).trim();
251       if ("".equals(attributeName))
252       {
253         throw new ParseException(
254             ParseExceptionCode.MISSING_PARENT_PROPERTY_ATTRIBUTE, input,
255             currentParsingPosition + index);
256       }
257       final String subInput = token.substring(index + 1);
258       if ("".equals(subInput))
259       {
260         throw new ParseException(
261             ParseExceptionCode.MISSING_PARENT_PROPERTY_INDEX, input,
262             currentParsingPosition + index + 1);
263       }
264       final List<MessageParamInfo> info = parse(subInput);
265       map.put(attributeName, info);
266     }
267     else
268     {
269       throw new ParseException(
270           ParseExceptionCode.MISSING_PARENT_PROPERTY_SEPARATOR, input,
271           currentParsingPosition);
272     }
273   }
274 
275   // --- object basics --------------------------------------------------------
276 
277 }