001/*
002 * (C) Copyright 2006-2008 Nuxeo SA (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 *     bstefanescu
018 */
019package org.nuxeo.ecm.webengine.forms.validation;
020
021import java.lang.reflect.Array;
022import java.util.Calendar;
023import java.util.Date;
024import java.util.TimeZone;
025import java.util.regex.Matcher;
026import java.util.regex.Pattern;
027
028import org.nuxeo.ecm.webengine.WebEngine;
029import org.nuxeo.runtime.api.Framework;
030
031/**
032 * @author <a href="mailto:bs@nuxeo.com">Bogdan Stefanescu</a>
033 */
034public abstract class TypeConvertor<T> {
035
036    public abstract Class<?> getType();
037
038    public abstract T convert(String value) throws ValidationException;
039
040    public Object[] newArray(int length) {
041        return (Object[]) Array.newInstance(getType(), length);
042    }
043
044    @SuppressWarnings("unchecked")
045    public static <T> TypeConvertor<T> getConvertor(Class<T> type) {
046        if (type == String.class) {
047            return null;
048        }
049        TypeConvertor<?> result = null;
050        if (type == Boolean.class) {
051            result = BOOLEAN;
052        } else if (type == Date.class) {
053            result = DATE;
054        } else if (type == Integer.class) {
055            result = INTEGER;
056        } else if (type == Float.class) {
057            result = FLOAT;
058        } else if (type == Long.class) {
059            result = LONG;
060        } else if (type == Double.class) {
061            result = DOUBLE;
062        } else if (type == Class.class) {
063            result = CLASS;
064        } else {
065            throw new IllegalArgumentException("Unsupported type: " + type);
066        }
067        return (TypeConvertor<T>) result;
068    }
069
070    public static final TypeConvertor<Boolean> BOOLEAN = new TypeConvertor<Boolean>() {
071        @Override
072        public Class<?> getType() {
073            return Boolean.class;
074        }
075
076        @Override
077        public Boolean convert(String value) throws ValidationException {
078            if ("true".equals(value)) {
079                return Boolean.TRUE;
080            } else if ("false".equals(value)) {
081                return Boolean.FALSE;
082            } else {
083                throw new ValidationException();
084            }
085        }
086    };
087
088    public static final TypeConvertor<Integer> INTEGER = new TypeConvertor<Integer>() {
089        @Override
090        public Class<?> getType() {
091            return Integer.class;
092        }
093
094        @Override
095        public Integer convert(String value) throws ValidationException {
096            try {
097                return Integer.valueOf(value);
098            } catch (NumberFormatException e) {
099                throw new ValidationException();
100            }
101        }
102    };
103
104    public static final TypeConvertor<Long> LONG = new TypeConvertor<Long>() {
105        @Override
106        public Class<?> getType() {
107            return Long.class;
108        }
109
110        @Override
111        public Long convert(String value) throws ValidationException {
112            try {
113                return Long.valueOf(value);
114            } catch (NumberFormatException e) {
115                throw new ValidationException();
116            }
117        }
118    };
119
120    public static final TypeConvertor<Float> FLOAT = new TypeConvertor<Float>() {
121        @Override
122        public Class<?> getType() {
123            return Float.class;
124        }
125
126        @Override
127        public Float convert(String value) throws ValidationException {
128            try {
129                return Float.valueOf(value);
130            } catch (NumberFormatException e) {
131                throw new ValidationException();
132            }
133        }
134    };
135
136    public static final TypeConvertor<Double> DOUBLE = new TypeConvertor<Double>() {
137        @Override
138        public Class<?> getType() {
139            return Double.class;
140        }
141
142        @Override
143        public Double convert(String value) throws ValidationException {
144            try {
145                return Double.valueOf(value);
146            } catch (NumberFormatException e) {
147                throw new ValidationException();
148            }
149        }
150    };
151
152    public static final TypeConvertor<Date> DATE = new TypeConvertor<Date>() {
153        @Override
154        public Class<?> getType() {
155            return Date.class;
156        }
157
158        @Override
159        public Date convert(String value) throws ValidationException {
160            try {
161                return parseDate(value);
162            } catch (IllegalArgumentException e) {
163                throw new ValidationException();
164            }
165        }
166    };
167
168    public static final TypeConvertor<Class<?>> CLASS = new TypeConvertor<Class<?>>() {
169        @Override
170        public Class<?> getType() {
171            return Class.class;
172        }
173
174        @Override
175        public Class<?> convert(String value) throws ValidationException {
176            try {
177                return loadClass(value);
178            } catch (ReflectiveOperationException e) {
179                throw new ValidationException();
180            }
181        }
182    };
183
184    public static Class<?> loadClass(String name) throws ReflectiveOperationException {
185        return Framework.getLocalService(WebEngine.class).loadClass(name);
186    }
187
188    private static final Pattern PATTERN = Pattern.compile("(\\d{4})(?:-(\\d{2}))?(?:-(\\d{2}))?(?:[Tt](?:(\\d{2}))?(?::(\\d{2}))?(?::(\\d{2}))?(?:\\.(\\d{3}))?)?([Zz])?(?:([+-])(\\d{2}):(\\d{2}))?");
189
190    /**
191     * Parse the serialized string form into a java.util.Date
192     *
193     * @param date The serialized string form of the date
194     * @return The created java.util.Date
195     */
196    public static Date parseDate(String date) {
197        Matcher m = PATTERN.matcher(date);
198        if (m.find()) {
199            Calendar c = Calendar.getInstance(TimeZone.getTimeZone("GMT"));
200            int hoff = 0, moff = 0, doff = -1;
201            if (m.group(9) != null) {
202                doff = m.group(9).equals("-") ? 1 : -1;
203                hoff = doff * (m.group(10) != null ? Integer.parseInt(m.group(10)) : 0);
204                moff = doff * (m.group(11) != null ? Integer.parseInt(m.group(11)) : 0);
205            }
206            c.set(Calendar.YEAR, Integer.parseInt(m.group(1)));
207            c.set(Calendar.MONTH, m.group(2) != null ? Integer.parseInt(m.group(2)) - 1 : 0);
208            c.set(Calendar.DATE, m.group(3) != null ? Integer.parseInt(m.group(3)) : 1);
209            c.set(Calendar.HOUR_OF_DAY, m.group(4) != null ? Integer.parseInt(m.group(4)) + hoff : 0);
210            c.set(Calendar.MINUTE, m.group(5) != null ? Integer.parseInt(m.group(5)) + moff : 0);
211            c.set(Calendar.SECOND, m.group(6) != null ? Integer.parseInt(m.group(6)) : 0);
212            c.set(Calendar.MILLISECOND, m.group(7) != null ? Integer.parseInt(m.group(7)) : 0);
213            return c.getTime();
214        } else {
215            throw new IllegalArgumentException("Invalid Date Format");
216        }
217    }
218
219}