001/* 002 * (C) Copyright 2006-2018 Nuxeo (http://nuxeo.com/) and others. 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 * 016 * Contributors: 017 * Nuxeo - initial API and implementation 018 */ 019package org.nuxeo.common.xmap; 020 021import java.io.File; 022import java.net.MalformedURLException; 023import java.net.URL; 024import java.text.DateFormat; 025import java.text.ParseException; 026import java.time.Duration; 027import java.util.Date; 028import java.util.Hashtable; 029import java.util.Map; 030import java.util.regex.Matcher; 031import java.util.regex.Pattern; 032 033import org.w3c.dom.Node; 034 035/** 036 * Value factories are used to decode values from XML strings. 037 * <p> 038 * To register a new factory for a given XMap instance use the method 039 * {@link XMap#setValueFactory(Class, XValueFactory)}. 040 * 041 * @author <a href="mailto:[email protected]">Bogdan Stefanescu</a> 042 */ 043public abstract class XValueFactory { 044 045 static final Map<Class<?>, XValueFactory> defaultFactories = new Hashtable<>(); 046 047 public abstract Object deserialize(Context context, String value); 048 049 public abstract String serialize(Context context, Object value); 050 051 public final Object getElementValue(Context context, Node element, boolean trim) { 052 String text = element.getTextContent(); 053 return deserialize(context, trim ? text.trim() : text); 054 } 055 056 public final Object getAttributeValue(Context context, Node element, String name) { 057 Node at = element.getAttributes().getNamedItem(name); 058 return at != null ? deserialize(context, at.getNodeValue()) : null; 059 } 060 061 public static void addFactory(Class klass, XValueFactory factory) { 062 defaultFactories.put(klass, factory); 063 } 064 065 public static XValueFactory getFactory(Class type) { 066 return defaultFactories.get(type); 067 } 068 069 public static Object getValue(Context context, Class klass, String value) { 070 XValueFactory factory = defaultFactories.get(klass); 071 if (factory == null) { 072 return null; 073 } 074 return factory.deserialize(context, value); 075 } 076 077 public static final XValueFactory STRING = new XValueFactory() { 078 @Override 079 public Object deserialize(Context context, String value) { 080 return value; 081 } 082 083 @Override 084 public String serialize(Context context, Object value) { 085 return value.toString(); 086 } 087 }; 088 089 public static final XValueFactory INTEGER = new XValueFactory() { 090 @Override 091 public Object deserialize(Context context, String value) { 092 return Integer.valueOf(value); 093 } 094 095 @Override 096 public String serialize(Context context, Object value) { 097 return value.toString(); 098 } 099 }; 100 101 public static final XValueFactory LONG = new XValueFactory() { 102 @Override 103 public Object deserialize(Context context, String value) { 104 return Long.valueOf(value); 105 } 106 107 @Override 108 public String serialize(Context context, Object value) { 109 return value.toString(); 110 } 111 }; 112 113 public static final XValueFactory DOUBLE = new XValueFactory() { 114 @Override 115 public Object deserialize(Context context, String value) { 116 return Double.valueOf(value); 117 } 118 119 @Override 120 public String serialize(Context context, Object value) { 121 return value.toString(); 122 } 123 }; 124 125 public static final XValueFactory FLOAT = new XValueFactory() { 126 @Override 127 public Object deserialize(Context context, String value) { 128 return Float.valueOf(value); 129 } 130 131 @Override 132 public String serialize(Context context, Object value) { 133 return value.toString(); 134 } 135 }; 136 137 public static final XValueFactory BOOLEAN = new XValueFactory() { 138 @Override 139 public Object deserialize(Context context, String value) { 140 return Boolean.valueOf(value); 141 } 142 143 @Override 144 public String serialize(Context context, Object value) { 145 return value.toString(); 146 } 147 }; 148 149 public static final XValueFactory DATE = new XValueFactory() { 150 private final DateFormat df = DateFormat.getDateInstance(); 151 152 @Override 153 public Object deserialize(Context context, String value) { 154 try { 155 return df.parse(value); 156 } catch (ParseException e) { 157 return null; 158 } 159 } 160 161 @Override 162 public String serialize(Context context, Object value) { 163 Date date = (Date) value; 164 return df.format(date); 165 } 166 }; 167 168 public static final XValueFactory FILE = new XValueFactory() { 169 @Override 170 public Object deserialize(Context context, String value) { 171 return new File(value); 172 } 173 174 @Override 175 public String serialize(Context context, Object value) { 176 File file = (File) value; 177 return file.getName(); 178 } 179 }; 180 181 public static final XValueFactory URL = new XValueFactory() { 182 @Override 183 public Object deserialize(Context context, String value) { 184 try { 185 return new URL(value); 186 } catch (MalformedURLException e) { 187 return null; 188 } 189 } 190 191 @Override 192 public String serialize(Context context, Object value) { 193 return value.toString(); 194 } 195 }; 196 197 public static final XValueFactory CLASS = new XValueFactory() { 198 @Override 199 public Object deserialize(Context context, String value) { 200 try { 201 return context.loadClass(value); 202 } catch (ClassNotFoundException e) { 203 throw new XMapException("Cannot load class: " + value, e); 204 } 205 } 206 207 @Override 208 public String serialize(Context context, Object value) { 209 Class<?> clazz = (Class<?>) value; 210 return clazz.getName(); 211 } 212 }; 213 214 public static final XValueFactory RESOURCE = new XValueFactory() { 215 @Override 216 public Object deserialize(Context context, String value) { 217 return new Resource(context.getResource(value)); 218 } 219 220 @Override 221 public String serialize(Context context, Object value) { 222 return value.toString(); 223 } 224 }; 225 226 public static final Pattern DURATION_SIMPLE_FORMAT = Pattern.compile( 227 "(?:(\\d+)d)?(?:(\\d+)h)?(?:(\\d+)m)?(?:(\\d+)s)?(?:(\\d+)ms)?"); 228 229 public static final XValueFactory DURATION = new XValueFactory() { 230 231 @Override 232 public Object deserialize(Context context, String value) { 233 if (value.startsWith("P") || value.startsWith("-P")) { 234 // Duration JDK format 235 return Duration.parse(value); 236 } 237 Matcher matcher = DURATION_SIMPLE_FORMAT.matcher(value); 238 if (matcher.matches()) { 239 240 long days = 0; 241 long hours = 0; 242 long minutes = 0; 243 long seconds = 0; 244 long millis = 0; 245 if (matcher.group(1) != null) { 246 days = Long.parseLong(matcher.group(1)); 247 } 248 if (matcher.group(2) != null) { 249 hours = Long.parseLong(matcher.group(2)); 250 } 251 if (matcher.group(3) != null) { 252 minutes = Long.parseLong(matcher.group(3)); 253 } 254 if (matcher.group(4) != null) { 255 seconds = Long.parseLong(matcher.group(4)); 256 } 257 if (matcher.group(5) != null) { 258 millis = Long.parseLong(matcher.group(5)); 259 } 260 return Duration.ofDays(days).plusHours(hours).plusMinutes(minutes).plusSeconds(seconds).plusMillis( 261 millis); 262 } 263 throw new RuntimeException("Unable to read Duration=" + value); 264 } 265 266 @Override 267 public String serialize(Context context, Object value) { 268 // always use JDK format 269 return value.toString(); 270 } 271 }; 272 273 static { 274 addFactory(String.class, STRING); 275 addFactory(Integer.class, INTEGER); 276 addFactory(Long.class, LONG); 277 addFactory(Double.class, DOUBLE); 278 addFactory(Date.class, DATE); 279 addFactory(Boolean.class, BOOLEAN); 280 addFactory(File.class, FILE); 281 addFactory(URL.class, URL); 282 283 addFactory(int.class, INTEGER); 284 addFactory(long.class, LONG); 285 addFactory(double.class, DOUBLE); 286 addFactory(float.class, FLOAT); 287 addFactory(boolean.class, BOOLEAN); 288 289 addFactory(Class.class, CLASS); 290 addFactory(Resource.class, RESOURCE); 291 292 addFactory(Duration.class, DURATION); 293 } 294 295}