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 if (Calendar.class.isAssignableFrom(componentType)) { 142 return Date.class; 143 } 144 return componentType; 145 } 146 147 // Fake converters for now 148 // XXX make an extension point to register Adapters 149 // XXX update TypeManager to handle Adapter configuration 150 151 private static Calendar getDateAsCalendar(Date value) { 152 Calendar calValue = Calendar.getInstance(); 153 calValue.setTime(value); 154 return calValue; 155 } 156 157 private static Date getCalendarAsDate(Calendar value) { 158 return value.getTime(); 159 } 160 161 /** 162 * @since 7.1 163 */ 164 private static Long getBigDecimalAsLong(BigDecimal value) { 165 return value.longValueExact(); 166 } 167 168}