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