Author: jdonnerstag
Date: Sat Sep 26 12:07:24 2009
New Revision: 819116
URL: http://svn.apache.org/viewvc?rev=819116&view=rev
Log:
committed initial contribution
Issue: WICKET-2388
Added:
wicket/trunk/wicket/src/main/java/org/apache/wicket/PageParametersMarshaller.java
Modified:
wicket/trunk/wicket/src/main/java/org/apache/wicket/PageParameters.java
wicket/trunk/wicket/src/test/java/org/apache/wicket/PageParametersTest.java
Modified:
wicket/trunk/wicket/src/main/java/org/apache/wicket/PageParameters.java
URL:
http://svn.apache.org/viewvc/wicket/trunk/wicket/src/main/java/org/apache/wicket/PageParameters.java?rev=819116&r1=819115&r2=819116&view=diff
==============================================================================
--- wicket/trunk/wicket/src/main/java/org/apache/wicket/PageParameters.java
(original)
+++ wicket/trunk/wicket/src/main/java/org/apache/wicket/PageParameters.java Sat
Sep 26 12:07:24 2009
@@ -202,4 +202,66 @@
}
return params;
}
+
+ /**
+ * Get an instance of an interface, which transparently reads and
writes to this PageParameters.
+ * Useful if you would prefer to deal in Java objects rather than
extracting key/value pairs.
+ * <h4>Usage:</h4> Write an interface that follows the beans pattern,
such as
+ *
+ * <pre>
+ * public interface MyData
+ * {
+ * double getUserid();
+ *
+ * void setUserId(double id);
+ *
+ * String getRequestedCheese();
+ *
+ * void setRequestedCheese(String cheese);
+ *
+ * boolean isBackorder();
+ *
+ * void setBackorder(boolean val);
+ * }
+ * </pre>
+ *
+ * It <em>must</em> be a Java interface, because the implementation
uses dynamic proxies to
+ * generate an implementation of that interface.
+ * <p/>
+ * Your PageParameters should contain key/value pairs with names such as
+ * "requestedCheese", "userid" and
"backorder"
+ * <p/>
+ * You will be returned an implementation of your interface, which
delegates to the
+ * PageParameters for the actual values, but handles typecasting and
avoids issues with typos in
+ * string keys.
+ * <p/>
+ * The resulting object is read/write, so you can use it both to
populate and to read from a
+ * PageParameters object.
+ * <p/>
+ * If the requested value does not exist in the PageParameters, the
return value will be null,
+ * -1 or false depending on the requested type.
+ * <p/>
+ * Note that it is possible to read and write <code>Serializable</code>
objects; however, this
+ * should not be done for objects with many fields, as it may exceed
the browser's URL-length
+ * limit.
+ *
+ * @param <T>
+ * The return type
+ * @param ifaceType
+ * The concrete type of the interface you need
+ * @return An instance of that interface, dynamically generated
+ */
+ public <T> T asObject(Class<T> ifaceType)
+ {
+ if (ifaceType == null)
+ {
+ throw new NullPointerException("Parameter 'ifaceType'
must not be null");
+ }
+ if (!ifaceType.isInterface())
+ {
+ throw new IllegalArgumentException("ifaceType is not an
interface: " +
+ ifaceType.getName());
+ }
+ return new PageParametersMarshaller().read(ifaceType, this);
+ }
}
Added:
wicket/trunk/wicket/src/main/java/org/apache/wicket/PageParametersMarshaller.java
URL:
http://svn.apache.org/viewvc/wicket/trunk/wicket/src/main/java/org/apache/wicket/PageParametersMarshaller.java?rev=819116&view=auto
==============================================================================
---
wicket/trunk/wicket/src/main/java/org/apache/wicket/PageParametersMarshaller.java
(added)
+++
wicket/trunk/wicket/src/main/java/org/apache/wicket/PageParametersMarshaller.java
Sat Sep 26 12:07:24 2009
@@ -0,0 +1,516 @@
+/*
+ * Licensed to the Apache Software Foundation (ASF) under one or more
+ * contributor license agreements. See the NOTICE file distributed with
+ * this work for additional information regarding copyright ownership.
+ * The ASF licenses this file to You 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.apache.wicket;
+
+import java.io.ByteArrayInputStream;
+import java.io.ByteArrayOutputStream;
+import java.io.IOException;
+import java.io.ObjectInputStream;
+import java.io.ObjectOutputStream;
+import java.io.Serializable;
+import java.io.UnsupportedEncodingException;
+import java.lang.reflect.Array;
+import java.lang.reflect.InvocationHandler;
+import java.lang.reflect.Method;
+import java.lang.reflect.Proxy;
+import java.util.Arrays;
+import java.util.Collections;
+import java.util.HashSet;
+import java.util.Set;
+
+import org.apache.wicket.util.crypt.Base64;
+import org.slf4j.Logger;
+import org.slf4j.LoggerFactory;
+
+/**
+ * Pass in an interface type to the read method. The code will find all
getters and setters, and use
+ * their property names as keys for reading and writing to the underlying
properties object.
+ *
+ * @author Tim Boudreau
+ */
+public final class PageParametersMarshaller
+{
+ /** Log */
+ private static final Logger log =
LoggerFactory.getLogger(PageParametersMarshaller.class);
+
+ /**
+ * Construct.
+ */
+ public PageParametersMarshaller()
+ {
+ }
+
+ /**
+ * Get a proxy object
+ *
+ * @param <T>
+ * @param returnType
+ * @param data
+ * @return A new proxy object
+ */
+ @SuppressWarnings("unchecked")
+ public <T> T read(final Class<T> returnType, final PageParameters data)
+ {
+ if (!returnType.isInterface())
+ {
+ throw new IllegalArgumentException("Must be an
interface: returnType=" +
+ returnType.getName());
+ }
+ return (T)Proxy.newProxyInstance(returnType.getClassLoader(),
new Class[] { returnType },
+ new MyInvocationHandler(data));
+ }
+
+ /**
+ *
+ */
+ private static final class MyInvocationHandler implements
InvocationHandler
+ {
+ private final PageParameters params;
+
+ /**
+ * Construct.
+ *
+ * @param params
+ */
+ public MyInvocationHandler(final PageParameters params)
+ {
+ this.params = params;
+ }
+
+ /**
+ * @see
java.lang.reflect.InvocationHandler#invoke(java.lang.Object,
+ * java.lang.reflect.Method, java.lang.Object[])
+ */
+ public Object invoke(final Object proxy, final Method method,
final Object[] args)
+ throws Throwable
+ {
+ try
+ {
+ String mname = method.getName();
+ boolean isSetter = mname.startsWith("set");
+ boolean isGetter = mname.startsWith("get") ||
mname.startsWith("is");
+ Class<?> type = isSetter ?
method.getParameterTypes()[0] : method.getReturnType();
+
+ if (!isSetter && !isGetter)
+ {
+ return nullOrNumber(isGetter, type);
+ }
+ if (isSetter &&
+ ((method.getParameterTypes() == null)
|| (method.getParameterTypes().length == 0)))
+ {
+ return nullOrNumber(isGetter, type);
+ }
+ if (isGetter && method.getParameterTypes() !=
null &&
+ method.getParameterTypes().length != 0)
+ {
+ return nullOrNumber(isGetter, type);
+ }
+
+ String name = stripName(method);
+ for (Type t : Type.values())
+ {
+ if (t.match(type))
+ {
+ if (isGetter)
+ {
+ return read(params, t,
name);
+ }
+ else
+ {
+ Object val = args ==
null ? null : args.length == 0 ? null : args[0];
+ if (val == null)
+ {
+
params.remove(name);
+ }
+ else
+ {
+ write(params,
t, name, val);
+ }
+ }
+ break;
+ }
+ }
+ }
+ catch (RuntimeException e)
+ {
+ log.error("Error while reading/updating
PageParamaters. ", e);
+ throw e;
+ }
+ return null;
+ }
+ }
+
+ /**
+ * @param method
+ * @return method name without set/get/is
+ */
+ private static final String stripName(final Method method)
+ {
+ String s = method.getName();
+ if (s.startsWith("get") || s.startsWith("set"))
+ {
+ s = s.substring(3);
+ }
+ else if (s.startsWith("is"))
+ {
+ s = s.substring(2);
+ }
+ StringBuilder sb = new StringBuilder(s);
+ sb.setCharAt(0, Character.toLowerCase(sb.charAt(0)));
+ return sb.toString();
+ }
+
+ /**
+ *
+ * @param getter
+ * @param c
+ * @return
+ */
+ private static final Object nullOrNumber(final boolean getter, final
Class<?> c)
+ {
+ if (!getter)
+ {
+ return null;
+ }
+ if (c.isArray())
+ {
+ return Array.newInstance(c, 0);
+ }
+ for (Type t : Type.values())
+ {
+ if (t.match(c))
+ {
+ return t.noValue();
+ }
+ }
+ return null;
+ }
+
+ /**
+ *
+ * @param p
+ * @param type
+ * @param name
+ * @param o
+ */
+ private static void write(final PageParameters p, final Type type,
final String name,
+ final Object o)
+ {
+ new PageParametersWriteStrategy().write(p, type, name, o);
+ }
+
+ /**
+ *
+ * @param p
+ * @param t
+ * @param name
+ * @return
+ */
+ private static Object read(final PageParameters p, final Type t, final
String name)
+ {
+ return new PageParametersReadStrategy().read(p, t, name);
+ }
+
+ /**
+ *
+ */
+ private static enum Type {
+
+ BOOLEAN, INT, STRING, LONG, DOUBLE, FLOAT, BYTE, SHORT, CHAR,
BYTE_ARRAY, SERIALIZABLE;
+
+ public boolean match(Class<?> type)
+ {
+ for (Class<?> c : getTypes())
+ {
+ if (c.isAssignableFrom(type))
+ {
+ return true;
+ }
+ }
+ return false;
+ }
+
+ Set<Class<?>> types;
+
+ public Set<Class<?>> getTypes()
+ {
+ if (types == null)
+ {
+ switch (this)
+ {
+ case BOOLEAN :
+ types = toSet(Boolean.class,
Boolean.TYPE);
+ break;
+ case BYTE :
+ types = toSet(Byte.class,
Byte.TYPE);
+ break;
+ case BYTE_ARRAY :
+ types = toSet(new
byte[0].getClass());
+ break;
+ case DOUBLE :
+ types = toSet(Double.class,
Double.TYPE);
+ break;
+ case FLOAT :
+ types = toSet(Float.class,
Float.TYPE);
+ break;
+ case INT :
+ types = toSet(Integer.class,
Integer.TYPE);
+ break;
+ case LONG :
+ types = toSet(Long.class,
Long.TYPE);
+ break;
+ case SHORT :
+ types = toSet(Short.class,
Short.TYPE);
+ break;
+ case STRING :
+ types = toSet(String.class);
+ break;
+ case CHAR :
+ types = toSet(Character.TYPE,
Character.class);
+ break;
+ // Note: Serializable *must* remain the
last item tested for
+ // or everything will be resolved as
serializable
+ case SERIALIZABLE :
+ types =
toSet(Serializable.class);
+ break;
+ default :
+ throw new AssertionError();
+ }
+ }
+ return Collections.unmodifiableSet(types);
+ }
+
+ /**
+ *
+ * @param types
+ * @return a new HashSet with all the types provided
+ */
+ private Set<Class<?>> toSet(final Class<?>... types)
+ {
+ return new HashSet<Class<?>>(Arrays.asList(types));
+ }
+
+ /**
+ *
+ * @return The "null" or "no" values for all types
+ */
+ public Object noValue()
+ {
+ switch (this)
+ {
+ case BOOLEAN :
+ return Boolean.FALSE;
+ case INT :
+ return -1;
+ case STRING :
+ return null;
+ case LONG :
+ return -1L;
+ case DOUBLE :
+ return -1D;
+ case FLOAT :
+ return -1F;
+ case BYTE :
+ return new Byte((byte)-1);
+ case SHORT :
+ return new Short((short)-1);
+ case CHAR :
+ return (char)0;
+ default :
+ return null;
+ }
+ }
+ }
+
+ /**
+ *
+ */
+ private static final class PageParametersWriteStrategy
+ {
+ public void write(final PageParameters p, final Type type,
final String name, final Object o)
+ {
+ if (o == null)
+ {
+ p.remove(name);
+ return;
+ }
+
+ switch (type)
+ {
+ case BOOLEAN :
+ case INT :
+ case STRING :
+ case LONG :
+ case DOUBLE :
+ case FLOAT :
+ case BYTE :
+ case SHORT :
+ case CHAR :
+ p.put(name, o);
+ break;
+ case BYTE_ARRAY :
+ byte[] bytes = (byte[])o;
+ String s = bytesToString(bytes);
+ p.put(name, s);
+ break;
+ case SERIALIZABLE :
+ ByteArrayOutputStream out = new
ByteArrayOutputStream(512);
+ try
+ {
+ ObjectOutputStream oout = new
ObjectOutputStream(out);
+ oout.writeObject(o);
+ out.close();
+ String asString =
bytesToString(out.toByteArray());
+ p.put(name, asString);
+ }
+ catch (IOException ex)
+ {
+ log.error("Error while reading
Serializable into a String", ex);
+ }
+ finally
+ {
+ try
+ {
+ out.close();
+ }
+ catch (IOException ex)
+ {
+ // Should normally not
happen
+ log.error(null, ex);
+ }
+ }
+ break;
+ default :
+ throw new AssertionError(
+ "Must be primitive type, byte
array or serializable: " + o +
+ " does not match type "
+ type);
+ }
+ }
+
+ /**
+ * Convert a byte array into a hexadecimal string
+ *
+ * @param bytes
+ * @return String
+ */
+ static String bytesToString(byte[] bytes)
+ {
+ try
+ {
+ return new String(Base64.encodeBase64(bytes),
"UTF-8");
+ }
+ catch (UnsupportedEncodingException ex)
+ {
+ log.error(
+ "Error while converting UTF-8 byte[]
into String. Retrying with default Locale.",
+ ex);
+
+ return new String(Base64.decodeBase64(bytes));
+ }
+ }
+ }
+
+ /**
+ *
+ */
+ private static final class PageParametersReadStrategy
+ {
+ public Object read(final PageParameters p, final Type type,
final String name)
+ {
+ switch (type)
+ {
+ case BOOLEAN :
+ return
Boolean.valueOf(p.getAsBoolean(name, false));
+ case INT :
+ return new Integer(p.getInt(name, -1));
+ case STRING :
+ return p.getString(name, null);
+ case LONG :
+ return new Long(p.getLong(name, -1));
+ case DOUBLE :
+ return new Double(p.getDouble(name,
-1));
+ case FLOAT :
+ return new Float(p.getAsDouble(name,
-1));
+ case BYTE :
+ return new Byte((byte)p.getInt(name,
-1));
+ case SHORT :
+ return new Short((short)p.getInt(name,
-1));
+ case CHAR :
+ String s = p.getString(name);
+ if (s == null || s.length() == 0)
+ {
+ return -1;
+ }
+ return s.charAt(0);
+ case BYTE_ARRAY :
+ String hex = p.getString(name);
+ return stringToBytes(hex);
+ case SERIALIZABLE :
+ byte[] data = (byte[])read(p,
Type.BYTE_ARRAY, name);
+ if (data != null && data.length > 0)
+ {
+ try
+ {
+ ObjectInputStream in =
new ObjectInputStream(new ByteArrayInputStream(
+ data));
+ try
+ {
+ return
in.readObject();
+ }
+ catch
(ClassNotFoundException ex)
+ {
+
log.error("Error converting serialized data stream into an Object",
+ ex);
+ }
+ finally
+ {
+ in.close();
+ }
+ }
+ catch (IOException ex)
+ {
+ log.error("Error
reading serialized data", ex);
+ }
+ }
+ break;
+ default :
+ throw new AssertionError();
+ }
+ return null;
+ }
+
+ /**
+ *
+ * @param s
+ * @return String converted into byte[] using UTF-8
+ */
+ private static byte[] stringToBytes(final String s)
+ {
+ try
+ {
+ return Base64.decodeBase64(s.getBytes("UTF-8"));
+ }
+ catch (UnsupportedEncodingException ex)
+ {
+ // should not happen
+ log.error("Error while converting String into
byte[]", ex);
+
+ return Base64.encodeBase64(s.getBytes(), false);
+ }
+ }
+ }
+}
Modified:
wicket/trunk/wicket/src/test/java/org/apache/wicket/PageParametersTest.java
URL:
http://svn.apache.org/viewvc/wicket/trunk/wicket/src/test/java/org/apache/wicket/PageParametersTest.java?rev=819116&r1=819115&r2=819116&view=diff
==============================================================================
--- wicket/trunk/wicket/src/test/java/org/apache/wicket/PageParametersTest.java
(original)
+++ wicket/trunk/wicket/src/test/java/org/apache/wicket/PageParametersTest.java
Sat Sep 26 12:07:24 2009
@@ -16,6 +16,10 @@
*/
package org.apache.wicket;
+import java.io.Serializable;
+import java.util.Arrays;
+import java.util.Map;
+
import junit.framework.TestCase;
/**
@@ -123,4 +127,428 @@
params.put("myint", 12345);
assertEquals(params.getAsInteger("myint").intValue(), 12345);
}
+
+ /**
+ *
+ */
+ public void testAsObject()
+ {
+ System.out.println("testObjectDereferencing");
+ PageParameters params = new PageParameters();
+ Face face = new Face();
+ IFace fromParams = params.asObject(IFace.class);
+ assertNotNull(fromParams);
+ face.copyInto(fromParams);
+
+ IFace another = params.asObject(IFace.class);
+ Face copy = new Face(another);
+ assertEquals(face, copy);
+
+ System.err.println("Params:\n" + params);
+
+ // Do the equivalent of passing this as a URL and
reconstructing it
+
+ Map<String, String[]> x = params.toRequestParameters();
+ PageParameters nue = new PageParameters(x);
+
+ another = nue.asObject(IFace.class);
+ copy = new Face(another);
+ assertEquals(face, copy);
+ assertNotSame(face, copy);
+
+ PageParameters pp = new PageParameters();
+ IFace i = pp.asObject(IFace.class);
+ Face one = new Face(new SerializableThing("whee"), 72, false,
1.23, 3.75F, (short)450,
+ (byte)-5, new byte[] { 1, 2, 3 }, 'q', 342L,
"testAllThis");
+ one.copyInto(i);
+
+ Face two = new Face(i);
+ assertFalse(copy.equals(two));
+ assertEquals(one, two);
+
+ assertEquals(72, i.getIntVal());
+ assertNotNull(pp.get("intVal"));
+
+ assertEquals(72, (int)pp.getAsInteger("intVal"));
+
+ i.setIntVal(325);
+ assertEquals(325, (int)pp.getAsInteger("intVal"));
+
+ }
+
+
+ public static interface IFace extends Serializable
+ {
+ byte[] getByteArrVal();
+
+ byte getByteVal();
+
+ char getCharVal();
+
+ double getDoubleVal();
+
+ float getFloatVal();
+
+ int getIntVal();
+
+ long getLongVal();
+
+ short getShortVal();
+
+ String getStringVal();
+
+ SerializableThing getThing();
+
+ boolean isBoolVal();
+
+ void setBoolVal(boolean boolVal);
+
+ void setByteArrVal(byte[] byteArrVal);
+
+ void setByteVal(byte byteVal);
+
+ void setCharVal(char charVal);
+
+ void setDoubleVal(double doubleVal);
+
+ void setFloatVal(float floatVal);
+
+ void setIntVal(int intVal);
+
+ void setLongVal(long longVal);
+
+ void setShortVal(short shortVal);
+
+ void setStringVal(String stringVal);
+
+ void setThing(SerializableThing thing);
+ }
+
+ /**
+ *
+ */
+ public static final class Face implements IFace
+ {
+ private static final long serialVersionUID = 1L;
+
+ private SerializableThing thing = new SerializableThing("Foo");
+ private int intVal = 23;
+ private boolean boolVal = true;
+ private double doubleVal = 0.135D;
+ private float floatVal = 12.230F;
+ private short shortVal = 32766;
+ private byte byteVal = -123;
+ private byte[] byteArrVal = new byte[] { -124, -3, 0, 14, 22 };
+ private char charVal = 'c';
+ private long longVal = 1294380151L;
+ private String stringVal = "Hello World";
+
+ /**
+ * Construct.
+ *
+ * @param thing
+ * @param intVal
+ * @param boolVal
+ * @param doubleVal
+ * @param floatVal
+ * @param shortVal
+ * @param byteVal
+ * @param byteArrVal
+ * @param charVal
+ * @param longVal
+ * @param stringVal
+ */
+ public Face(SerializableThing thing, int intVal, boolean
boolVal, double doubleVal,
+ float floatVal, short shortVal, byte byteVal, byte[]
byteArrVal, char charVal,
+ long longVal, String stringVal)
+ {
+ this.thing = thing;
+ this.intVal = intVal;
+ this.boolVal = boolVal;
+ this.doubleVal = doubleVal;
+ this.floatVal = floatVal;
+ this.shortVal = shortVal;
+ this.byteVal = byteVal;
+ this.byteArrVal = byteArrVal;
+ this.charVal = charVal;
+ this.longVal = longVal;
+ this.stringVal = stringVal;
+ }
+
+ /**
+ * Construct.
+ */
+ public Face()
+ {
+
+ }
+
+ /**
+ * Construct.
+ *
+ * @param o
+ */
+ public Face(IFace o)
+ {
+ thing = o.getThing();
+ intVal = o.getIntVal();
+ boolVal = o.isBoolVal();
+ doubleVal = o.getDoubleVal();
+ floatVal = o.getFloatVal();
+ shortVal = o.getShortVal();
+ byteVal = o.getByteVal();
+ byteArrVal = o.getByteArrVal();
+ charVal = o.getCharVal();
+ longVal = o.getLongVal();
+ stringVal = o.getStringVal();
+ }
+
+ /**
+ *
+ * @param o
+ */
+ public void copyInto(IFace o)
+ {
+ o.setThing(thing);
+ o.setIntVal(intVal);
+ o.setBoolVal(boolVal);
+ o.setDoubleVal(doubleVal);
+ o.setFloatVal(floatVal);
+ o.setShortVal(shortVal);
+ o.setByteVal(byteVal);
+ o.setByteArrVal(byteArrVal);
+ o.setCharVal(charVal);
+ o.setLongVal(longVal);
+ o.setStringVal(stringVal);
+ }
+
+ public boolean isBoolVal()
+ {
+ return boolVal;
+ }
+
+ public void setBoolVal(boolean boolVal)
+ {
+ this.boolVal = boolVal;
+ }
+
+ public byte[] getByteArrVal()
+ {
+ return byteArrVal;
+ }
+
+ public void setByteArrVal(byte[] byteArrVal)
+ {
+ this.byteArrVal = byteArrVal;
+ }
+
+ public byte getByteVal()
+ {
+ return byteVal;
+ }
+
+ public void setByteVal(byte byteVal)
+ {
+ this.byteVal = byteVal;
+ }
+
+ public char getCharVal()
+ {
+ return charVal;
+ }
+
+ public void setCharVal(char charVal)
+ {
+ this.charVal = charVal;
+ }
+
+ public double getDoubleVal()
+ {
+ return doubleVal;
+ }
+
+ public void setDoubleVal(double doubleVal)
+ {
+ this.doubleVal = doubleVal;
+ }
+
+ public float getFloatVal()
+ {
+ return floatVal;
+ }
+
+ public void setFloatVal(float floatVal)
+ {
+ this.floatVal = floatVal;
+ }
+
+ public int getIntVal()
+ {
+ return intVal;
+ }
+
+ public void setIntVal(int intVal)
+ {
+ this.intVal = intVal;
+ }
+
+ public long getLongVal()
+ {
+ return longVal;
+ }
+
+ public void setLongVal(long longVal)
+ {
+ this.longVal = longVal;
+ }
+
+ public short getShortVal()
+ {
+ return shortVal;
+ }
+
+ public void setShortVal(short shortVal)
+ {
+ this.shortVal = shortVal;
+ }
+
+ public String getStringVal()
+ {
+ return stringVal;
+ }
+
+ public void setStringVal(String stringVal)
+ {
+ this.stringVal = stringVal;
+ }
+
+ public SerializableThing getThing()
+ {
+ return thing;
+ }
+
+ public void setThing(SerializableThing thing)
+ {
+ this.thing = thing;
+ }
+
+ @Override
+ public boolean equals(Object obj)
+ {
+ if (obj == null)
+ {
+ return false;
+ }
+ if (getClass() != obj.getClass())
+ {
+ return false;
+ }
+ final Face other = (Face)obj;
+ if (thing != other.thing && (thing == null ||
!thing.equals(other.thing)))
+ {
+ return false;
+ }
+ if (intVal != other.intVal)
+ {
+ return false;
+ }
+ if (boolVal != other.boolVal)
+ {
+ return false;
+ }
+ if (doubleVal != other.doubleVal)
+ {
+ return false;
+ }
+ if (floatVal != other.floatVal)
+ {
+ return false;
+ }
+ if (shortVal != other.shortVal)
+ {
+ return false;
+ }
+ if (byteVal != other.byteVal)
+ {
+ return false;
+ }
+ if (!Arrays.equals(byteArrVal, other.byteArrVal))
+ {
+ return false;
+ }
+ if (charVal != other.charVal)
+ {
+ return false;
+ }
+ if (longVal != other.longVal)
+ {
+ return false;
+ }
+ if ((stringVal == null) ? (other.stringVal != null)
+ : !stringVal.equals(other.stringVal))
+ {
+ return false;
+ }
+ return true;
+ }
+
+ @Override
+ public int hashCode()
+ {
+ int hash = 3;
+ hash = 23 * hash + (thing != null ? thing.hashCode() :
0);
+ hash = 23 * hash + intVal;
+ hash = 23 * hash + (boolVal ? 1 : 0);
+ hash = 23 *
+ hash +
+ (int)(Double.doubleToLongBits(doubleVal) ^
(Double.doubleToLongBits(doubleVal) >>> 32));
+ hash = 23 * hash + Float.floatToIntBits(floatVal);
+ hash = 23 * hash + shortVal;
+ hash = 23 * hash + byteVal;
+ hash = 23 * hash + Arrays.hashCode(byteArrVal);
+ hash = 23 * hash + charVal;
+ hash = 23 * hash + (int)(longVal ^ (longVal >>> 32));
+ hash = 23 * hash + (stringVal != null ?
stringVal.hashCode() : 0);
+ return hash;
+ }
+
+
+ }
+
+ public static final class SerializableThing implements Serializable
+ {
+ public final String word;
+
+ public SerializableThing(String word)
+ {
+ this.word = word;
+ }
+
+ @Override
+ public boolean equals(Object obj)
+ {
+ if (obj == null)
+ {
+ return false;
+ }
+ if (getClass() != obj.getClass())
+ {
+ return false;
+ }
+ final SerializableThing other = (SerializableThing)obj;
+ if ((word == null) ? (other.word != null) :
!word.equals(other.word))
+ {
+ return false;
+ }
+ return true;
+ }
+
+ @Override
+ public int hashCode()
+ {
+ int hash = 7;
+ hash = 23 * hash + (word != null ? word.hashCode() : 0);
+ return hash;
+ }
+ }
}