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}