001    /**
002     * Copyright (c) 2000-2012 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.jabsorb.serializer;
016    
017    import com.liferay.portal.kernel.log.Log;
018    import com.liferay.portal.kernel.log.LogFactoryUtil;
019    import com.liferay.portal.kernel.util.ArrayUtil;
020    
021    import java.io.Serializable;
022    
023    import java.lang.reflect.Constructor;
024    import java.lang.reflect.Field;
025    import java.lang.reflect.Modifier;
026    
027    import java.util.HashSet;
028    import java.util.Iterator;
029    import java.util.Set;
030    
031    import org.jabsorb.JSONSerializer;
032    import org.jabsorb.serializer.AbstractSerializer;
033    import org.jabsorb.serializer.MarshallException;
034    import org.jabsorb.serializer.ObjectMatch;
035    import org.jabsorb.serializer.SerializerState;
036    import org.jabsorb.serializer.UnmarshallException;
037    
038    import org.json.JSONObject;
039    
040    /**
041     * @author Raymond Augé
042     */
043    public class LiferaySerializer extends AbstractSerializer {
044    
045            @Override
046            public boolean canSerialize(
047                    @SuppressWarnings("rawtypes") Class clazz,
048                    @SuppressWarnings("rawtypes") Class jsonClass) {
049    
050                    Constructor<?> constructor = null;
051    
052                    try {
053                            constructor = clazz.getConstructor();
054                    }
055                    catch (Exception e) {
056                    }
057    
058                    if (Serializable.class.isAssignableFrom(clazz) &&
059                            ((jsonClass == null) || (jsonClass == JSONObject.class)) &&
060                            (constructor != null)) {
061    
062                            return true;
063                    }
064    
065                    return false;
066            }
067    
068            public Class<?>[] getJSONClasses() {
069                    return _JSON_CLASSES;
070            }
071    
072            public Class<?>[] getSerializableClasses() {
073                    return _SERIALIZABLE_CLASSES;
074            }
075    
076            public Object marshall(
077                            SerializerState serializerState, Object parentObject, Object object)
078                    throws MarshallException {
079    
080                    JSONObject jsonObject = new JSONObject();
081    
082                    Class<?> javaClass = object.getClass();
083    
084                    if (ser.getMarshallClassHints()) {
085                            try {
086                                    jsonObject.put("javaClass", javaClass.getName());
087                            }
088                            catch (Exception e) {
089                                    throw new MarshallException("Unable to put javaClass", e);
090                            }
091                    }
092    
093                    JSONObject serializableJSONObject = new JSONObject();
094    
095                    try {
096                            jsonObject.put("serializable", serializableJSONObject);
097    
098                            serializerState.push(
099                                    object, serializableJSONObject, "serializable");
100                    }
101                    catch (Exception e) {
102                            throw new MarshallException("Unable to put serializable", e);
103                    }
104    
105                    String fieldName = null;
106    
107                    try {
108                            Set<String> processedFieldNames = new HashSet<String>();
109    
110                            while (javaClass != null) {
111                                    Field[] declaredFields = javaClass.getDeclaredFields();
112    
113                                    for (Field field : declaredFields) {
114                                            fieldName = field.getName();
115    
116                                            // Avoid processing overridden fields of super classes
117    
118                                            if (processedFieldNames.contains(fieldName)) {
119                                                    continue;
120                                            }
121    
122                                            processedFieldNames.add(fieldName);
123    
124                                            int modifiers = field.getModifiers();
125    
126                                            // Only marshall fields that are not final, static, or
127                                            // transient
128    
129                                            if (((modifiers & Modifier.FINAL) == Modifier.FINAL) ||
130                                                    ((modifiers & Modifier.STATIC) == Modifier.STATIC) ||
131                                                    ((modifiers & Modifier.TRANSIENT) ==
132                                                            Modifier.TRANSIENT)) {
133    
134                                                    continue;
135                                            }
136    
137                                            if (!field.isAccessible()) {
138                                                    field.setAccessible(true);
139                                            }
140    
141                                            if (fieldName.startsWith("_")) {
142                                                    fieldName = fieldName.substring(1);
143                                            }
144    
145                                            Object fieldObject = ser.marshall(
146                                                    serializerState, serializableJSONObject,
147                                                    field.get(object), fieldName);
148    
149                                            // Omit the object entirely if it is a circular reference or
150                                            // duplicate. It will be regenerated in the fixups phase.
151    
152                                            if (JSONSerializer.CIRC_REF_OR_DUPLICATE != fieldObject) {
153                                                    serializableJSONObject.put(fieldName, fieldObject);
154                                            }
155                                    }
156    
157                                    javaClass = javaClass.getSuperclass();
158                            }
159                    }
160                    catch (Exception e) {
161                            throw new MarshallException(
162                                    "Unable to match field " + fieldName, e);
163                    }
164                    finally {
165                            serializerState.pop();
166                    }
167    
168                    return jsonObject;
169            }
170    
171            public ObjectMatch tryUnmarshall(
172                            SerializerState serializerState,
173                            @SuppressWarnings("rawtypes") Class clazz, Object object)
174                    throws UnmarshallException {
175    
176                    JSONObject jsonObject = (JSONObject)object;
177    
178                    String javaClassName = null;
179    
180                    try {
181                            javaClassName = jsonObject.getString("javaClass");
182                    }
183                    catch (Exception e) {
184                            throw new UnmarshallException("Unable to get javaClass", e);
185                    }
186    
187                    if (javaClassName == null) {
188                            throw new UnmarshallException("javaClass is undefined");
189                    }
190    
191                    try {
192                            Class<?> javaClass = Class.forName(javaClassName);
193    
194                            Serializable.class.isAssignableFrom(javaClass);
195                    }
196                    catch (Exception e) {
197                            throw new UnmarshallException(
198                                    "Unable to load javaClass " + javaClassName, e);
199                    }
200    
201                    JSONObject serializableJSONObject = null;
202    
203                    try {
204                            serializableJSONObject = jsonObject.getJSONObject("serializable");
205                    }
206                    catch (Exception e) {
207                            throw new UnmarshallException("Unable to get serializable", e);
208                    }
209    
210                    if (serializableJSONObject == null) {
211                            throw new UnmarshallException("serializable is undefined");
212                    }
213    
214                    ObjectMatch objectMatch = new ObjectMatch(-1);
215    
216                    serializerState.setSerialized(object, objectMatch);
217    
218                    String fieldName = null;
219    
220                    try {
221                            Iterator<?> iterator = serializableJSONObject.keys();
222    
223                            while (iterator.hasNext()) {
224                                    fieldName = (String)iterator.next();
225    
226                                    ObjectMatch fieldObjectMatch = ser.tryUnmarshall(
227                                            serializerState, null,
228                                            serializableJSONObject.get(fieldName));
229    
230                                    ObjectMatch maxFieldObjectMatch = fieldObjectMatch.max(
231                                            objectMatch);
232    
233                                    objectMatch.setMismatch(maxFieldObjectMatch.getMismatch());
234                            }
235                    }
236                    catch (Exception e) {
237                            throw new UnmarshallException(
238                                    "Unable to match field " + fieldName, e);
239                    }
240    
241                    return objectMatch;
242            }
243    
244            public Object unmarshall(
245                            SerializerState serializerState,
246                            @SuppressWarnings("rawtypes") Class clazz, Object object)
247                    throws UnmarshallException {
248    
249                    JSONObject jsonObject = (JSONObject)object;
250    
251                    String javaClassName = null;
252    
253                    try {
254                            javaClassName = jsonObject.getString("javaClass");
255                    }
256                    catch (Exception e) {
257                            throw new UnmarshallException("Unable to get javaClass", e);
258                    }
259    
260                    if (javaClassName == null) {
261                            throw new UnmarshallException("javaClass is undefined");
262                    }
263    
264                    Class<?> javaClass = null;
265    
266                    Object javaClassInstance = null;
267    
268                    try {
269                            javaClass = Class.forName(javaClassName);
270    
271                            javaClassInstance = javaClass.newInstance();
272                    }
273                    catch (Exception e) {
274                            throw new UnmarshallException(
275                                    "Unable to load javaClass " + javaClassName, e);
276                    }
277    
278                    JSONObject serializableJSONObject = null;
279    
280                    try {
281                            serializableJSONObject = jsonObject.getJSONObject("serializable");
282                    }
283                    catch (Exception e) {
284                            throw new UnmarshallException("Unable to get serializable", e);
285                    }
286    
287                    if (serializableJSONObject == null) {
288                            throw new UnmarshallException("serializable is undefined");
289                    }
290    
291                    serializerState.setSerialized(object, javaClassInstance);
292    
293                    String fieldName = null;
294    
295                    try {
296                            Set<String> processedFieldNames = new HashSet<String>();
297    
298                            while (javaClass != null) {
299                                    Field[] fields = javaClass.getDeclaredFields();
300    
301                                    for (Field field : fields) {
302                                            fieldName = field.getName();
303    
304                                            // Avoid processing overridden fields of super classes
305    
306                                            if (processedFieldNames.contains(fieldName)) {
307                                                    continue;
308                                            }
309    
310                                            processedFieldNames.add(fieldName);
311    
312                                            int modifiers = field.getModifiers();
313    
314                                            // Only unmarshall fields that are not final, static, or
315                                            // transient
316    
317                                            if (((modifiers & Modifier.FINAL) == Modifier.FINAL) ||
318                                                    ((modifiers & Modifier.STATIC) == Modifier.STATIC) ||
319                                                    ((modifiers & Modifier.TRANSIENT) ==
320                                                            Modifier.TRANSIENT)) {
321    
322                                                    continue;
323                                            }
324    
325                                            if (!field.isAccessible()) {
326                                                    field.setAccessible(true);
327                                            }
328    
329                                            if (fieldName.startsWith("_")) {
330                                                    fieldName = fieldName.substring(1);
331                                            }
332    
333                                            Object value = null;
334    
335                                            try {
336                                                    value = ser.unmarshall(
337                                                            serializerState, null,
338                                                            serializableJSONObject.get(fieldName));
339                                            }
340                                            catch (Exception e) {
341                                            }
342    
343                                            if (value != null) {
344                                                    try {
345                                                            value = getValue(field, value);
346    
347                                                            field.set(javaClassInstance, value);
348                                                    }
349                                                    catch (Exception e) {
350                                                            _log.error(e, e);
351                                                    }
352                                            }
353                                    }
354    
355                                    javaClass = javaClass.getSuperclass();
356                            }
357                    }
358                    catch (Exception e) {
359                            throw new UnmarshallException(
360                                    "Unable to match field " + fieldName, e);
361                    }
362    
363                    return javaClassInstance;
364            }
365    
366            protected Object getValue(Field field, Object value) {
367                    Class<?> type = field.getType();
368    
369                    if (!type.isArray()) {
370                            return value;
371                    }
372    
373                    Class<?> componentType = type.getComponentType();
374    
375                    if (!componentType.isPrimitive()) {
376                            return value;
377                    }
378    
379                    if (type.isAssignableFrom(boolean[].class)) {
380                            value = ArrayUtil.toArray((Boolean[])value);
381                    }
382                    else if (type.isAssignableFrom(byte[].class)) {
383                            value = ArrayUtil.toArray((Byte[])value);
384                    }
385                    else if (type.isAssignableFrom(char[].class)) {
386                            value = ArrayUtil.toArray((Character[])value);
387                    }
388                    else if (type.isAssignableFrom(double[].class)) {
389                            value = ArrayUtil.toArray((Double[])value);
390                    }
391                    else if (type.isAssignableFrom(float[].class)) {
392                            value = ArrayUtil.toArray((Float[])value);
393                    }
394                    else if (type.isAssignableFrom(int[].class)) {
395                            value = ArrayUtil.toArray((Integer[])value);
396                    }
397                    else if (type.isAssignableFrom(long[].class)) {
398                            value = ArrayUtil.toArray((Long[])value);
399                    }
400                    else if (type.isAssignableFrom(short[].class)) {
401                            value = ArrayUtil.toArray((Short[])value);
402                    }
403    
404                    return value;
405            }
406    
407            private static final Class<?>[] _JSON_CLASSES = {JSONObject.class};
408    
409            private static final Class<?>[] _SERIALIZABLE_CLASSES =
410                    {Serializable.class};
411    
412            private static Log _log = LogFactoryUtil.getLog(LiferaySerializer.class);
413    
414    }