001/* 002 * Copyright 2014 Anyware Services 003 * 004 * Licensed under the Apache License, Version 2.0 (the "License"); 005 * you may not use this file except in compliance with the License. 006 * You may obtain a copy of the License at 007 * 008 * http://www.apache.org/licenses/LICENSE-2.0 009 * 010 * Unless required by applicable law or agreed to in writing, software 011 * distributed under the License is distributed on an "AS IS" BASIS, 012 * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. 013 * See the License for the specific language governing permissions and 014 * limitations under the License. 015 */ 016package org.ametys.runtime.parameter; 017 018import java.io.InputStream; 019import java.time.LocalDateTime; 020import java.time.ZonedDateTime; 021import java.time.chrono.IsoChronology; 022import java.time.format.DateTimeFormatter; 023import java.time.format.ResolverStyle; 024import java.util.ArrayList; 025import java.util.Date; 026import java.util.HashMap; 027import java.util.List; 028import java.util.Map; 029 030import org.apache.cocoon.ProcessingException; 031import org.apache.cocoon.xml.AttributesImpl; 032import org.apache.cocoon.xml.XMLUtils; 033import org.slf4j.Logger; 034import org.slf4j.LoggerFactory; 035import org.xml.sax.ContentHandler; 036import org.xml.sax.SAXException; 037 038import org.ametys.core.util.DateUtils; 039import org.ametys.runtime.i18n.I18nizableText; 040 041 042/** 043 * This class handles all needed to use typed parameters 044 */ 045public final class ParameterHelper 046{ 047 /** Enumeration of supported types */ 048 public static enum ParameterType 049 { 050 /** boolean values */ 051 BOOLEAN, 052 /** string values */ 053 STRING, 054 /** password values */ 055 PASSWORD, 056 /** long values */ 057 LONG, 058 /** double values */ 059 DOUBLE, 060 /** date values */ 061 DATE, 062 /** binary values */ 063 BINARY, 064 /** datasource values */ 065 DATASOURCE 066 } 067 068 /** 069 * The ISO date-time formatter that formats or parses a date-time with an offset, such as '2011-12-03T10:15:30.000+01:00'. 070 */ 071 private static DateTimeFormatter __ISO_OFFSET_DATE_TIME = DateTimeFormatter.ofPattern("yyyy-MM-dd'T'HH:mm:ss.SSSXXX").withResolverStyle(ResolverStyle.STRICT).withChronology(IsoChronology.INSTANCE); 072 073 // Logger for traces 074 private static Logger _logger = LoggerFactory.getLogger(ParameterHelper.class); 075 076 private ParameterHelper () 077 { 078 // empty 079 } 080 081 /** 082 * Get the ISO date-time formatter that formats or parses a date-time with an offset, such as '2011-12-03T10:15:30.000+01:00'. 083 * This formatter is similar to {@link DateTimeFormatter#ISO_OFFSET_DATE_TIME} but force 3-digits milliseconds. 084 * @return ISO date-time formatter 085 */ 086 public static DateTimeFormatter getISODateTimeFormatter() 087 { 088 return __ISO_OFFSET_DATE_TIME; 089 } 090 091 /** 092 * Return the readable name of a type 093 * 094 * @param type Type to convert 095 * @return Returns the name of the type 096 * @throws IllegalArgumentException If the type is unknwon 097 */ 098 public static String typeToString(ParameterType type) 099 { 100 return type.name().toLowerCase(); 101 } 102 103 /** 104 * Convert a string containing a type to its value 105 * 106 * @param type Name of the type 107 * @return Type 108 * @throws IllegalArgumentException if the type is unknown 109 */ 110 public static ParameterType stringToType(String type) 111 { 112 try 113 { 114 return ParameterType.valueOf(type.toUpperCase()); 115 } 116 catch (IllegalArgumentException e) 117 { 118 throw new IllegalArgumentException("The type '" + type + "' is unknown for config parameters"); 119 } 120 } 121 122 /** 123 * Cast a untyped value (string) to an object of the type 124 * 125 * @param value Value to cast 126 * @param type Type to cast value in 127 * @return An object of the type 'type' with value 'value', or null if type 128 * is unknown or value cannot be cast 129 */ 130 public static Object castValue(String value, ParameterType type) 131 { 132 133 if (value == null) 134 { 135 return null; 136 } 137 138 try 139 { 140 if (type == ParameterType.BOOLEAN) 141 { 142 return new Boolean(value); 143 } 144 else if (type == ParameterType.STRING) 145 { 146 return value; 147 } 148 else if (type == ParameterType.PASSWORD) 149 { 150 return value; 151 } 152 else if (type == ParameterType.DATASOURCE) 153 { 154 return value; 155 } 156 else if (type == ParameterType.LONG) 157 { 158 return new Long(value); 159 } 160 else if (type == ParameterType.DOUBLE) 161 { 162 return new Double(value); 163 } 164 else if (type == ParameterType.DATE) 165 { 166 LocalDateTime ldt = LocalDateTime.parse(value, DateTimeFormatter.ISO_DATE_TIME); 167 return DateUtils.asDate(ldt); 168 } 169 else if (type == ParameterType.BINARY) 170 { 171 return null; 172 } 173 } 174 catch (Exception nfe) 175 { 176 if (value.length() != 0) 177 { 178 _logger.error("Cannot cast value '" + value + "' into type '" + typeToString(type) + "'. Null object will be used.", nfe); 179 } 180 else if (_logger.isDebugEnabled()) 181 { 182 _logger.debug("Failed to cast empty string to type '" + typeToString(type) + "'. Null object will be used.", nfe); 183 } 184 } 185 return null; 186 } 187 188 189 /** 190 * Converts known types to string 191 * 192 * @param value Typed value 193 * @return String readable by the config bean 194 * @throws IllegalArgumentException if the object is a InputStream 195 */ 196 public static String valueToString(Object value) 197 { 198 if (value == null) 199 { 200 return null; 201 } 202 203 if (value instanceof Date) 204 { 205 ZonedDateTime zdt = DateUtils.asZonedDateTime((Date) value, null); 206 return zdt.format(getISODateTimeFormatter()); 207 } 208 209 if (value instanceof InputStream) 210 { 211 throw new IllegalArgumentException("The object to convert is an input stream"); 212 } 213 214 return value.toString(); 215 } 216 217 /** 218 * SAX a parameter 219 * @param handler The content handler where to SAX 220 * @param parameter The parameter to SAX 221 * @param value The parameter value. Can be null. 222 * @throws SAXException If an error occurred while SAXing 223 * @throws ProcessingException If an error occurred 224 */ 225 public static void toSAXParameter (ContentHandler handler, Parameter parameter, Object value) throws SAXException, ProcessingException 226 { 227 AttributesImpl parameterAttr = new AttributesImpl(); 228 parameterAttr.addAttribute("", "plugin", "plugin", "CDATA", parameter.getPluginName()); 229 XMLUtils.startElement(handler, parameter.getId(), parameterAttr); 230 231 toSAXParameterInternal(handler, parameter, value); 232 233 XMLUtils.endElement(handler, parameter.getId()); 234 } 235 236 /** 237 * SAX a parameter except the root tag 238 * @param handler The content handler where to SAX 239 * @param parameter The parameter to SAX 240 * @param value The parameter value. Can be null. 241 * @throws SAXException If an error occurred while SAXing 242 * @throws ProcessingException If an error occurred 243 */ 244 public static void toSAXParameterInternal(ContentHandler handler, Parameter parameter, Object value) throws SAXException, ProcessingException 245 { 246 parameter.getLabel().toSAX(handler, "label"); 247 parameter.getDescription().toSAX(handler, "description"); 248 249 XMLUtils.createElement(handler, "type", ParameterHelper.typeToString((ParameterType) parameter.getType())); 250 251 Object defaultValue = parameter.getDefaultValue(); 252 253 if (defaultValue != null) 254 { 255 XMLUtils.createElement(handler, "default-value", ParameterHelper.valueToString(defaultValue)); 256 } 257 258 if (value != null) 259 { 260 XMLUtils.createElement(handler, "value", ParameterHelper.valueToString(value)); 261 } 262 263 if (parameter.getWidget() != null) 264 { 265 XMLUtils.createElement(handler, "widget", parameter.getWidget()); 266 } 267 268 Map<String, I18nizableText> widgetParameters = parameter.getWidgetParameters(); 269 if (widgetParameters.size() > 0) 270 { 271 XMLUtils.startElement(handler, "widget-params"); 272 for (String paramName : widgetParameters.keySet()) 273 { 274 XMLUtils.startElement(handler, paramName); 275 widgetParameters.get(paramName).toSAX(handler); 276 XMLUtils.endElement(handler, paramName); 277 } 278 XMLUtils.endElement(handler, "widget-params"); 279 } 280 281 Enumerator enumerator = parameter.getEnumerator(); 282 if (enumerator != null) 283 { 284 toSAXEnumerator(handler, enumerator); 285 } 286 287 Validator validator = parameter.getValidator(); 288 toSAXValidator(handler, validator); 289 } 290 291 /** 292 * SAX parameter enumerator 293 * @param handler The content handler where to SAX 294 * @param enumerator The enumerator to SAX 295 * @throws SAXException If an error occurred to SAX 296 * @throws ProcessingException If an error occurred 297 */ 298 public static void toSAXEnumerator (ContentHandler handler, Enumerator enumerator) throws SAXException, ProcessingException 299 { 300 XMLUtils.startElement(handler, "enumeration"); 301 302 try 303 { 304 for (Map.Entry<Object, I18nizableText> entry : enumerator.getEntries().entrySet()) 305 { 306 String valueAsString = ParameterHelper.valueToString(entry.getKey()); 307 I18nizableText label = entry.getValue(); 308 309 // Generate option 310 AttributesImpl attrs = new AttributesImpl(); 311 attrs.addCDATAAttribute("value", valueAsString); 312 313 XMLUtils.startElement(handler, "option", attrs); 314 315 if (label != null) 316 { 317 label.toSAX(handler); 318 } 319 else 320 { 321 XMLUtils.data(handler, valueAsString); 322 } 323 324 XMLUtils.endElement(handler, "option"); 325 } 326 } 327 catch (Exception e) 328 { 329 throw new ProcessingException("Unable to enumerate entries with enumerator: " + enumerator, e); 330 } 331 332 XMLUtils.endElement(handler, "enumeration"); 333 } 334 335 /** 336 * SAX parameter validator 337 * @param handler The content handler where to SAX 338 * @param validator The validator to SAX 339 * @throws SAXException If an error occurred while SAXing 340 */ 341 public static void toSAXValidator (ContentHandler handler, Validator validator) throws SAXException 342 { 343 if (validator != null) 344 { 345 XMLUtils.startElement(handler, "validation"); 346 validator.saxConfiguration(handler); 347 XMLUtils.endElement(handler, "validation"); 348 } 349 } 350 351 /** 352 * Convert the parameter in a JSON map 353 * @param parameter The parameter to convert 354 * @return The Parameter as a map 355 * @throws Exception If an error occured when converting the parameter 356 */ 357 public static Map<String, Object> toJSON(Parameter parameter) throws Exception 358 { 359 Map<String, Object> result = new HashMap<>(); 360 361 result.put("id", parameter.getId()); 362 result.put("type", parameter.getType()); 363 result.put("default-value", parameter.getDefaultValue()); 364 result.put("description", parameter.getDescription()); 365 result.put("label", parameter.getLabel()); 366 result.put("plugin", parameter.getPluginName()); 367 368 if (parameter.getValidator() != null) 369 { 370 result.put("validation", parameter.getValidator().toJson()); 371 } 372 373 if (parameter.getEnumerator() != null) 374 { 375 List<Map<String, Object>> enumeration = new ArrayList<>(); 376 377 Map<Object, I18nizableText> entries = parameter.getEnumerator().getEntries(); 378 for (Object entryKey : entries.keySet()) 379 { 380 Map<String, Object> option = new HashMap<>(); 381 option.put("value", entryKey); 382 option.put("label", entries.get(entryKey)); 383 enumeration.add(option); 384 } 385 386 result.put("enumeration", enumeration); 387 } 388 389 result.put("widget", parameter.getWidget()); 390 result.put("widget-params", parameter.getWidgetParameters()); 391 392 return result; 393 } 394}