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