001/*
002 * (C) Copyright 2006-2007 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 *     Nuxeo - initial API and implementation
018 *
019 * $Id: FieldAdapterManager.java 28460 2008-01-03 15:34:05Z sfermigier $
020 */
021
022package org.nuxeo.ecm.platform.el;
023
024import java.lang.reflect.Array;
025import java.math.BigDecimal;
026import java.util.Calendar;
027import java.util.Date;
028import java.util.HashMap;
029import java.util.List;
030import java.util.Map;
031
032import org.apache.commons.logging.Log;
033import org.apache.commons.logging.LogFactory;
034
035/**
036 * The FieldAdapterManager fills the gap between the storage and the display structures.
037 * <p>
038 * The Display representation of a DataModel is a set of JSF Beans There are mainly 3 cases:
039 * <p>
040 * 1 - Perfect match: the JSF components generate a bean that can be directly stored ie: String ...
041 * <p>
042 * 2 - Type Mismatch: The JSF component generate a bean that is not of the right type ie: The JSF generate a Date
043 * whereas the Core expect a Calendar type.
044 * <p>
045 * 3 - Structure Mismatch: The JSF bean must be split in several fields ie: The uploaded file is one object, but the
046 * core expect at least 2 separate fields (filename and content)
047 *
048 * @author Thierry Delprat
049 */
050public final class FieldAdapterManager {
051
052    @SuppressWarnings("unused")
053    private static final Log log = LogFactory.getLog(FieldAdapterManager.class);
054
055    // Utility class.
056    private FieldAdapterManager() {
057    }
058
059    /**
060     * Sets value adapting it for storage.
061     */
062    @SuppressWarnings({ "unchecked", "rawtypes" })
063    public static Object getValueForStorage(Object value) {
064        if (value instanceof Date) {
065            value = getDateAsCalendar((Date) value);
066        } else if (value instanceof BigDecimal) {
067            value = getBigDecimalAsLong((BigDecimal) value);
068        } else if (value instanceof Object[]) {
069            Object[] array = (Object[]) value;
070            Class<?> oldType = array.getClass().getComponentType();
071            Class<?> newType = getComponentTypeForStorage(oldType);
072            Object[] newArray = (Object[]) Array.newInstance(newType, array.length);
073            for (int i = 0; i < array.length; i++) {
074                newArray[i] = getValueForStorage(array[i]);
075            }
076            value = newArray;
077        } else if (value instanceof List) {
078            List list = (List) value;
079            for (int i = 0; i < list.size(); i++) {
080                list.set(i, getValueForStorage(list.get(i)));
081            }
082        } else if (value instanceof Map) {
083            Map<Object, Object> map = (Map) value;
084            Map<Object, Object> newMap = new HashMap<Object, Object>();
085            for (Map.Entry<Object, Object> entry : map.entrySet()) {
086                newMap.put(entry.getKey(), getValueForStorage(entry.getValue()));
087            }
088            value = newMap;
089        }
090        // TODO: maybe handle list diffs (?)
091        return value;
092    }
093
094    /**
095     * Returns component type that will be used to store objects of given component type.
096     */
097    public static Class<?> getComponentTypeForStorage(Class<?> componentType) {
098        Class<?> newType = componentType;
099        if (componentType.equals(Date.class)) {
100            newType = Calendar.class;
101        }
102        return newType;
103    }
104
105    /**
106     * Gets value adapting it for display.
107     */
108    @SuppressWarnings({ "unchecked", "rawtypes" })
109    public static Object getValueForDisplay(Object value) {
110        if (value instanceof Calendar) {
111            value = getCalendarAsDate((Calendar) value);
112        } else if (value instanceof Object[]) {
113            Object[] array = (Object[]) value;
114            Class<?> oldType = array.getClass().getComponentType();
115            Class<?> newType = getComponentTypeForDisplay(oldType);
116            Object[] newArray = (Object[]) Array.newInstance(newType, array.length);
117            for (int i = 0; i < array.length; i++) {
118                newArray[i] = getValueForDisplay(array[i]);
119            }
120            value = newArray;
121        } else if (value instanceof List) {
122            List list = (List) value;
123            for (int i = 0; i < list.size(); i++) {
124                list.set(i, getValueForDisplay(list.get(i)));
125            }
126        } else if (value instanceof Map) {
127            Map<Object, Object> map = (Map) value;
128            Map<Object, Object> newMap = new HashMap<Object, Object>();
129            for (Map.Entry<Object, Object> entry : map.entrySet()) {
130                newMap.put(entry.getKey(), getValueForDisplay(entry.getValue()));
131            }
132            value = newMap;
133        }
134        return value;
135    }
136
137    /**
138     * Returns component type that will be used to display objects of given component type.
139     */
140    public static Class<?> getComponentTypeForDisplay(Class<?> componentType) {
141        Class<?> newType = componentType;
142        if (componentType.equals(Calendar.class)) {
143            newType = Date.class;
144        }
145        return newType;
146    }
147
148    // Fake converters for now
149    // XXX make an extension point to register Adapters
150    // XXX update TypeManager to handle Adapter configuration
151
152    private static Calendar getDateAsCalendar(Date value) {
153        Calendar calValue = Calendar.getInstance();
154        calValue.setTime(value);
155        return calValue;
156    }
157
158    private static Date getCalendarAsDate(Calendar value) {
159        return value.getTime();
160    }
161
162    /**
163     * @since 7.1
164     */
165    private static Long getBigDecimalAsLong(BigDecimal value) {
166        return value.longValueExact();
167    }
168
169}