001    /**
002     * Copyright (c) 2000-2013 Liferay, Inc. All rights reserved.
003     *
004     * This library is free software; you can redistribute it and/or modify it under
005     * the terms of the GNU Lesser General Public License as published by the Free
006     * Software Foundation; either version 2.1 of the License, or (at your option)
007     * any later version.
008     *
009     * This library is distributed in the hope that it will be useful, but WITHOUT
010     * ANY WARRANTY; without even the implied warranty of MERCHANTABILITY or FITNESS
011     * FOR A PARTICULAR PURPOSE. See the GNU Lesser General Public License for more
012     * details.
013     */
014    
015    package com.liferay.portal.json;
016    
017    import com.liferay.portal.kernel.log.Log;
018    import com.liferay.portal.kernel.log.LogFactoryUtil;
019    import com.liferay.portal.kernel.util.StringPool;
020    import com.liferay.portal.util.PropsValues;
021    
022    import flexjson.BeanAnalyzer;
023    import flexjson.BeanProperty;
024    import flexjson.JSONException;
025    import flexjson.ObjectBinder;
026    
027    import flexjson.factories.BeanObjectFactory;
028    
029    import java.lang.reflect.Field;
030    import java.lang.reflect.Modifier;
031    import java.lang.reflect.Type;
032    
033    import java.util.HashMap;
034    import java.util.Map;
035    import java.util.concurrent.ConcurrentHashMap;
036    
037    /**
038     * @author Michael C. Han
039     */
040    public class PortalBeanObjectFactory extends BeanObjectFactory {
041    
042            @Override
043            public Object instantiate(
044                    ObjectBinder objectBinder, Object value, Type targetType,
045                    @SuppressWarnings("rawtypes") Class targetClass) {
046    
047                    if (_safeMode) {
048                            Map<Object, Object> target = new HashMap<Object, Object>();
049    
050                            target.put("class", targetClass.getName());
051    
052                            Map<?, ?> values = (Map<?, ?>)value;
053    
054                            return objectBinder.bindIntoMap(values, target, null, null);
055                    }
056    
057                    String targetClassName = targetClass.getName();
058    
059                    if (targetClassName.contains("com.liferay") &&
060                            targetClassName.contains("Util")) {
061    
062                            throw new JSONException(
063                                    "Not instantiating " + targetClass.getName() + " at " +
064                                            objectBinder.getCurrentPath());
065                    }
066    
067                    try {
068                            Object target = instantiate(targetClass);
069    
070                            Map<?, ?> values = (Map<?, ?>)value;
071    
072                            if (PropsValues.JSON_DESERIALIZER_STRICT_MODE) {
073                                    removeInvalidFields(values, targetClass);
074                            }
075    
076                            return objectBinder.bindIntoObject(values, target, targetType);
077                    }
078                    catch (Exception e) {
079                            throw new JSONException(
080                                    "Unable to instantiate " + targetClass.getName() + " at " +
081                                            objectBinder.getCurrentPath(),
082                                    e);
083                    }
084            }
085    
086            public void setSafeMode(boolean safeMode) {
087                    _safeMode = safeMode;
088            }
089    
090            protected Map<String, Field> getDeclaredFields(
091                    @SuppressWarnings("rawtypes") Class targetClass) {
092    
093                    Map<String, Field> declaredFieldsMap = _declaredFields.get(targetClass);
094    
095                    if (declaredFieldsMap == null) {
096                            declaredFieldsMap = new ConcurrentHashMap<String, Field>();
097    
098                            Field[] declaredFields = targetClass.getDeclaredFields();
099    
100                            for (Field declaredField : declaredFields) {
101                                    String fieldName = declaredField.getName();
102    
103                                    if (fieldName.startsWith(StringPool.UNDERLINE)) {
104                                            fieldName = fieldName.substring(1);
105                                    }
106    
107                                    declaredFieldsMap.put(fieldName, declaredField);
108                            }
109    
110                            _declaredFields.put(targetClass, declaredFieldsMap);
111                    }
112    
113                    return declaredFieldsMap;
114            }
115    
116            protected boolean isValidField(
117                    Map<String, Field> declaredFields, String beanName) {
118    
119                    Field declaredField = declaredFields.get(beanName);
120    
121                    if (declaredField == null) {
122                            return false;
123                    }
124    
125                    int modifier = declaredField.getModifiers();
126    
127                    if (Modifier.isStatic(modifier)) {
128                            return false;
129                    }
130    
131                    return true;
132            }
133    
134            protected void removeInvalidFields(Map<?, ?> values, Class<?> targetClass) {
135                    Map<String, Field> declaredFields = getDeclaredFields(targetClass);
136    
137                    BeanAnalyzer beanAnalyzer = BeanAnalyzer.analyze(targetClass);
138    
139                    for (BeanProperty beanProperty : beanAnalyzer.getProperties()) {
140                            String beanName = beanProperty.getName();
141    
142                            String capitalizedBeanName = null;
143    
144                            Object beanValue = values.get(beanName);
145    
146                            if (beanValue == null) {
147                                    capitalizedBeanName = Character.toUpperCase(
148                                            beanName.charAt(0)) + beanName.substring(1);
149    
150                                    beanValue = values.get(capitalizedBeanName);
151                            }
152    
153                            if ((beanValue != null) &&
154                                    !isValidField(declaredFields, beanName)) {
155    
156                                    if (capitalizedBeanName != null) {
157                                            beanName = capitalizedBeanName;
158                                    }
159    
160                                    if (_log.isDebugEnabled()) {
161                                            _log.debug("Removing non-JavaBeans field " + beanName);
162                                    }
163    
164                                    values.remove(beanName);
165                            }
166                    }
167            }
168    
169            private static Log _log = LogFactoryUtil.getLog(
170                    PortalBeanObjectFactory.class);
171    
172            private Map<Class<?>, Map<String, Field>> _declaredFields =
173                    new ConcurrentHashMap<Class<?>, Map<String, Field>>();
174            private boolean _safeMode;
175    
176    }