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