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