001/** 002 * Copyright (C) 2014 Universidade de Aveiro, DETI/IEETA, Bioinformatics Group - http://bioinformatics.ua.pt/ 003 * 004 * This file is part of Dicoogle/dicoogle-sdk. 005 * 006 * Dicoogle/dicoogle-sdk is free software: you can redistribute it and/or modify 007 * it under the terms of the GNU General Public License as published by 008 * the Free Software Foundation, either version 3 of the License, or 009 * (at your option) any later version. 010 * 011 * Dicoogle/dicoogle-sdk is distributed in the hope that it will be useful, 012 * but WITHOUT ANY WARRANTY; without even the implied warranty of 013 * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the 014 * GNU General Public License for more details. 015 * 016 * You should have received a copy of the GNU General Public License 017 * along with Dicoogle. If not, see <http://www.gnu.org/licenses/>. 018 */ 019package pt.ua.dicoogle.sdk.settings; 020 021import java.util.Map; 022import java.util.HashMap; 023import org.apache.commons.lang3.StringEscapeUtils; 024import pt.ua.dicoogle.sdk.settings.types.GenericSetting; 025 026/** 027 * Helper for parsing form input values and creating HTML content based on them, 028 * server side. 029 * 030 * @author António Novo <antonio.novo@ua.pt> 031 */ 032public class Utils 033{ 034 /** 035 * This converts and applies the new textual values into the originalSettings HashMap, taking into account their original class/type. 036 * Only the settings that exist on both Maps are updated, this avoid invalid settings and Class type injections from happening. 037 * 038 * @param originalSettings the original advanced/internal settings of a plugin or service. 039 * @param newSettings the new settings to apply. 040 * @return the originalSettings HashMap with the new values in place (you can use the reference passed as originalSettings instead if you want, they will allways be the same). 041 */ 042 public static HashMap<String, Object> processAdvancedSettings(HashMap<String, Object> originalSettings, HashMap<String, String[]> newSettings) 043 { 044 for (Map.Entry<String, Object> setting : originalSettings.entrySet()) 045 { 046 String name = getHTMLElementIDFromString(setting.getKey()); // NOTE remmember that the setting name is "kinda encoded" (and not in the URLEncode way, that's handled automagically) 047 Object value = setting.getValue(); 048 049 if (newSettings.containsKey(name)) // if the setting is found on the request try to update its value (maintaining the same type ofc) 050 { 051 String[] newValue = newSettings.get(name); 052 053 if (value != null) 054 { 055 // parse the value depending on its class 056 if (value.getClass().equals(Integer.class)) 057 value = Integer.valueOf(newValue[0]); 058 else if (value.getClass().equals(Float.class)) 059 value = Float.valueOf(newValue[0]); 060 else if (value.getClass().equals(Boolean.class)) 061 value = Boolean.valueOf(parseCheckBoxValue(newValue[0])); 062 else if (value instanceof GenericSetting) 063 value = ((GenericSetting) value).fromHTTPParams(newSettings, 0, name); 064 else // String or unrecognized class 065 value = newValue[0]; 066 067 setting.setValue(value); 068 } 069 else // null is treated as String 070 { 071 value = newValue[0]; 072 073 setting.setValue(value); 074 } 075 076 } 077 else // if the setting was not found check if it's a Boolean one, this is a FIX because browsers omit unchecked checkboxes on form action 078 { 079 if (value.getClass().equals(Boolean.class)) 080 setting.setValue(new Boolean(false)); 081 else if (value instanceof GenericSetting) 082 { 083 value = ((GenericSetting) value).fromHTTPParams(newSettings, 0, name); 084 setting.setValue(value); 085 } 086 } 087 } 088 089 return originalSettings; // just for easier use 090 } 091 092 /** 093 * Given the string value of a checkbox obtained from an http request, 094 * returns its boolean value. 095 * 096 * @param value the checkbox value obtained from an http request. 097 * @return the checkbox state in boolean form. 098 */ 099 public static boolean parseCheckBoxValue(String value) 100 { 101 // default state is not checked 102 boolean result = false; 103 104 // if the value is defined 105 if ((value != null) && (! value.isEmpty())) 106 result = value.equalsIgnoreCase("On"); // check if it matches the "on" string 107 108 return result; 109 } 110 111 /** 112 * Given the string value of a checkbox obtained from an http request, 113 * returns its boolean value. 114 * 115 * @param values an array of Strings where the checkbox value obtained from an http request is. 116 * @param index the index on the array where the wanted checkbox value is. 117 * @return the checkbox state in boolean form. 118 */ 119 public static boolean parseCheckBoxValue(String[] values, int index) 120 { 121 // default state is not checked 122 boolean result = false; 123 124 // if the value is defined 125 if ((values != null) && (values.length > index)) 126 result = parseCheckBoxValue(values[index]); 127 128 return result; 129 } 130 131 /** 132 * Based on a input String, returns a valid HTML element name or ID for it. 133 * <b>NOTE:</b> be aware that repeated calls with the same input String 134 * will return the same result, so, on these cases name/ID collisions will happen! 135 * 136 * @param input the input String. 137 * @return a valid HTML element ID or name. 138 */ 139 public static String getHTMLElementIDFromString(String input) 140 { 141 if ((input == null) || input.trim().isEmpty()) 142 return input; 143 144 input = input.trim(); 145 146 // the name must start with a letter, if it doesn't them add an "s" to it 147 if (! input.substring(0, 1).matches("[A-Za-z]")) 148 input = "s" + input; 149 150 // these are the whitelisted (valid) chars, replace all the others with nothing 151 return input.replaceAll("[^A-Za-z0-9-_]", ""); 152 } 153 154 /** 155 * Based on the "real" type/class of the plugin setting Object returns the appropriate form input for it. 156 * 157 * @param name the Form Name of this HTML input. 158 * @param value a Object. 159 * @param isArrayElement if this value Object is part of a another multiple value Object. 160 * @return the appropriate form input for the supplied Object. 161 */ 162 public static String getHTMLInputFromType(String name, Object value, boolean isArrayElement) 163 { 164 String result = "<input name=\"" + name + (isArrayElement ? "[]" : "") + "\" "; 165 166 if (value == null) 167 { 168 result += "type=\"text\" value=\"\" />"; 169 } 170 else if (value.getClass().equals(Integer.class)) 171 { 172 result += "type=\"number\" value=\"" + ((Integer) value).intValue() + "\" />"; 173 } 174 else if (value.getClass().equals(Float.class)) 175 { 176 result += "type=\"number\" value=\"" + ((Float) value).floatValue() + "\" />"; 177 } 178 else if (value.getClass().equals(Boolean.class)) 179 { 180 result += "type=\"checkbox\" " + (((Boolean) value).booleanValue() ? "checked=\"checked\"" : "") + " />"; 181 } 182 else if (value instanceof GenericSetting) 183 { 184 result = ((GenericSetting) value).toHTMLString(name + (isArrayElement ? "[]" : "")); 185 } 186 else 187 // NOTE add extra data type classes here, if needed 188 if (value.getClass().equals(String.class)) 189 { 190 result += "type=\"text\" value=\"" + StringEscapeUtils.escapeHtml4((String) value) + "\" />"; 191 } 192 else // unrecognized type/class 193 { 194 //throw new ClassCastException("Unsupported class \"" + value.getClass().getName() + "\""); 195 //result += "type=\"text\" value=\"" + StringEscapeUtils.escapeHtml4( value.toString()) + "\" />"; 196 result += StringEscapeUtils.escapeHtml4( value.toString()); 197 } 198 199 return result; 200 } 201 202 /** 203 * Based on the "real" type/class of the plugin setting Object returns the appropriate form input for it. 204 * This procedure espects the value Object to not be a part of a another multiple value Object. 205 * 206 * @param id the ID and Name of this HTML input. 207 * @param value a Object. 208 * @return the appropriate form input for the supplied Object. 209 */ 210 public static String getHTMLInputFromType(String id, Object value) 211 { 212 return getHTMLInputFromType(id, value, false); 213 } 214 215 /** 216 * Converts a String value into the type/class of originalValue. 217 * 218 * @param originalValue the original/target type/class of the value. 219 * @param newValue the new value in String form. 220 * @return a new Object of the same class as orignalValue but it's value updated to newValue. 221 */ 222 public static Object convertStringValueIntoProperType(Object originalValue, HashMap<String, String[]> params, int index, String htmlElementID) 223 { 224 Object result = null; 225 226 if (originalValue == null) 227 { 228 result = null; 229 } 230 else if (originalValue.getClass().equals(Integer.class)) 231 { 232 String[] values = params.get(htmlElementID); 233 if ((values == null) || (values.length <= index) || (params.get(htmlElementID)[index] == null) || (params.get(htmlElementID)[index].isEmpty())) 234 result = 0; 235 else 236 result = Integer.valueOf(params.get(htmlElementID)[index]); 237 } 238 else if (originalValue.getClass().equals(Float.class)) 239 { 240 String[] values = params.get(htmlElementID); 241 if ((values == null) || (values.length <= index) || (params.get(htmlElementID)[index] == null) || (params.get(htmlElementID)[index].isEmpty())) 242 result = 0.0F; 243 else 244 result = Float.valueOf(params.get(htmlElementID)[index]); 245 } 246 else if (originalValue.getClass().equals(Boolean.class)) 247 { 248 String[] values = params.get(htmlElementID); 249 if ((values == null) || (values.length <= index) || (params.get(htmlElementID)[index] == null) || (params.get(htmlElementID)[index].isEmpty())) 250 result = false; 251 else 252 result = Boolean.valueOf(params.get(htmlElementID)[index]); 253 } 254 else if (originalValue instanceof GenericSetting) 255 { 256 result = ((GenericSetting) originalValue).fromHTTPParams(params, index, htmlElementID); 257 } 258 else 259 // NOTE add extra data type classes here, if needed 260 if (originalValue.getClass().equals(String.class)) 261 { 262 result = params.get(htmlElementID)[index]; 263 } 264 else // unrecognized type/class 265 { 266 //throw new ClassCastException("Unsupported class \"" + value.getClass().getName() + "\""); 267 result = originalValue; 268 } 269 270 return result; 271 } 272}