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 }