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;
030
031import org.nuxeo.common.utils.DurationUtils;
032import org.w3c.dom.Node;
033
034/**
035 * Value factories are used to decode values from XML strings.
036 * <p>
037 * To register a new factory for a given XMap instance use the method
038 * {@link XMap#setValueFactory(Class, XValueFactory)}.
039 *
040 * @author <a href="mailto:bs@nuxeo.com">Bogdan Stefanescu</a>
041 */
042public abstract class XValueFactory {
043
044    static final Map<Class<?>, XValueFactory> defaultFactories = new Hashtable<>();
045
046    public abstract Object deserialize(Context context, String value);
047
048    public abstract String serialize(Context context, Object value);
049
050    public final Object getElementValue(Context context, Node element, boolean trim) {
051        String text = element.getTextContent();
052        return deserialize(context, trim ? text.trim() : text);
053    }
054
055    public final Object getAttributeValue(Context context, Node element, String name) {
056        Node at = element.getAttributes().getNamedItem(name);
057        return at != null ? deserialize(context, at.getNodeValue()) : null;
058    }
059
060    public static void addFactory(Class<?> klass, XValueFactory factory) {
061        defaultFactories.put(klass, factory);
062    }
063
064    public static XValueFactory getFactory(Class<?> type) {
065        return defaultFactories.get(type);
066    }
067
068    public static Object getValue(Context context, Class<?> klass, String value) {
069        XValueFactory factory = defaultFactories.get(klass);
070        if (factory == null) {
071            return null;
072        }
073        return factory.deserialize(context, value);
074    }
075
076    public static final XValueFactory STRING = new XValueFactory() {
077        @Override
078        public Object deserialize(Context context, String value) {
079            return value;
080        }
081
082        @Override
083        public String serialize(Context context, Object value) {
084            return value.toString();
085        }
086    };
087
088    public static final XValueFactory INTEGER = new XValueFactory() {
089        @Override
090        public Object deserialize(Context context, String value) {
091            return Integer.valueOf(value);
092        }
093
094        @Override
095        public String serialize(Context context, Object value) {
096            return value.toString();
097        }
098    };
099
100    public static final XValueFactory LONG = new XValueFactory() {
101        @Override
102        public Object deserialize(Context context, String value) {
103            return Long.valueOf(value);
104        }
105
106        @Override
107        public String serialize(Context context, Object value) {
108            return value.toString();
109        }
110    };
111
112    public static final XValueFactory DOUBLE = new XValueFactory() {
113        @Override
114        public Object deserialize(Context context, String value) {
115            return Double.valueOf(value);
116        }
117
118        @Override
119        public String serialize(Context context, Object value) {
120            return value.toString();
121        }
122    };
123
124    public static final XValueFactory FLOAT = new XValueFactory() {
125        @Override
126        public Object deserialize(Context context, String value) {
127            return Float.valueOf(value);
128        }
129
130        @Override
131        public String serialize(Context context, Object value) {
132            return value.toString();
133        }
134    };
135
136    public static final XValueFactory BOOLEAN = new XValueFactory() {
137        @Override
138        public Object deserialize(Context context, String value) {
139            return Boolean.valueOf(value);
140        }
141
142        @Override
143        public String serialize(Context context, Object value) {
144            return value.toString();
145        }
146    };
147
148    public static final XValueFactory DATE = new XValueFactory() {
149        private final DateFormat df = DateFormat.getDateInstance();
150
151        @Override
152        public Object deserialize(Context context, String value) {
153            try {
154                return df.parse(value);
155            } catch (ParseException e) {
156                return null;
157            }
158        }
159
160        @Override
161        public String serialize(Context context, Object value) {
162            Date date = (Date) value;
163            return df.format(date);
164        }
165    };
166
167    public static final XValueFactory FILE = new XValueFactory() {
168        @Override
169        public Object deserialize(Context context, String value) {
170            return new File(value);
171        }
172
173        @Override
174        public String serialize(Context context, Object value) {
175            File file = (File) value;
176            return file.getName();
177        }
178    };
179
180    public static final XValueFactory URL = new XValueFactory() {
181        @Override
182        public Object deserialize(Context context, String value) {
183            try {
184                return new URL(value);
185            } catch (MalformedURLException e) {
186                return null;
187            }
188        }
189
190        @Override
191        public String serialize(Context context, Object value) {
192            return value.toString();
193        }
194    };
195
196    public static final XValueFactory CLASS = new XValueFactory() {
197        @Override
198        public Object deserialize(Context context, String value) {
199            try {
200                return context.loadClass(value);
201            } catch (ClassNotFoundException e) {
202                throw new XMapException("Cannot load class: " + value, e);
203            }
204        }
205
206        @Override
207        public String serialize(Context context, Object value) {
208            Class<?> clazz = (Class<?>) value;
209            return clazz.getName();
210        }
211    };
212
213    public static final XValueFactory RESOURCE = new XValueFactory() {
214        @Override
215        public Object deserialize(Context context, String value) {
216            return new Resource(context.getResource(value));
217        }
218
219        @Override
220        public String serialize(Context context, Object value) {
221            return value.toString();
222        }
223    };
224
225    public static final XValueFactory DURATION = new XValueFactory() {
226
227        @Override
228        public Object deserialize(Context context, String value) {
229            return DurationUtils.parse(value);
230        }
231
232        @Override
233        public String serialize(Context context, Object value) {
234            // always use JDK format
235            return value.toString();
236        }
237    };
238
239    static {
240        addFactory(String.class, STRING);
241        addFactory(Integer.class, INTEGER);
242        addFactory(Long.class, LONG);
243        addFactory(Double.class, DOUBLE);
244        addFactory(Date.class, DATE);
245        addFactory(Boolean.class, BOOLEAN);
246        addFactory(File.class, FILE);
247        addFactory(URL.class, URL);
248
249        addFactory(int.class, INTEGER);
250        addFactory(long.class, LONG);
251        addFactory(double.class, DOUBLE);
252        addFactory(float.class, FLOAT);
253        addFactory(boolean.class, BOOLEAN);
254
255        addFactory(Class.class, CLASS);
256        addFactory(Resource.class, RESOURCE);
257
258        addFactory(Duration.class, DURATION);
259    }
260
261}