- Revision
- 116
- Author
- paulosilveira
- Date
- 2007-05-31 02:53:01 -0500 (Thu, 31 May 2007)
Log Message
WaffleXMLServlet is able to render ${controller} getters as XML through XStreamAdded Paths
Diff
Added: trunk/core/src/main/java/org/codehaus/waffle/view/GetterXMLConverter.java (0 => 116)
--- trunk/core/src/main/java/org/codehaus/waffle/view/GetterXMLConverter.java (rev 0) +++ trunk/core/src/main/java/org/codehaus/waffle/view/GetterXMLConverter.java 2007-05-31 07:53:01 UTC (rev 116) @@ -0,0 +1,130 @@ +package org.codehaus.waffle.view; + +import java.lang.reflect.InvocationTargetException; +import java.lang.reflect.Method; +import java.lang.reflect.Modifier; +import java.util.Map; +import java.util.TreeMap; + +import com.thoughtworks.xstream.converters.Converter; +import com.thoughtworks.xstream.converters.MarshallingContext; +import com.thoughtworks.xstream.converters.UnmarshallingContext; +import com.thoughtworks.xstream.io.HierarchicalStreamReader; +import com.thoughtworks.xstream.io.HierarchicalStreamWriter; + +/** + * Used to make XStream use bean getters to serialize, no attributes + * @author Paulo Silveira + * + */ +public class GetterXMLConverter implements Converter { + + private static final String IS_INITIALS = "is"; + + private static final String GET_INITIALS = "get"; + + private static final Object[] NO_ARGUMENTS = {}; + + public void marshal(Object o, HierarchicalStreamWriter writer, + MarshallingContext context) { + + Map<String, Method> getters = getGetters(o.getClass()); + for (String name : getters.keySet()) { + Method getter = getters.get(name); + writer.startNode(name); + try { + Object got = getter.invoke(o, NO_ARGUMENTS); + if (got != null) + + context.convertAnother(got); + } catch (IllegalArgumentException e) { + throw new IllegalStateException(e); + } catch (IllegalAccessException e) { + throw new IllegalStateException(e); + } catch (InvocationTargetException e) { + throw new IllegalStateException(e); + } + writer.endNode(); + + } + } + + public Object unmarshal(HierarchicalStreamReader arg0, + UnmarshallingContext arg1) { + throw new UnsupportedOperationException( + "Converter only available for marshaling"); + } + + public boolean canConvert(Class clazz) { + return true; + } + + public static Map<String, Method> getGetters(Class clazz) { + Map<String, Method> methods = new TreeMap<String, Method>(); + for (Method m : clazz.getMethods()) { + if (!isGetter(m)) { + continue; + } + if (m.getDeclaringClass().equals(Object.class)) { + // hack: removing getClass() + continue; + } + String propertyName = ""; + if (m.getName().startsWith(GET_INITIALS)) { + propertyName = m.getName().substring(GET_INITIALS.length()); + + } else if (m.getName().startsWith(IS_INITIALS)) { + propertyName = m.getName().substring(IS_INITIALS.length()); + } + // ok, this is a hack, cause we can have a problem + // with classes with a get() method + // (the propertyname would be an empty string) + if (propertyName.length() != 0) { + if (propertyName.length() == 1 + || Character.isLowerCase(propertyName.charAt(1))) { + propertyName = decapitalize(propertyName); + } + methods.put(propertyName, m); + } + } + return methods; + } + + private static String decapitalize(String name) { + if (name == null || name.length() == 0) { + return name; + } + if (name.length() > 1 && Character.isUpperCase(name.charAt(1)) + && Character.isUpperCase(name.charAt(0))) { + return name; + } + char chars[] = name.toCharArray(); + chars[0] = Character.toLowerCase(chars[0]); + return new String(chars); + } + + public static boolean isGetter(Method m) { + if (m.getParameterTypes().length != 0 + || !Modifier.isPublic(m.getModifiers()) + || m.getReturnType().equals(Void.TYPE)) { + return false; + } + if (Modifier.isStatic(m.getModifiers()) + || !Modifier.isPublic(m.getModifiers()) + || Modifier.isAbstract(m.getModifiers())) { + return false; + } + if (m.getName().startsWith(GET_INITIALS) + && m.getName().length() > GET_INITIALS.length()) { + return true; + } + if (m.getName().startsWith(IS_INITIALS) + && m.getName().length() > IS_INITIALS.length() + && (m.getReturnType().equals(boolean.class) || m + .getReturnType().equals(Boolean.class))) { + return true; + } + return false; + } + +}
Added: trunk/core/src/main/java/org/codehaus/waffle/view/ResponderView.java (0 => 116)
--- trunk/core/src/main/java/org/codehaus/waffle/view/ResponderView.java (rev 0) +++ trunk/core/src/main/java/org/codehaus/waffle/view/ResponderView.java 2007-05-31 07:53:01 UTC (rev 116) @@ -0,0 +1,31 @@ +package org.codehaus.waffle.view; + +import java.io.IOException; + +import javax.servlet.ServletRequest; +import javax.servlet.http.HttpServletResponse; + +/** + * A ResponderView is able to use the servlet response and write directly to it, + * instead of dispatching it. + * + * @author Paulo Silveira + */ +public abstract class ResponderView extends View { + + public ResponderView() { + super(null, ""); + } + + /** + * Renders the output directly into servlet response + * + * @param req + * servler request + * @param resp + * http servlet response + * @throws IOException + */ + public abstract void respond(ServletRequest req, HttpServletResponse resp) + throws IOException; +}
Added: trunk/core/src/main/java/org/codehaus/waffle/view/XMLView.java (0 => 116)
--- trunk/core/src/main/java/org/codehaus/waffle/view/XMLView.java (rev 0) +++ trunk/core/src/main/java/org/codehaus/waffle/view/XMLView.java 2007-05-31 07:53:01 UTC (rev 116) @@ -0,0 +1,73 @@ +package org.codehaus.waffle.view; + +import java.io.IOException; +import java.util.Collection; + +import javax.servlet.ServletRequest; +import javax.servlet.http.HttpServletResponse; + +import org.codehaus.waffle.Constants; + +import com.thoughtworks.xstream.XStream; +import com.thoughtworks.xstream.converters.collections.CollectionConverter; +import com.thoughtworks.xstream.io.xml.DomDriver; +import com.thoughtworks.xstream.mapper.MapperWrapper; + +/** + * A view that renders the controller as XML. + * + * @author Paulo Silveira + */ +public class XMLView extends ResponderView { + + public static final String CONTENT_TYPE = "text/plain"; + + @Override + public void respond(ServletRequest req, HttpServletResponse resp) + throws IOException { + XStream xs = new WaffleXStream(); + xs.registerConverter(new GetterXMLConverter(), -19); + xs.registerConverter(new CollectionConverter(xs.getMapper()) { + public boolean canConvert(Class c) { + return Collection.class.isAssignableFrom(c); + } + }, -18); + + // TODO: should we stream.setMode(XStream.NO_REFERENCES); ? + + String data = "" + resp.setContentType(CONTENT_TYPE); + + // TODO: char encoding? + resp.getOutputStream().print(data); + } + +} + +// todo: public class? isolated unit test +/** + * A XStream class already configured to output user friendly XML based on + * getters. + * + */ +class WaffleXStream extends XStream { + + public WaffleXStream() { + super(new DomDriver()); + } + + @Override + protected MapperWrapper wrapMapper(MapperWrapper next) { + return new MapperWrapper(next) { + @Override + public String serializedClass(Class type) { + String value = super.serializedClass(type); + if (type.getName().replace('$', '-').equals(value)) { + return type.getSimpleName(); + } + return value; + } + }; + } + +} \ No newline at end of file
To unsubscribe from this list please visit:
