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.InvocationHandler;
020import java.lang.reflect.InvocationTargetException;
021import java.lang.reflect.Method;
022import java.lang.reflect.Proxy;
023import java.util.ArrayList;
024import java.util.Collection;
025import java.util.HashMap;
026import java.util.Hashtable;
027import java.util.List;
028import java.util.Map;
029import java.util.Set;
030
031import org.nuxeo.ecm.webengine.WebEngine;
032import org.nuxeo.ecm.webengine.forms.FormDataProvider;
033import org.nuxeo.runtime.api.Framework;
034
035/**
036 * @author <a href="mailto:bs@nuxeo.com">Bogdan Stefanescu</a>
037 */
038public class FormManager implements InvocationHandler, Form {
039
040    protected Map<String, Object> map;
041
042    protected Map<String, String[]> fields;
043
044    protected List<String> unknownKeys;
045
046    protected FormDescriptor fd;
047
048    public FormManager(FormDescriptor fd) {
049        this.fd = fd;
050        unknownKeys = new ArrayList<String>();
051        map = new HashMap<String, Object>(); // remove any previous data
052        fields = new HashMap<String, String[]>(); // remove any previous data
053        // TODO when implementing file upload - remove here any previously created file
054    }
055
056    public Collection<String> unknownKeys() {
057        return unknownKeys;
058    }
059
060    public Map<String, String[]> fields() {
061        return fields;
062    }
063
064    @SuppressWarnings("unchecked")
065    public void load(FormDataProvider data, Form proxy) throws ValidationException {
066        ValidationException ve = null;
067        Set<String> reqs = (Set<String>) fd.requiredFields.clone();
068        for (String key : data.getKeys()) {
069            String[] values = data.getList(key);
070            if (values != null) {
071                int k = 0;
072                for (String v : values) {
073                    if (v.length() == 0) {
074                        k++;
075                    }
076                }
077                if (k == values.length) {
078                    values = null;
079                }
080            }
081            if (values != null) {
082                fields.put(key, values);
083                reqs.remove(key);
084            }
085            FormDescriptor.Field f = fd.fields.get(key);
086            if (f != null) {
087                Object o = null;
088                try {
089                    if (f.isArray) {
090                        if (values != null) {
091                            o = f.validateArray(values);
092                        }
093                    } else {
094                        String v = values != null && values.length > 0 ? values[0] : null;
095                        if (v != null && v.length() > 0) {
096
097                            o = f.validate(v);
098                        }
099                    }
100                } catch (ValidationException e) {
101                    if (ve == null) {
102                        ve = e;
103                    }
104                    ve.addInvalidField(key);
105                }
106                map.put(key, o);
107            } else {
108                unknownKeys.add(key);
109            }
110        }
111        if (!reqs.isEmpty()) {
112            if (ve == null) {
113                ve = new ValidationException();
114            }
115            for (String req : reqs) {
116                ve.addRequiredField(req);
117            }
118        }
119        if (ve != null) {
120            throw ve;
121        }
122        if (fd.validator != null) {
123            fd.validator.validate(data, proxy);
124        }
125    }
126
127    public Object invoke(Object proxy, Method method, Object[] args) throws Throwable {
128        if (method.getDeclaringClass() == Form.class) {
129            try {
130                return method.invoke(this, args);
131            } catch (InvocationTargetException e) {
132                throw e.getCause();
133            }
134        } else {
135            String name = method.getName();
136            int len = name.length();
137            if (len > 3) {
138                if (name.startsWith("get")) {
139                    name = FormDescriptor.getFieldName(name, len);
140                    return map.get(name);
141                }
142            }
143        }
144        throw new UnsupportedOperationException("Method unsupported: " + method);
145    }
146
147    protected static Map<Class<?>, FormDescriptor> forms = new Hashtable<Class<?>, FormDescriptor>();
148
149    @SuppressWarnings("unchecked")
150    public static <T> T newProxy(Class<T> type) {
151        WebEngine we = Framework.getService(WebEngine.class);
152        ClassLoader cl = we != null ? we.getWebLoader().getClassLoader() : FormManager.class.getClassLoader();
153        return (T) Proxy.newProxyInstance(cl, new Class<?>[] { type }, new FormManager(getDescriptor(type)));
154    }
155
156    public void flushCache() {
157        forms = new Hashtable<Class<?>, FormDescriptor>();
158    }
159
160    static FormDescriptor getDescriptor(Class<?> type) {
161        FormDescriptor fd = forms.get(type);
162        if (fd == null) {
163            try {
164                fd = new FormDescriptor(type);
165                forms.put(type, fd);
166            } catch (ReflectiveOperationException e) {
167                throw new RuntimeException("Failed to build form descriptor", e);
168            }
169        }
170        return fd;
171    }
172
173}