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