/* * Copyright 2005 Joe Walker * * Licensed under the Apache License, Version 2.0 (the "License"); * you may not use this file except in compliance with the License. * You may obtain a copy of the License at * * http://www.apache.org/licenses/LICENSE-2.0 * * Unless required by applicable law or agreed to in writing, software * distributed under the License is distributed on an "AS IS" BASIS, * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. * See the License for the specific language governing permissions and * limitations under the License. */ package org.directwebremoting.convert; import java.beans.BeanInfo; import java.beans.IntrospectionException; import java.beans.Introspector; import java.beans.PropertyDescriptor; import java.lang.reflect.Method; import java.util.HashMap; import java.util.Iterator; import java.util.Map; import java.util.Map.Entry; import org.apache.commons.logging.Log; import org.apache.commons.logging.LogFactory; import org.apache.xmlbeans.XmlObject; import org.directwebremoting.ConversionException; import org.directwebremoting.extend.InboundVariable; import org.directwebremoting.extend.Property; import org.directwebremoting.extend.PropertyDescriptorProperty; import org.directwebremoting.extend.ProtocolConstants; /** * A Converter for Apache XMLBeans. * @author Joe Walker [joe at getahead dot ltd dot uk] * @author Matthew Young [matthew dot young at forsakringskassan dot se] */ public class XmlBeanConverter extends BeanConverter { /* (non-Javadoc) * @see org.directwebremoting.extend.Converter#convertInbound(java.lang.Class, org.directwebremoting.extend.InboundVariable, org.directwebremoting.extend.InboundContext) */ @Override public Object convertInbound(Class<?> paramType, InboundVariable data) throws ConversionException { if (data == null) { return null; } String value = data.getValue(); log.debug("handling variable (" + value + ") for class (" + paramType.getName() + ")"); // If the text is null then the whole bean is null if (value.trim().equals(ProtocolConstants.INBOUND_NULL)) { return null; } if (!value.startsWith(ProtocolConstants.INBOUND_MAP_START) || !value.endsWith(ProtocolConstants.INBOUND_MAP_END)) { log.warn("Expected object while converting data for " + paramType.getName() + " in " + data.getContext().getCurrentProperty() + ". Passed: " + value); throw new ConversionException(paramType, "Data conversion error. See logs for more details."); } value = value.substring(1, value.length() - 1); try { if (instanceType != null) { Class.forName(instanceType.getName()); } else { Class.forName(paramType.getName()); } Class<?>[] innerClasses = paramType.getClasses(); Class<?> factory = null; for (Class<?> aClass : innerClasses) { if (aClass.getName().endsWith("Factory")) { factory = aClass; } } if (factory == null) { log.error("XmlObject.Factory method not found for Class [" + paramType.toString() + "]"); throw new ConversionException(paramType, "XmlObject.Factory method not found"); } Class<?>[] emptyArglist = new Class[0]; Method newInstance = factory.getMethod("newInstance", emptyArglist); Object bean = newInstance.invoke(null, (Object[]) emptyArglist); if (instanceType != null) { data.getContext().addConverted(data, instanceType, bean); } else { data.getContext().addConverted(data, paramType, bean); } Map<String, Property> properties = getPropertyMapFromClass(paramType, false, true); // Loop through the properties passed in Map<String, String> tokens = extractInboundTokens(paramType, value); for (Entry<String, String> entry : tokens.entrySet()) { String key = entry.getKey(); String val = entry.getValue(); log.debug("token entry (" + key + ") with value (" + val + ")"); Property property = properties.get(key); if (property == null) { log.warn("Missing java bean property to match javascript property: " + key + ". For causes see debug level logs:"); log.debug("- The javascript may be refer to a property that does not exist"); log.debug("- You may be missing the correct setter: set" + Character.toTitleCase(key.charAt(0)) + key.substring(1) + "()"); log.debug("- The property may be excluded using include or exclude rules."); StringBuffer all = new StringBuffer(); for (Iterator<String> pit = properties.keySet().iterator(); pit.hasNext();) { all.append(pit.next()); if (pit.hasNext()) { all.append(','); } } log.debug("Fields exist for (" + all + ")."); continue; } Class<?> propType = property.getPropertyType(); Object output = convert(val, propType, data.getContext(), property); property.setValue(bean, output); } return bean; } catch (ConversionException ex) { throw ex; } catch (Exception ex) { throw new ConversionException(paramType, ex); } } /* (non-Javadoc) * @see org.directwebremoting.convert.BeanConverter#getPropertyMapFromClass(java.lang.Class, boolean, boolean) */ @Override public Map<String, Property> getPropertyMapFromClass(Class<?> paramType, boolean readRequired, boolean writeRequired) throws ConversionException { try { if (!XmlObject.class.isAssignableFrom(paramType)) { throw new ConversionException(paramType, "class (" + paramType.getName() + ") not assignable from XmlObject"); } Class<?> beanInterface; if (paramType.isInterface()) { beanInterface = paramType; } else { beanInterface = paramType.getInterfaces()[0]; } Class<?> superInterface = (Class<?>) beanInterface.getGenericInterfaces()[0]; Map<String, Property> properties = new HashMap<String, Property>(); while (XmlObject.class.isAssignableFrom(superInterface)) { BeanInfo info = Introspector.getBeanInfo(beanInterface); PropertyDescriptor[] descriptors = info.getPropertyDescriptors(); for (int i = 0; i < descriptors.length; i++) { PropertyDescriptor descriptor = descriptors[i]; String name = descriptor.getName(); String type = descriptor.getPropertyType().getName(); // register Enum types if (type.matches(".*\\$Enum")) { getConverterManager().addConverter(type, enumConverter); } // We don't marshall getClass() if ("class".equals(name)) { continue; } // Access rules mean we might not want to do this one if (!isAllowedByIncludeExcludeRules(name)) { continue; } if (readRequired && descriptor.getReadMethod() == null) { continue; } if (writeRequired && descriptor.getWriteMethod() == null) { continue; } properties.put(name, new PropertyDescriptorProperty(descriptor)); } beanInterface = (Class<?>) beanInterface.getGenericInterfaces()[0]; superInterface = (Class<?>) beanInterface.getGenericInterfaces()[0]; } return properties; } catch (IntrospectionException ex) { throw new ConversionException(paramType, ex); } } /** * This used to be static, but there's a chance that could fail when there * is more than 1 DWR servlet in a given context. */ private final StringEnumAbstractBaseConverter enumConverter = new StringEnumAbstractBaseConverter(); /** * The log stream */ private static Log log = LogFactory.getLog(XmlBeanConverter.class); }