Title: [waffle-scm] [116] trunk/core/src/main/java/org/codehaus/waffle/view: WaffleXMLServlet is able to render ${controller} getters as XML through XStream
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 XStream

Added 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:

http://xircles.codehaus.org/manage_email

Reply via email to