View Javadoc

1   /*
2    * Licensed to the Apache Software Foundation (ASF) under one
3    * or more contributor license agreements. See the NOTICE file
4    * distributed with this work for additional information
5    * regarding copyright ownership. The ASF licenses this file
6    * to you under the Apache License, Version 2.0 (the
7    * "License"); you may not use this file except in compliance
8    * with the License. You may obtain a copy of the License at
9    *
10   *     http://www.apache.org/licenses/LICENSE-2.0
11   *
12   * Unless required by applicable law or agreed to in writing,
13   * software distributed under the License is distributed on an
14   * "AS IS" BASIS, WITHOUT WARRANTIES OR CONDITIONS OF ANY
15   * KIND, either express or implied. See the License for the
16   * specific language governing permissions and limitations under the License.
17   */
18  package org.apache.shindig.social.core.util.xstream;
19  
20  import com.google.inject.Injector;
21  
22  import com.thoughtworks.xstream.converters.ConversionException;
23  import com.thoughtworks.xstream.converters.Converter;
24  import com.thoughtworks.xstream.converters.MarshallingContext;
25  import com.thoughtworks.xstream.converters.UnmarshallingContext;
26  import com.thoughtworks.xstream.io.ExtendedHierarchicalStreamWriterHelper;
27  import com.thoughtworks.xstream.io.HierarchicalStreamReader;
28  import com.thoughtworks.xstream.io.HierarchicalStreamWriter;
29  import com.thoughtworks.xstream.mapper.Mapper;
30  
31  import org.apache.shindig.social.opensocial.model.Exportablebean;
32  
33  import java.util.Collection;
34  import java.util.Iterator;
35  
36  /***
37   * 
38   */
39  public class GuiceBeanConverter implements Converter {
40    private Mapper mapper;
41    private GuiceBeanProvider beanProvider;
42  
43    public GuiceBeanConverter(Mapper mapper, Injector injector) {
44      this(mapper, new GuiceBeanProvider(injector));
45    }
46  
47    public GuiceBeanConverter(Mapper mapper, GuiceBeanProvider beanProvider) {
48      this.mapper = mapper;
49      this.beanProvider = beanProvider;
50    }
51  
52    /***
53     * Only checks for the availability of a public default constructor. If you
54     * need stricter checks, subclass JavaBeanConverter
55     */
56    public boolean canConvert(Class type) {
57      if ( type == null ) {
58        return false;
59      }
60      if (Object.class.equals(type)) {
61        return false;
62      }
63      if ( type.isInterface() ) {
64        return true;
65      }
66      for (Class<?> iff : type.getInterfaces()) {
67        if (iff.isAnnotationPresent(Exportablebean.class)) {
68          return true;
69        }
70      }
71      return canConvert(type.getSuperclass());
72    }
73  
74    public void marshal(final Object source,
75        final HierarchicalStreamWriter writer, final MarshallingContext context) {
76      beanProvider.visitSerializableProperties(source,
77          new GuiceBeanProvider.Visitor() {
78            public boolean shouldVisit(String name, Class<?> definedIn) {
79              return mapper.shouldSerializeMember(definedIn, name);
80            }
81  
82            public void visit(String propertyName, Class<?> fieldType,
83                Class<?> definedIn, Object newObj) {
84              if (newObj != null) {
85                Mapper.ImplicitCollectionMapping mapping = mapper
86                    .getImplicitCollectionDefForFieldName(source.getClass(),
87                        propertyName);
88                if (mapping != null) {
89                  if (mapping.getItemFieldName() != null) {
90                    Collection<?> list = (Collection<?>) newObj;
91                    for (Iterator<?> iter = list.iterator(); iter.hasNext();) {
92                      Object obj = iter.next();
93                      writeField(propertyName, mapping.getItemFieldName(),
94                          mapping.getItemType(), definedIn, obj);
95                    }
96                  } else {
97                    context.convertAnother(newObj);
98                  }
99                } else {
100                 writeField(propertyName, propertyName, fieldType, definedIn,
101                     newObj);
102               }
103             }
104           }
105 
106           private void writeField(String propertyName, String aliasName,
107               Class<?> fieldType, Class<?> definedIn, Object newObj) {
108             ExtendedHierarchicalStreamWriterHelper.startNode(writer, mapper
109                 .serializedMember(source.getClass(), aliasName), fieldType);
110             context.convertAnother(newObj);
111             writer.endNode();
112 
113           }
114         });
115   }
116 
117   public Object unmarshal(final HierarchicalStreamReader reader,
118       final UnmarshallingContext context) {
119     final Object result = instantiateNewInstance(context);
120 
121     while (reader.hasMoreChildren()) {
122       reader.moveDown();
123 
124       String propertyName = mapper.realMember(result.getClass(), reader
125           .getNodeName());
126 
127       boolean propertyExistsInClass = beanProvider.propertyDefinedInClass(
128           propertyName, result.getClass());
129 
130       if (propertyExistsInClass) {
131         Class<?> type = determineType(reader, result, propertyName);
132         Object value = context.convertAnother(result, type);
133         beanProvider.writeProperty(result, propertyName, value);
134       } else if (mapper.shouldSerializeMember(result.getClass(), propertyName)) {
135         throw new ConversionException("Property '" + propertyName
136             + "' not defined in class " + result.getClass().getName());
137       }
138 
139       reader.moveUp();
140     }
141 
142     return result;
143   }
144 
145   private Object instantiateNewInstance(UnmarshallingContext context) {
146     Object result = context.currentObject();
147     if (result == null) {
148       result = beanProvider.newInstance(context.getRequiredType());
149     }
150     return result;
151   }
152 
153   private Class<?> determineType(HierarchicalStreamReader reader,
154       Object result, String fieldName) {
155     final String classAttributeName = mapper.attributeForAlias("class");
156     String classAttribute = reader.getAttribute(classAttributeName);
157     if (classAttribute != null) {
158       return mapper.realClass(classAttribute);
159     } else {
160       return mapper.defaultImplementationOf(beanProvider.getPropertyType(
161           result, fieldName));
162     }
163   }
164 
165   /***
166    * @deprecated since 1.3
167    */
168   public static class DuplicateFieldException extends ConversionException {
169     public DuplicateFieldException(String msg) {
170       super(msg);
171     }
172   }
173 
174 }