001/*
002 * (C) Copyright 2006-2007 Nuxeo SAS <http://nuxeo.com> and others
003 *
004 * All rights reserved. This program and the accompanying materials
005 * are made available under the terms of the Eclipse Public License v1.0
006 * which accompanies this distribution, and is available at
007 * http://www.eclipse.org/legal/epl-v10.html
008 *
009 * Contributors:
010 *     Jean-Marc Orliaguet, Chalmers
011 *
012 * $Id$
013 */
014
015package org.nuxeo.theme.properties;
016
017import java.io.IOException;
018import java.lang.reflect.Field;
019import java.lang.reflect.ParameterizedType;
020import java.lang.reflect.Type;
021import java.util.ArrayList;
022import java.util.Collection;
023import java.util.Enumeration;
024import java.util.List;
025import java.util.Properties;
026
027import org.nuxeo.theme.Utils;
028import org.nuxeo.theme.themes.ThemeIOException;
029
030public class FieldIO {
031
032    public static FieldInfo getFieldInfo(Class<?> c, String name) {
033        try {
034            return c.getField(name).getAnnotation(FieldInfo.class);
035        } catch (SecurityException e) {
036            return null;
037        } catch (NoSuchFieldException e) {
038            return null;
039        }
040    }
041
042    public static void updateFieldsFromProperties(Object object, Properties properties) throws ThemeIOException {
043        Enumeration<?> names = properties.propertyNames();
044
045        while (names.hasMoreElements()) {
046            String name = (String) names.nextElement();
047            String value = properties.getProperty(name);
048
049            Class<?> c = object.getClass();
050
051            FieldInfo fieldInfo = getFieldInfo(c, name);
052            if (fieldInfo == null) {
053                continue;
054            }
055
056            Field field;
057            try {
058                field = c.getField(name);
059            } catch (SecurityException e) {
060                throw new ThemeIOException(e);
061            } catch (NoSuchFieldException e) {
062                throw new ThemeIOException("Failed to set field '" + name + "' on " + c.getCanonicalName());
063            }
064
065            Class<?> fieldType = field.getType();
066            Type fieldGenericType = field.getGenericType();
067
068            // boolean fields
069            if (fieldType.equals(boolean.class) || fieldType.equals(Boolean.class)) {
070                try {
071                    field.set(object, Boolean.parseBoolean(value));
072                } catch (IllegalArgumentException e) {
073                    throw new ThemeIOException(e);
074                } catch (IllegalAccessException e) {
075                    throw new ThemeIOException(e);
076                }
077            }
078
079            // string fields
080            else if (fieldType.equals(String.class)) {
081                try {
082                    field.set(object, value);
083                } catch (IllegalArgumentException e) {
084                    throw new ThemeIOException(e);
085                } catch (IllegalAccessException e) {
086                    throw new ThemeIOException(e);
087                }
088            }
089
090            // integer fields
091            else if (fieldType.equals(int.class) || fieldType.equals(Integer.class)) {
092                try {
093                    if ("".equals(value)) {
094                        field.set(object, null);
095                    } else {
096                        field.set(object, Integer.valueOf(value));
097                    }
098                } catch (NumberFormatException e) {
099                    throw new ThemeIOException("Failed to parse integer value: '" + value + "'");
100                } catch (IllegalArgumentException e) {
101                    throw new ThemeIOException(e);
102                } catch (IllegalAccessException e) {
103                    throw new ThemeIOException(e);
104                }
105            }
106
107            // generics
108            else if (fieldGenericType instanceof ParameterizedType) {
109                if (fieldType.equals(ArrayList.class) || fieldType.equals(List.class)
110                        || fieldType.equals(Collection.class)) {
111
112                    Type[] actualTypes = ((ParameterizedType) fieldGenericType).getActualTypeArguments();
113
114                    if (actualTypes.length > 1) {
115                        throw new ThemeIOException("Only one-dimension arrays are supported.");
116                    }
117
118                    // Collection<String>
119                    if (actualTypes[0].equals(String.class)) {
120                        List<String> list = new ArrayList<String>();
121                        try {
122                            list = Utils.csvToList(value);
123                        } catch (IOException e) {
124                            throw new ThemeIOException(e);
125                        }
126                        try {
127                            field.set(object, list);
128                        } catch (IllegalArgumentException e) {
129                            throw new ThemeIOException(e);
130                        } catch (IllegalAccessException e) {
131                            throw new ThemeIOException(e);
132                        }
133                    }
134                }
135            } else {
136                throw new ThemeIOException("Cannot update field type '" + name + "' of " + c.getCanonicalName()
137                        + " because " + fieldType.getCanonicalName() + " is not supported.");
138            }
139        }
140
141    }
142
143    @SuppressWarnings("unchecked")
144    public static Properties dumpFieldsToProperties(Object object) throws ThemeIOException {
145
146        Properties properties = new Properties();
147
148        Class<?> c = object.getClass();
149        for (Field field : c.getDeclaredFields()) {
150            Class<?> fieldType = field.getType();
151            Type fieldGenericType = field.getGenericType();
152            String fieldName = field.getName();
153
154            FieldInfo fieldInfo = getFieldInfo(c, fieldName);
155            if (fieldInfo == null) {
156                continue;
157            }
158
159            String property = "";
160            Object value;
161            try {
162                value = field.get(object);
163            } catch (IllegalAccessException e) {
164                throw new ThemeIOException(e);
165            } catch (IllegalArgumentException e) {
166                throw new ThemeIOException(e);
167            }
168
169            // boolean fields
170            if (fieldType.equals(boolean.class) || fieldType.equals(Boolean.class)) {
171                if (value == null) {
172                    property = "false";
173                } else {
174                    property = Boolean.parseBoolean(value.toString()) ? "true" : "false";
175                }
176            }
177            // string fields
178            else if (fieldType.equals(String.class)) {
179                property = value == null ? "" : value.toString();
180            }
181            // integer fields
182            else if (fieldType.equals(int.class) || fieldType.equals(Integer.class)) {
183                property = value == null ? "" : value.toString();
184            }
185            // generics
186            else if (fieldGenericType instanceof ParameterizedType) {
187                if (fieldType.equals(ArrayList.class) || fieldType.equals(List.class)
188                        || fieldType.equals(Collection.class)) {
189
190                    Type[] actualTypes = ((ParameterizedType) fieldGenericType).getActualTypeArguments();
191
192                    if (actualTypes.length > 1) {
193                        throw new ThemeIOException("Only one-dimension arrays are supported.");
194                    }
195
196                    // Collection<String>
197                    if (actualTypes[0].equals(String.class)) {
198                        if (value == null) {
199                            property = "";
200                        } else {
201                            property = Utils.listToCsv((List<String>) (value));
202                        }
203                    } else {
204                        throw new ThemeIOException("Only list of strings are supported.");
205                    }
206
207                }
208            } else {
209                throw new ThemeIOException("Cannot extract property from field type '" + fieldName + "' of "
210                        + c.getCanonicalName() + " because " + fieldType.getCanonicalName() + " is not supported.");
211            }
212
213            properties.setProperty(fieldName, property);
214
215        }
216        return properties;
217    }
218}