001: /*
002: * Copyright 2005 Joe Walker
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: package org.directwebremoting.convert;
017:
018: import java.beans.BeanInfo;
019: import java.beans.IntrospectionException;
020: import java.beans.Introspector;
021: import java.beans.PropertyDescriptor;
022: import java.lang.reflect.Method;
023: import java.util.HashMap;
024: import java.util.Iterator;
025: import java.util.Map;
026: import java.util.Map.Entry;
027:
028: import org.apache.commons.logging.Log;
029: import org.apache.commons.logging.LogFactory;
030: import org.apache.xmlbeans.XmlObject;
031: import org.directwebremoting.dwrp.ParseUtil;
032: import org.directwebremoting.dwrp.ProtocolConstants;
033: import org.directwebremoting.extend.InboundContext;
034: import org.directwebremoting.extend.InboundVariable;
035: import org.directwebremoting.extend.MarshallException;
036: import org.directwebremoting.extend.Property;
037: import org.directwebremoting.extend.TypeHintContext;
038: import org.directwebremoting.impl.PropertyDescriptorProperty;
039: import org.directwebremoting.util.LocalUtil;
040: import org.directwebremoting.util.Messages;
041:
042: /**
043: * A Converter for Apache XMLBeans.
044: * @author Joe Walker [joe at getahead dot ltd dot uk]
045: * @author Matthew Young [matthew dot young at forsakringskassan dot se]
046: */
047: public class XmlBeanConverter extends BeanConverter {
048: /* (non-Javadoc)
049: * @see org.directwebremoting.extend.Converter#convertInbound(java.lang.Class, org.directwebremoting.extend.InboundVariable, org.directwebremoting.extend.InboundContext)
050: */
051: @Override
052: public Object convertInbound(Class<?> paramType,
053: InboundVariable iv, InboundContext inctx)
054: throws MarshallException {
055: String value = iv.getValue();
056:
057: logger.debug("handling variable (" + value + ") for class ("
058: + paramType.getName() + ")");
059:
060: // If the text is null then the whole bean is null
061: if (value.trim().equals(ProtocolConstants.INBOUND_NULL)) {
062: return null;
063: }
064:
065: if (!value.startsWith(ProtocolConstants.INBOUND_MAP_START)) {
066: throw new MarshallException(paramType, Messages.getString(
067: "BeanConverter.FormatError",
068: ProtocolConstants.INBOUND_MAP_START));
069: }
070:
071: if (!value.endsWith(ProtocolConstants.INBOUND_MAP_END)) {
072: throw new MarshallException(paramType, Messages.getString(
073: "BeanConverter.FormatError",
074: ProtocolConstants.INBOUND_MAP_START));
075: }
076:
077: value = value.substring(1, value.length() - 1);
078:
079: try {
080: if (instanceType != null) {
081: Class.forName(instanceType.getName());
082: } else {
083: Class.forName(paramType.getName());
084: }
085:
086: Class<?>[] innerClasses = paramType.getClasses();
087: Class<?> factory = null;
088: for (Class<?> aClass : innerClasses) {
089: if (aClass.getName().endsWith("Factory")) {
090: factory = aClass;
091: }
092: }
093:
094: if (factory == null) {
095: logger
096: .error("XmlObject.Factory method not found for Class ["
097: + paramType.toString() + "]");
098: throw new MarshallException(paramType,
099: "XmlObject.Factory method not found");
100: }
101:
102: Class<?>[] emptyArglist = new Class[0];
103: Method newInstance = factory.getMethod("newInstance",
104: emptyArglist);
105: Object bean = newInstance.invoke(null,
106: (Object[]) emptyArglist);
107:
108: if (instanceType != null) {
109: inctx.addConverted(iv, instanceType, bean);
110: } else {
111: inctx.addConverted(iv, paramType, bean);
112: }
113:
114: Map<String, Property> properties = getPropertyMapFromClass(
115: paramType, false, true);
116:
117: // Loop through the properties passed in
118: Map<String, String> tokens = extractInboundTokens(
119: paramType, value);
120: for (Entry<String, String> entry : tokens.entrySet()) {
121: String key = entry.getKey();
122: String val = entry.getValue();
123:
124: logger.debug("token entry (" + key + ") with value ("
125: + val + ")");
126:
127: Property property = properties.get(key);
128: if (property == null) {
129: logger
130: .warn("Missing java bean property to match javascript property: "
131: + key
132: + ". For causes see debug level logs:");
133:
134: logger
135: .debug("- The javascript may be refer to a property that does not exist");
136: logger
137: .debug("- You may be missing the correct setter: set"
138: + Character.toTitleCase(key
139: .charAt(0))
140: + key.substring(1) + "()");
141: logger
142: .debug("- The property may be excluded using include or exclude rules.");
143:
144: StringBuffer all = new StringBuffer();
145: for (Iterator<String> pit = properties.keySet()
146: .iterator(); pit.hasNext();) {
147: all.append(pit.next());
148: if (pit.hasNext()) {
149: all.append(',');
150: }
151: }
152: logger.debug("Fields exist for (" + all + ").");
153: continue;
154: }
155:
156: Class<?> propType = property.getPropertyType();
157:
158: String[] split = ParseUtil.splitInbound(val);
159: String splitValue = split[LocalUtil.INBOUND_INDEX_VALUE];
160: String splitType = split[LocalUtil.INBOUND_INDEX_TYPE];
161:
162: InboundVariable nested = new InboundVariable(iv
163: .getLookup(), null, splitType, splitValue);
164: nested.dereference();
165:
166: TypeHintContext incc = createTypeHintContext(inctx,
167: property);
168:
169: Object output = converterManager.convertInbound(
170: propType, nested, inctx, incc);
171: property.setValue(bean, output);
172: }
173:
174: return bean;
175: } catch (MarshallException ex) {
176: throw ex;
177: } catch (Exception ex) {
178: throw new MarshallException(paramType, ex);
179: }
180: }
181:
182: /* (non-Javadoc)
183: * @see org.directwebremoting.convert.BeanConverter#getPropertyMapFromClass(java.lang.Class, boolean, boolean)
184: */
185: @Override
186: public Map<String, Property> getPropertyMapFromClass(
187: Class<?> paramType, boolean readRequired,
188: boolean writeRequired) throws MarshallException {
189: try {
190: if (!XmlObject.class.isAssignableFrom(paramType)) {
191: throw new MarshallException(paramType, "class ("
192: + paramType.getName()
193: + ") not assignable from XmlObject");
194: }
195:
196: Class<?> beanInterface;
197: if (paramType.isInterface()) {
198: beanInterface = paramType;
199: } else {
200: beanInterface = paramType.getInterfaces()[0];
201: }
202:
203: Class<?> super Interface = (Class<?>) beanInterface
204: .getGenericInterfaces()[0];
205:
206: Map<String, Property> properties = new HashMap<String, Property>();
207:
208: while (XmlObject.class.isAssignableFrom(super Interface)) {
209: BeanInfo info = Introspector.getBeanInfo(beanInterface);
210: PropertyDescriptor[] descriptors = info
211: .getPropertyDescriptors();
212:
213: for (int i = 0; i < descriptors.length; i++) {
214: PropertyDescriptor descriptor = descriptors[i];
215: String name = descriptor.getName();
216: String type = descriptor.getPropertyType()
217: .getName();
218:
219: // register Enum types
220: if (type.matches(".*\\$Enum")) {
221: getConverterManager().addConverter(type,
222: enumConverter);
223: }
224:
225: // We don't marshall getClass()
226: if ("class".equals(name)) {
227: continue;
228: }
229:
230: // Access rules mean we might not want to do this one
231: if (!isAllowedByIncludeExcludeRules(name)) {
232: continue;
233: }
234:
235: if (readRequired
236: && descriptor.getReadMethod() == null) {
237: continue;
238: }
239:
240: if (writeRequired
241: && descriptor.getWriteMethod() == null) {
242: continue;
243: }
244:
245: properties.put(name,
246: new PropertyDescriptorProperty(descriptor));
247: }
248:
249: beanInterface = (Class<?>) beanInterface
250: .getGenericInterfaces()[0];
251: super Interface = (Class<?>) beanInterface
252: .getGenericInterfaces()[0];
253: }
254:
255: return properties;
256: } catch (IntrospectionException ex) {
257: throw new MarshallException(paramType, ex);
258: }
259:
260: }
261:
262: private static StringEnumAbstractBaseConverter enumConverter = new StringEnumAbstractBaseConverter();
263:
264: private static Log logger = LogFactory
265: .getLog(XmlBeanConverter.class);
266: }
|